哪些因素影响用户是否可以在 Linux 上挂载 NTFS 分区?

哪些因素影响用户是否可以在 Linux 上挂载 NTFS 分区?

我的系统上有两个 Win8 制作的 NTFS 分区。运行 Debian 测试版、Linux 内核 3.16,我可以以普通用户身份挂载其中一个,但尝试挂载另一个会导致错误:

$ mount /media/WinC
Error opening '/dev/sda1': Permission denied
Failed to mount '/dev/sda1': Permission denied
Please check '/dev/sda1' and the ntfs-3g binary permissions,
and the mounting user ID. More explanation is provided at
http://tuxera.com/community/ntfs-3g-faq/#unprivileged

两者都可以由 root 挂载,从而产生相同的条目/etc/mtab

/dev/sda1 /media/WinC fuseblk rw,nosuid,nodev,noexec,relatime,user_id=0,group_id=0,allow_other,blksize=4096 0 0
/dev/sdb6 /media/WinE fuseblk rw,nosuid,nodev,noexec,relatime,user_id=0,group_id=0,allow_other,blksize=4096 0 0

两者的条目/etc/fstab相同:

/dev/sda1  /media/WinC  ntfs-3g  rw,user,noauto  0  0
/dev/sdb6  /media/WinE  ntfs-3g  rw,user,noauto  0  0

挂载点的访问权限是相同的:

drwxr-xr-x  2 root root 4096 Jan 25  2013 WinC/
drwxr-xr-x  2 root root 4096 Nov 27 22:58 WinE/

最后,块设备的访问权限是相同的:

brw-rw---- 1 root disk 8,  1 Dec 10 20:17 /dev/sda1
brw-rw---- 1 root disk 8, 22 Dec 10 16:24 /dev/sdb6

那么,是什么因素导致其中一个以用户身份挂载成功,而另一个失败?


根据 podwysoc 的要求,以下是输出strace ntfs-3g /dev/sda1 /media/WinC

