path_resolution(7) - Linux 手册页
名称
path_resolution - 路径名如何解析为文件描述
第 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_OVERRIDE 和 CAP_DAC_READ_SEARCH。(如果进程的 fsuid 为 0,则该进程拥有这些能力。)
CAP_DAC_OVERRIDE 能力会覆盖所有权限检查,但仅在文件的三个执行权限位中至少有一个被设置时,才授予执行权限。
CAP_DAC_READ_SEARCH 能力授予对目录的读取和搜索权限,以及对普通文件的读取权限。