path_resolution(7) - Linux 手册页

名称

path_resolution - 路径名如何解析为文件

描述

某些 UNIX/Linux 系统调用以一个或多个文件名作为参数。文件名(或路径名)的解析过程如下。

第 1 步:解析过程的起点

如果路径名以“/”字符开头,则起始查找目录为调用进程的根目录。(进程从其父进程继承根目录。通常这将是文件层次结构的根目录。进程可以通过使用 chroot(2) 系统调用来获取不同的根目录。如果进程——或其祖先之一——是通过调用设置了 CLONE_NEWNS 标志的 clone(2) 系统调用启动的,则该进程可能会获得一个完全私有的挂载命名空间。)这处理了路径名中的“/”部分。

如果路径名不以“/”字符开头,则解析过程的起始查找目录为进程的当前工作目录。(这也从父进程继承而来。可以通过使用 chdir(2) 系统调用来更改。)

以“/”字符开头的路径名称为绝对路径名。不以“/”开头的路径名称为相对路径名。

第 2 步:沿路径遍历

将当前查找目录设置为起始查找目录。现在,对于路径名的每个非最终分量(分量是指由“/”字符分隔的子字符串),都在当前查找目录中查找该分量。

如果进程在当前查找目录上没有搜索权限,则返回 EACCES 错误(“Permission denied”,权限被拒绝)。

如果未找到该分量,则返回 ENOENT 错误(“No such file or directory”,没有那个文件或目录)。

如果找到了该分量,但它既不是目录也不是符号链接,则返回 ENOTDIR 错误(“Not a directory”,不是目录)。

如果找到该组件并且是目录,我们将当前查找目录设置为该目录,然后转到下一个组件。

如果找到的分量是符号链接(symlink),我们首先解析此符号链接(以当前查找目录作为起始查找目录)。出错时,返回相应的错误。如果结果不是目录,则返回 ENOTDIR 错误。如果符号链接解析成功并返回一个目录,我们将当前查找目录设置为该目录,并进入下一个分量。请注意,这里的解析过程涉及递归。为了保护内核免受栈溢出,同时也为了防止拒绝服务攻击,解析过程对最大递归深度以及跟踪的符号链接最大数量有限制。当超过限制时,返回 ELOOP 错误(“Too many levels of symbolic links”,符号链接层数过多)。

第 3 步:查找最终条目

路径名最终分量的查找方式与之前步骤中所述的所有其他分量相同,但有两点不同:(i) 最终分量不必是目录(至少对于路径解析过程而言是这样——由于特定系统调用的要求,它可能必须是目录,也可能是非目录),以及 (ii) 如果未找到该分量,并不一定是一个错误——也许我们只是在创建它。关于最终条目处理的详细信息,请参阅特定系统调用的手册页。

. 和 ..

按照惯例,每个目录都有条目“.”和“..”,它们分别指向目录本身及其父目录。

路径解析过程会假定这些条目具有其传统含义,无论它们是否实际存在于物理文件系统中。

不能越过根目录向上遍历:“/..”与“/”相同。

挂载点

执行“mount dev path”命令后,路径名“path”指向设备“dev”上文件系统层次结构的根,不再指向之前指向的内容。

可以从挂载的文件系统中“走出”:“path/..”指向“path”的父目录,位于“dev”上的文件系统层次结构之外。

末尾斜杠

如果路径名以“/”结尾,则强制按第 2 步解析前一个分量:它必须存在且解析为一个目录。否则,末尾的“/”将被忽略。(或者等效地,带有末尾“/”的路径名等同于在该路径名后附加“/.”得到的路径名。)

最终符号链接

如果路径名的最后一个分量是符号链接,则所引用的文件是符号链接本身,还是其内容路径解析的结果,取决于具体的系统调用。例如,系统调用 lstat(2) 会操作符号链接本身,而 stat(2) 则操作符号链接所指向的文件。

长度限制

路径名有最大长度限制。如果路径名(或解析符号链接时获得的某个中间路径名)过长,则返回 ENAMETOOLONG 错误(“Filename too long”,文件名过长)。

空路径名

在最初的 UNIX 中,空路径名指的是当前目录。如今 POSIX 规定空路径名不得解析成功。在 Linux 中,这种情况会返回 ENOENT

权限

文件的权限位由三组三位组成,参见 chmod(1)stat(2)。当调用进程的有效用户 ID 等于文件的所有者 ID 时,使用第一组三位。当文件的组 ID 等于调用进程的有效组 ID,或者是调用进程的补充组 ID 之一(由 setgroups(2) 设置)时,使用第二组三位。当两者都不满足时,使用第三组。

所用的三位中,第一位确定读取权限,第二位确定写入权限,最后一位确定执行权限(对于普通文件)或搜索权限(对于目录)。

Linux 在权限检查中使用 fsuid 而不是有效用户 ID。通常 fsuid 等于有效用户 ID,但可以通过系统调用 setfsuid(2) 更改 fsuid。

(此处“fsuid”意为“文件系统用户 ID”。这个概念是在进程可以向具有相同有效用户 ID 的进程发送信号的时代,为了实现用户空间 NFS 服务器而需要的。它现在已经过时了。不应再使用 setfsuid(2)。)

同样,Linux 使用 fsgid(“文件系统组 ID”)而不是有效组 ID。参见 setfsgid(2)

绕过权限检查:超级用户和能力 (Capabilities)

在传统的 UNIX 系统上,超级用户(root,用户 ID 0)拥有无上权力,在访问文件时绕过所有权限限制。

在 Linux 上,超级用户权限被划分为各种能力(参见 capabilities(7))。有两种能力与文件权限检查相关:CAP_DAC_OVERRIDECAP_DAC_READ_SEARCH。(如果进程的 fsuid 为 0,则该进程拥有这些能力。)

CAP_DAC_OVERRIDE 能力会覆盖所有权限检查,但仅在文件的三个执行权限位中至少有一个被设置时,才授予执行权限。

CAP_DAC_READ_SEARCH 能力授予对目录的读取和搜索权限,以及对普通文件的读取权限。

参见

readlink(2), capabilities(7), credentials(7), symlink(7)

引用自

access(2), acct(2), bind(2), chmod(2), chown(2), connect(2), euidaccess(3), execve(2), explain_lca2010(1), faccessat(2), fchmodat(2), fchownat(2), fstatat(2), futimesat(2), intro(2), intro(3), link(2), linkat(2), mkdir(2), mkdirat(2), mkfifoat(3), mknod(2), mknodat(2), mount(2), open(2), openat(2), readlinkat(2), rename(2), renameat(2), rmdir(2), scandirat(3), send(2), statfs(2), statvfs(2), symlink(2), symlinkat(2), truncate(2), umount(2), unlink(2), unlinkat(2), uselib(2), utime(2), utimensat(2)