Linux: refine in-kernel NTFS driver selection

Keep the NTFS kernel-driver option as a generic in-kernel NTFS path rather than an ntfs3-specific path. Add --filesystem=kernel-ntfs and -m kernelntfs routes that select a registered or loadable kernel NTFS driver and mount with -i so mount.ntfs/ntfs-3g helpers are not invoked.

Preserve --filesystem=ntfs3 as a literal pin to the ntfs3 driver. Treat both ntfs3 and kernel-ntfs as mount-only selectors; volume creation continues to use filesystem type NTFS.

The preference and -m kernelntfs path only select an in-kernel NTFS driver when no explicit filesystem type was supplied and blkid detects NTFS.

Treat ntfs as the preferred in-kernel driver on Linux 7.1 and later, where the upstream read/write driver is expected. On earlier kernels, select ntfs only when module metadata identifies the standalone read/write driver and /sys/module confirms it loaded, avoiding ntfs3 read-only ntfs compatibility registrations. Fall back to ntfs3 otherwise, and report a generic kernel-driver error if neither supported driver is available or loadable.

Rename the internal preference/config field to MountNtfsWithKernelDriver, migrate the old MountNtfsWithNtfs3 preference key, and update UI strings, CLI help, documentation, release notes, and translation placeholders accordingly.