execve("/bin/ntfs-3g", ["ntfs-3g", "/dev/sda1", "/media/WinC"], [/* 45 vars */]) = 0
brk(0)                                  = 0x7fe6bbca8000
fcntl(0, F_GETFD)                       = 0
fcntl(1, F_GETFD)                       = 0
fcntl(2, F_GETFD)                       = 0
access("/etc/suid-debug", F_OK)         = -1 ENOENT (No such file or directory)
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fe6bb8b9000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=157945, ...}) = 0
mmap(NULL, 157945, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fe6bb892000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
open("/lib/x86_64-linux-gnu/libpthread.so.0", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\20o\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=137440, ...}) = 0
mmap(NULL, 2213008, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fe6bb47e000
mprotect(0x7fe6bb496000, 2093056, PROT_NONE) = 0
mmap(0x7fe6bb695000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x17000) = 0x7fe6bb695000
mmap(0x7fe6bb697000, 13456, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fe6bb697000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
open("/lib/x86_64-linux-gnu/libntfs-3g.so.852", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\0\216\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0644, st_size=334248, ...}) = 0
mmap(NULL, 2429528, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fe6bb22c000
mprotect(0x7fe6bb27c000, 2097152, PROT_NONE) = 0
mmap(0x7fe6bb47c000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x50000) = 0x7fe6bb47c000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0P\34\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1729984, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fe6bb891000
mmap(NULL, 3836448, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fe6bae83000
mprotect(0x7fe6bb022000, 2097152, PROT_NONE) = 0
mmap(0x7fe6bb222000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x19f000) = 0x7fe6bb222000
mmap(0x7fe6bb228000, 14880, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fe6bb228000
close(3)                                = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fe6bb890000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fe6bb88f000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fe6bb88e000
arch_prctl(ARCH_SET_FS, 0x7fe6bb88f700) = 0
mprotect(0x7fe6bb222000, 16384, PROT_READ) = 0
mprotect(0x7fe6bb47c000, 4096, PROT_READ) = 0
mprotect(0x7fe6bb695000, 4096, PROT_READ) = 0
mprotect(0x7fe6bbadf000, 8192, PROT_READ) = 0
mprotect(0x7fe6bb8bb000, 4096, PROT_READ) = 0
munmap(0x7fe6bb892000, 157945)          = 0
set_tid_address(0x7fe6bb88f9d0)         = 8822
set_robust_list(0x7fe6bb88f9e0, 24)     = 0
rt_sigaction(SIGRTMIN, {0x7fe6bb4849f0, [], SA_RESTORER|SA_SIGINFO, 0x7fe6bb48d8d0}, NULL, 8) = 0
rt_sigaction(SIGRT_1, {0x7fe6bb484a80, [], SA_RESTORER|SA_RESTART|SA_SIGINFO, 0x7fe6bb48d8d0}, NULL, 8) = 0
rt_sigprocmask(SIG_UNBLOCK, [RTMIN RT_1], NULL, 8) = 0
getrlimit(RLIMIT_STACK, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
open("/dev/null", O_RDWR)               = 3
close(3)                                = 0
getegid()                               = 1000
geteuid()                               = 1000
brk(0)                                  = 0x7fe6bbca8000
brk(0x7fe6bbcc9000)                     = 0x7fe6bbcc9000
open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=1613360, ...}) = 0
mmap(NULL, 1613360, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fe6bb704000
close(3)                                = 0
lstat("/dev", {st_mode=S_IFDIR|0755, st_size=3700, ...}) = 0
lstat("/dev/sda1", {st_mode=S_IFBLK|0660, st_rdev=makedev(8, 1), ...}) = 0
getuid()                                = 1000
getgid()                                = 1000
lstat("/dev", {st_mode=S_IFDIR|0755, st_size=3700, ...}) = 0
lstat("/dev/sda1", {st_mode=S_IFBLK|0660, st_rdev=makedev(8, 1), ...}) = 0
open("/proc/mounts", O_RDONLY|O_CLOEXEC) = 3
futex(0x7fe6bb229088, FUTEX_WAKE_PRIVATE, 2147483647) = 0
fstat(3, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fe6bb8b8000
read(3, "rootfs / rootfs rw 0 0\nsysfs /sy"..., 1024) = 1024
getcwd("/home/ca", 4096)                = 9
lstat("/home/ca/rootfs", 0x7fffdd24c640) = -1 ENOENT (No such file or directory)
getcwd("/home/ca", 4096)                = 9
lstat("/home/ca/sysfs", 0x7fffdd24c640) = -1 ENOENT (No such file or directory)
getcwd("/home/ca", 4096)                = 9
lstat("/home/ca/proc", 0x7fffdd24c640)  = -1 ENOENT (No such file or directory)
getcwd("/home/ca", 4096)                = 9
lstat("/home/ca/udev", 0x7fffdd24c640)  = -1 ENOENT (No such file or directory)
getcwd("/home/ca", 4096)                = 9
lstat("/home/ca/devpts", 0x7fffdd24c640) = -1 ENOENT (No such file or directory)
getcwd("/home/ca", 4096)                = 9
lstat("/home/ca/tmpfs", 0x7fffdd24c640) = -1 ENOENT (No such file or directory)
lstat("/dev", {st_mode=S_IFDIR|0755, st_size=3700, ...}) = 0
lstat("/dev/disk", {st_mode=S_IFDIR|0755, st_size=140, ...}) = 0
lstat("/dev/disk/by-uuid", {st_mode=S_IFDIR|0755, st_size=200, ...}) = 0
lstat("/dev/disk/by-uuid/f522a1c8-076e-451a-8204-846884bbd509", {st_mode=S_IFLNK|0777, st_size=10, ...}) = 0
readlink("/dev/disk/by-uuid/f522a1c8-076e-451a-8204-846884bbd509", "../../sda2", 4095) = 10
lstat("/dev/sda2", {st_mode=S_IFBLK|0660, st_rdev=makedev(8, 2), ...}) = 0
getcwd("/home/ca", 4096)                = 9
lstat("/home/ca/securityfs", 0x7fffdd24c640) = -1 ENOENT (No such file or directory)
getcwd("/home/ca", 4096)                = 9
lstat("/home/ca/tmpfs", 0x7fffdd24c640) = -1 ENOENT (No such file or directory)
getcwd("/home/ca", 4096)                = 9
lstat("/home/ca/tmpfs", 0x7fffdd24c640) = -1 ENOENT (No such file or directory)
getcwd("/home/ca", 4096)                = 9
lstat("/home/ca/tmpfs", 0x7fffdd24c640) = -1 ENOENT (No such file or directory)
getcwd("/home/ca", 4096)                = 9
lstat("/home/ca/cgroup", 0x7fffdd24c640) = -1 ENOENT (No such file or directory)
getcwd("/home/ca", 4096)                = 9
lstat("/home/ca/pstore", 0x7fffdd24c640) = -1 ENOENT (No such file or directory)
getcwd("/home/ca", 4096)                = 9
lstat("/home/ca/cgroup", 0x7fffdd24c640) = -1 ENOENT (No such file or directory)
read(3, "/cgroup/cpu,cpuacct cgroup rw,no"..., 1024) = 1024
getcwd("/home/ca", 4096)                = 9
lstat("/home/ca/cgroup", 0x7fffdd24c640) = -1 ENOENT (No such file or directory)
getcwd("/home/ca", 4096)                = 9
lstat("/home/ca/cgroup", 0x7fffdd24c640) = -1 ENOENT (No such file or directory)
getcwd("/home/ca", 4096)                = 9
lstat("/home/ca/cgroup", 0x7fffdd24c640) = -1 ENOENT (No such file or directory)
getcwd("/home/ca", 4096)                = 9
lstat("/home/ca/cgroup", 0x7fffdd24c640) = -1 ENOENT (No such file or directory)
getcwd("/home/ca", 4096)                = 9
lstat("/home/ca/cgroup", 0x7fffdd24c640) = -1 ENOENT (No such file or directory)
getcwd("/home/ca", 4096)                = 9
lstat("/home/ca/cgroup", 0x7fffdd24c640) = -1 ENOENT (No such file or directory)
getcwd("/home/ca", 4096)                = 9
lstat("/home/ca/systemd-1", 0x7fffdd24c640) = -1 ENOENT (No such file or directory)
getcwd("/home/ca", 4096)                = 9
lstat("/home/ca/mqueue", 0x7fffdd24c640) = -1 ENOENT (No such file or directory)
getcwd("/home/ca", 4096)                = 9
lstat("/home/ca/debugfs", 0x7fffdd24c640) = -1 ENOENT (No such file or directory)
getcwd("/home/ca", 4096)                = 9
lstat("/home/ca/hugetlbfs", 0x7fffdd24c640) = -1 ENOENT (No such file or directory)
getcwd("/home/ca", 4096)                = 9
lstat("/home/ca/fusectl", 0x7fffdd24c640) = -1 ENOENT (No such file or directory)
getcwd("/home/ca", 4096)                = 9
lstat("/home/ca/tmpfs", 0x7fffdd24c640) = -1 ENOENT (No such file or directory)
lstat("/dev", {st_mode=S_IFDIR|0755, st_size=3700, ...}) = 0
lstat("/dev/sdb3", {st_mode=S_IFBLK|0660, st_rdev=makedev(8, 19), ...}) = 0
lstat("/dev", {st_mode=S_IFDIR|0755, st_size=3700, ...}) = 0
lstat("/dev/sdb2", {st_mode=S_IFBLK|0660, st_rdev=makedev(8, 18), ...}) = 0
read(3, "rdered 0 0\n/dev/sdb4 /home/ca/St"..., 1024) = 221
lstat("/dev", {st_mode=S_IFDIR|0755, st_size=3700, ...}) = 0
lstat("/dev/sdb5", {st_mode=S_IFBLK|0660, st_rdev=makedev(8, 21), ...}) = 0
lstat("/dev", {st_mode=S_IFDIR|0755, st_size=3700, ...}) = 0
lstat("/dev/sdb4", {st_mode=S_IFBLK|0660, st_rdev=makedev(8, 20), ...}) = 0
getcwd("/home/ca", 4096)                = 9
lstat("/home/ca/rpc_pipefs", 0x7fffdd24c640) = -1 ENOENT (No such file or directory)
getcwd("/home/ca", 4096)                = 9
lstat("/home/ca/tmpfs", 0x7fffdd24c640) = -1 ENOENT (No such file or directory)
read(3, "", 1024)                       = 0
close(3)                                = 0
munmap(0x7fe6bb8b8000, 4096)            = 0
stat("/media/WinC", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
open("/proc/filesystems", O_RDONLY)     = 3
fstat(3, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fe6bb8b8000
read(3, "nodev\tsysfs\nnodev\trootfs\nnodev\tr"..., 1024) = 338
close(3)                                = 0
munmap(0x7fe6bb8b8000, 4096)            = 0
geteuid()                               = 1000
getresuid([1000], [1000], [1000])       = 0
setresuid(4294967295, 1000, 4294967295) = 0
geteuid()                               = 1000
getegid()                               = 1000
getresgid([1000], [1000], [1000])       = 0
setresgid(4294967295, 1000, 4294967295) = 0
getegid()                               = 1000
stat("/dev/fuse", {st_mode=S_IFCHR|0666, st_rdev=makedev(10, 229), ...}) = 0
getegid()                               = 1000
geteuid()                               = 1000
stat("/dev/sda1", {st_mode=S_IFBLK|0660, st_rdev=makedev(8, 1), ...}) = 0
mmap(NULL, 135168, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fe6bb898000
stat("/dev/sda1", {st_mode=S_IFBLK|0660, st_rdev=makedev(8, 1), ...}) = 0
open("/dev/sda1", O_RDWR)               = -1 EACCES (Permission denied)
write(2, "Error opening '/dev/sda1'", 25Error opening '/dev/sda1') = 25
open("/usr/share/locale/locale.alias", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=2492, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fe6bb897000
read(3, "# Locale name alias data base.\n#"..., 4096) = 2492
read(3, "", 4096)                       = 0
close(3)                                = 0
munmap(0x7fe6bb897000, 4096)            = 0
open("/usr/share/locale/en_US/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale/en/LC_MESSAGES/libc.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
write(2, ": Permission denied\n", 20: Permission denied
)   = 20
munmap(0x7fe6bb898000, 135168)          = 0
write(2, "Failed to mount '/dev/sda1'", 27Failed to mount '/dev/sda1') = 27
write(2, ": Permission denied\n", 20: Permission denied
)   = 20
write(2, "Please check '/dev/sda1' and the"..., 173Please check '/dev/sda1' and the ntfs-3g binary permissions,
and the mounting user ID. More explanation is provided at
http://tuxera.com/community/ntfs-3g-faq/#unprivileged
) = 173
exit_group(19)                          = ?
+++ exited with 19 +++

答案1

错误中提到的链接或许可以找到答案。相关章节:

为什么 chmod 和 chown 没有效果?

默认情况下,NTFS 上的文件归 root 所有,每个人都可以完全访问。要获得标准的按文件保护,您应该使用“权限”选项进行安装。此外,如果您希望权限与特定的 Windows 配置可互操作,则必须映射用户。

为什么非特权用户无法挂载块设备?
或者
为什么我会收到“fusermount:option blkdev isprivileged”错误?

仅当满足以下所有要求时,非特权块设备挂载才有效:

  1. ntfs-3g 编译时集成了 FUSE 支持
  2. ntfs-3g 二进制文件至少为版本 1.2506
  3. ntfs-3g 二进制文件设置为 setuid-root
  4. 用户有访问该卷的权限
  5. 用户有访问挂载点的权限

root 用户可以创建 ntfs-3g 二进制 setuid-root,如下所示

chown root $(which ntfs-3g)
chmod 4755 $(which ntfs-3g)

在这种情况下,司机还可以

  • 修复常见的FUSE内核模块加载问题
  • 创建必需的但有时会错误地删除或丢失的 FUSE 设备文件

请注意,使用 setuid-root 可能会导致不可预见的权限提升,因此不建议使用。只有绝对受信任的用户才必须被授予此类访问权限。下面是一个示例,说明如何让 ntfsuser 组中的用户在还具有所需的卷访问权限的情况下挂载任何 NTFS 卷。

chown root.ntfsuser $(which ntfs-3g)
chmod 4750 $(which ntfs-3g)

setuid-root ntfs-3g 驱动程序在其生命周期内应用最小特权原则作为一项安全措施。

为什么 /etc/fstab 中的“用户”和“用户”选项不起作用?

在检查并批准用户有权在指定挂载点上挂载给定设备后,“mount”命令不会以所需的权限调用 ntfs-3g 二进制文件,因此用户无法打开在 /etc/fstab 中获得批准的设备。这是“mount”实用程序中的一个问题。

解决方案:至少使用 NTFS-3G 1.2506 并setuid-root设置并确保用户具有对卷和挂载点的访问权限。

这个问题有两个方面:

  • 如何授予普通用户访问 NTFS 分区的权限
  • 科学好奇心:为什么这两个分区表现不同

分区之间没有明显的区别。链接讨论了 mount 实用程序的问题(以及解决方案)。可能还有其他事情发生,例如 udev 活动,使情况复杂化。当我阅读链接时,似乎问题实际上应该是为什么一个分区确实以普通用户身份挂载,因为mount并且fstab应该只对 root 起作用。回答问题的第二部分很可能是一场侦探和猜谜游戏,鉴于现有信息,我无法帮助解决这一部分。

但是,问题的第一部分似乎有解决方案。根据链接,分区必须以 root 身份挂载,根据问题,这是可行的。常规用户访问是通过权限实现的,如上所述。

相关内容