From 82f7bbb4f1f96bb0f369c391f5a53c9a3ce2cb49 Mon Sep 17 00:00:00 2001
From: Trijal Saha <97483939+Trijal08@users.noreply.github.com>
Date: Fri, 1 May 2026 17:08:16 -0400
Subject: [PATCH 1/1] fs: Prioritize mounting ntfs volumes RWX with the native
 Linux kernel drivers

---
 fs/Ntfs.cpp | 106 +++++++++++++++++++++++++++++++++++++++-------------
 1 file changed, 79 insertions(+), 27 deletions(-)

diff --git a/fs/Ntfs.cpp b/fs/Ntfs.cpp
index 6be65bc..518f92d 100644
--- a/fs/Ntfs.cpp
+++ b/fs/Ntfs.cpp
@@ -15,9 +15,13 @@
  */
 
 #include <sys/mount.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <unistd.h>
 
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
+#include <private/android_filesystem_config.h>
 
 #include <logwrap/logwrap.h>
 
@@ -35,10 +39,9 @@ static const char* kFsckPath = "/system/bin/fsck.ntfs";
 static const char* kMountPath = "/system/bin/mount.ntfs";
 
 bool IsSupported() {
-    return access(kMkfsPath, X_OK) == 0
-            && access(kFsckPath, X_OK) == 0
-            && access(kMountPath, X_OK) == 0
-            && IsFilesystemSupported("ntfs");
+    return access(kMkfsPath, X_OK) == 0 &&
+           access(kFsckPath, X_OK) == 0 &&
+           (IsFilesystemSupported("ntfs3") || IsFilesystemSupported("ntfs"));
 }
 
 status_t Check(const std::string& source) {
@@ -49,37 +52,95 @@ status_t Check(const std::string& source) {
 
     int rc = ForkExecvp(cmd, nullptr, sFsckUntrustedContext);
     if (rc == 0) {
-        LOG(INFO) << "Check OK";
+        LOG(INFO) << "NTFS Check OK";
         return 0;
     } else {
-        LOG(ERROR) << "Check failed (code " << rc << ")";
+        LOG(ERROR) << "NTFS Check failed (code " << rc << ")";
         errno = EIO;
         return -1;
     }
 }
 
+status_t DoMount(const std::string& source, const std::string& target, int ownerUid, int ownerGid,
+                 int permMask) {
+    int mountFlags = MS_NODEV | MS_NOSUID | MS_DIRSYNC | MS_NOATIME;
+
+    // Filter permMask to ensure the 'World' (PC) has full access
+    // This solves the 'root required on Linux PC' issue
+    int ntfsMask = permMask & 0770;
+
+    // 1. DATA FOR NATIVE (ntfs / ntfs3)
+    // Using umask as it is the requirement for the native kernel drivers
+    auto nativeMountData = StringPrintf("uid=%d,gid=%d,fmask=%o,dmask=%o,nls=utf8,iocharset=utf8,windows_names",
+                                        ownerUid, ownerGid, ntfsMask, ntfsMask);
+
+    // Try primary "ntfs3" driver, most popular and often better than FUSE NTFS-3G, but not better than new NTFSPlus by Namjae Jeon
+    if (mount(source.c_str(), target.c_str(), "ntfs3", mountFlags, nativeMountData.c_str()) == 0) {
+        return 0;
+    }
+
+    // Try secondary "ntfs" driver, this will also be Namjae Jeon's NTFS driver
+    if (mount(source.c_str(), target.c_str(), "ntfs", mountFlags, nativeMountData.c_str()) == 0) {
+        return 0;
+    }
+
+    return -1;
+}
+
+struct mount_args {
+    const std::string& source;
+    const std::string& target;
+    int ownerUid;
+    int ownerGid;
+    int permMask;
+};
+
+int DoMountWrapper(void* args) {
+    struct mount_args* m_args = (struct mount_args*)args;
+
+    return DoMount(m_args->source, m_args->target, m_args->ownerUid, m_args->ownerGid,
+                   m_args->permMask);
+}
+
 status_t Mount(const std::string& source, const std::string& target, int ownerUid, int ownerGid,
                int permMask) {
-    auto mountData = android::base::StringPrintf("utf8,uid=%d,gid=%d,fmask=%o,dmask=%o,"
-                                                 "shortname=mixed,nodev,nosuid,dirsync,noatime,"
-                                                 "noexec", ownerUid, ownerGid, permMask, permMask);
+    struct mount_args args = {source, target, ownerUid, ownerGid, permMask};
+
+    // Attempt native kernel mount via ForkTimeout (Same as Exfat.cpp)
+    if (ForkTimeout(DoMountWrapper, &args, kUntrustedMountSleepTime) == 0) {
+        LOG(INFO) << "NTFS mounted via native kernel driver";
+        return 0;
+    }
+
+    // FALLBACK: ntfs-3g (FUSE)
+    LOG(WARNING) << "Native NTFS mount failed; falling back to ntfs-3g";
+
+    if (access(kMountPath, X_OK) != 0) {
+        LOG(ERROR) << "Fallback mount.ntfs binary not found";
+        return -1;
+    }
+
+    auto fuseData = StringPrintf("utf8,uid=%d,gid=%d,fmask=%o,dmask=%o,"
+                                 "shortname=mixed,nodev,nosuid,dirsync,noatime,noexec",
+                                 ownerUid, ownerGid, permMask, permMask);
 
     std::vector<std::string> cmd;
     cmd.push_back(kMountPath);
     cmd.push_back("-o");
-    cmd.push_back(mountData.c_str());
-    cmd.push_back(source.c_str());
-    cmd.push_back(target.c_str());
+    cmd.push_back(fuseData);
+    cmd.push_back(source);
+    cmd.push_back(target);
 
     int rc = ForkExecvp(cmd);
     if (rc == 0) {
-        LOG(INFO) << "Mount OK";
+        LOG(INFO) << "ntfs-3g mount successful";
+        chown(target.c_str(), AID_MEDIA_RW, AID_MEDIA_RW);
+        chmod(target.c_str(), 0775);
         return 0;
-    } else {
-        LOG(ERROR) << "Mount failed (code " << rc << ")";
-        errno = EIO;
-        return -1;
     }
+
+    LOG(ERROR) << "All NTFS mount attempts failed";
+    return rc;
 }
 
 status_t Format(const std::string& source) {
@@ -87,16 +148,7 @@ status_t Format(const std::string& source) {
     cmd.push_back(kMkfsPath);
     cmd.push_back(source);
 
-    int rc = ForkExecvp(cmd);
-    if (rc == 0) {
-        LOG(INFO) << "Format OK";
-        return 0;
-    } else {
-        LOG(ERROR) << "Format failed (code " << rc << ")";
-        errno = EIO;
-        return -1;
-    }
-    return 0;
+    return ForkExecvp(cmd);
 }
 
 }  // namespace ntfs
-- 
2.43.0