Reference: https://github.com/veracrypt/VeraCrypt/issues/1735
This commit is contained in:
Mounir IDRASSI
2026-05-17 18:33:36 +09:00
parent 9535e65bd8
commit 6bef9e009c
66 changed files with 447 additions and 147 deletions
+195 -7
View File
@@ -18,6 +18,9 @@
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#ifdef TC_LINUX
#include <sys/utsname.h>
#endif
#include <stdio.h>
#include <unistd.h>
#include "Platform/FileStream.h"
@@ -30,6 +33,10 @@ namespace VeraCrypt
{
#ifdef TC_LINUX
static string GetTmpUser ();
static bool GetLinuxKernelVersion (int &kernelMajor, int &kernelMinor);
static bool IsLinuxKernelModuleLoaded (const string &moduleName);
static bool IsLinuxKernelVersionAtLeast (int major, int minor);
static bool IsNtfsReadWriteKernelModuleAvailable ();
static bool SamePath (const string& path1, const string& path2);
#endif
@@ -747,6 +754,82 @@ namespace VeraCrypt
}
#ifdef TC_LINUX
static bool GetLinuxKernelVersion (int &kernelMajor, int &kernelMinor)
{
struct utsname kernelInfo;
if (uname (&kernelInfo) != 0)
return false;
kernelMajor = 0;
kernelMinor = 0;
int versionFields = sscanf (kernelInfo.release, "%d.%d", &kernelMajor, &kernelMinor);
if (versionFields < 1)
return false;
return true;
}
static bool IsLinuxKernelVersionAtLeast (int major, int minor)
{
int kernelMajor = 0;
int kernelMinor = 0;
if (!GetLinuxKernelVersion (kernelMajor, kernelMinor))
return false;
return kernelMajor > major || (kernelMajor == major && kernelMinor >= minor);
}
static bool IsLinuxKernelModuleLoaded (const string &moduleName)
{
string modulePath = "/sys/module/" + moduleName;
struct stat moduleStat;
return stat (modulePath.c_str(), &moduleStat) == 0 && S_ISDIR (moduleStat.st_mode);
}
static bool IsNtfsReadWriteKernelModuleAvailable ()
{
list <string> args;
args.push_back ("-F");
args.push_back ("description");
args.push_back ("ntfs");
try
{
string description = StringConverter::ToLower (StringConverter::Trim (Process::Execute ("modinfo", args, 2000)));
// The upstream fs/ntfs module reports "NTFS read-write filesystem driver".
// ntfs3 compatibility aliases report different wording, such as read/write.
return description.find ("ntfs") != string::npos
&& description.find ("read-write") != string::npos
&& description.find ("filesystem driver") != string::npos;
}
catch (...) { }
return false;
}
bool CoreUnix::IsNtfsReadWriteKernelFilesystemTypeAvailable () const
{
if (!IsNtfsReadWriteKernelModuleAvailable ())
return false;
if (!IsLinuxKernelModuleLoaded ("ntfs"))
{
list <string> args;
args.push_back ("-q");
args.push_back ("-b");
args.push_back ("ntfs");
try
{
Process::Execute ("modprobe", args, 5000);
}
catch (...) { }
}
return IsLinuxKernelModuleLoaded ("ntfs") && IsFilesystemTypeRegistered ("ntfs");
}
string CoreUnix::DetectFilesystemType (const DevicePath &devicePath) const
{
list <string> args;
@@ -767,9 +850,113 @@ namespace VeraCrypt
return string();
}
}
bool CoreUnix::IsFilesystemTypeRegistered (const string &filesystemType) const
{
FILE *procFilesystems = fopen ("/proc/filesystems", "r");
if (!procFilesystems)
return false;
bool registered = false;
char line[256];
finally_do_arg (FILE *, procFilesystems, fclose (finally_arg););
while (fgets (line, sizeof (line), procFilesystems))
{
string entry = StringConverter::Trim (line);
size_t separator = entry.find_last_of (" \t");
if (separator != string::npos)
entry = entry.substr (separator + 1);
if (entry == filesystemType)
{
registered = true;
break;
}
}
return registered;
}
bool CoreUnix::IsKernelFilesystemTypeAvailable (const string &filesystemType) const
{
if (IsFilesystemTypeRegistered (filesystemType))
return true;
// This is only used from mount-time paths that run with root-equivalent privileges.
// If a future unprivileged caller uses it, modprobe is expected to fail silently here.
list <string> moduleNames;
moduleNames.push_back (filesystemType);
moduleNames.push_back ("fs-" + filesystemType);
foreach (const string &moduleName, moduleNames)
{
list <string> args;
args.push_back ("-q");
args.push_back ("-b");
args.push_back (moduleName);
try
{
Process::Execute ("modprobe", args, 5000);
}
catch (...) { }
if (IsFilesystemTypeRegistered (filesystemType))
return true;
}
return false;
}
string CoreUnix::SelectNtfsKernelFilesystemType () const
{
bool kernelHasStandaloneNtfs = IsLinuxKernelVersionAtLeast (7, 1);
// Linux 6.9-7.0 may expose an "ntfs" compatibility alias from ntfs3,
// but that legacy mount path is forced read-only. Only use "ntfs" where
// the standalone read/write in-kernel driver is expected upstream, or when
// module metadata and /sys/module positively identify a loaded backport as
// the modern driver. Do not trust a pre-existing "ntfs" registration on
// pre-7.1 kernels; it may belong to ntfs3's read-only compatibility path.
if (!kernelHasStandaloneNtfs && IsNtfsReadWriteKernelFilesystemTypeAvailable ())
return "ntfs";
if (kernelHasStandaloneNtfs && IsKernelFilesystemTypeAvailable ("ntfs"))
return "ntfs";
if (IsKernelFilesystemTypeAvailable ("ntfs3"))
return "ntfs3";
throw KernelNtfsDriverUnavailable (SRC_POS);
}
void CoreUnix::ResolveNtfsKernelMountOptions (const DevicePath &devicePath, bool mountNtfsWithKernelDriver,
wstring &filesystemType, bool &internalMountOnly) const
{
string requestedFilesystemType = StringConverter::ToLower (StringConverter::ToSingle (filesystemType));
bool explicitKernelNtfsRequest = requestedFilesystemType == "kernel-ntfs" || requestedFilesystemType == "ntfs-kernel";
if (requestedFilesystemType == "ntfs3")
{
// mount.ntfs3 helpers are not required; -i keeps mount(8) on the kernel path.
internalMountOnly = true;
return;
}
if (!explicitKernelNtfsRequest
&& !(mountNtfsWithKernelDriver
&& filesystemType.empty()
&& DetectFilesystemType (devicePath) == "ntfs"))
return;
filesystemType = StringConverter::ToWide (SelectNtfsKernelFilesystemType());
internalMountOnly = true;
}
#endif
void CoreUnix::MountFilesystem (const DevicePath &devicePath, const DirectoryPath &mountPoint, const string &filesystemType, bool readOnly, const string &systemMountOptions) const
void CoreUnix::MountFilesystem (const DevicePath &devicePath, const DirectoryPath &mountPoint, const string &filesystemType, bool readOnly, const string &systemMountOptions, bool internalMountOnly) const
{
if (GetMountedFilesystems (DevicePath(), mountPoint).size() > 0)
throw MountPointUnavailable (SRC_POS);
@@ -777,6 +964,9 @@ namespace VeraCrypt
list <string> args;
string options;
if (internalMountOnly)
args.push_back ("-i");
if (!filesystemType.empty())
{
#ifdef TC_SOLARIS
@@ -1118,19 +1308,17 @@ namespace VeraCrypt
if (!options.NoFilesystem && options.MountPoint && !options.MountPoint->IsEmpty())
{
wstring filesystemType = options.FilesystemType;
bool internalMountOnly = false;
#ifdef TC_LINUX
if (options.MountNtfsWithNtfs3 && filesystemType.empty()
&& DetectFilesystemType (loopDev) == "ntfs")
{
filesystemType = L"ntfs3";
}
ResolveNtfsKernelMountOptions (loopDev, options.MountNtfsWithKernelDriver, filesystemType, internalMountOnly);
#endif
MountFilesystem (loopDev, *options.MountPoint,
StringConverter::ToSingle (filesystemType),
options.Protection == VolumeProtection::ReadOnly,
StringConverter::ToSingle (options.FilesystemOptions));
StringConverter::ToSingle (options.FilesystemOptions),
internalMountOnly);
}
return loopDev;