当路径长于PATH_MAX时,从文件描述符获取路径

时间:2016-10-26 11:28:04

标签: c linux fanotify

我从fanotify接收文件系统事件。有时我想获得一个被访问文件的绝对路径。

通常,它不是问题 - fanotify_event_metadata包含文件描述符fd,因此我可以在readlink上调用/proc/self/fd/<fd>并获取我的路径。< / p>

但是,如果路径超过PATH_MAX readlink,则无法再使用 - 它会因ENAMETOOLONG而失败。我想知道在这种情况下是否有办法获得文件路径。

显然,我可以fstat从fanotify获取的描述符并遍历整个文件系统,查找具有相同设备ID和inode编号的文件。但是这种方法在性能方面对我来说是不可行的(即使我优化它以忽略短于PATH_MAX的路径)。

我已尝试通过fd重新打开O_PATH并致电openat(fd, "..", ...)来获取父目录。显然,失败是因为fd没有引用目录。我也尝试在readlink调用失败后检查缓冲区的内容(希望它包含部分路径)。这也不起作用。

到目前为止,我已经设法为打开它们的进程的工作目录中的文件获取长路径(fanotify事件包含目标进程的pid,因此我可以阅读{{1}并从那里获取根路径)。但这是部分解决方案。

有没有办法从文件描述符获取绝对路径而不遍历整个文件系统?最好是与内核2.6.32 / glibc 2.11一起使用的那个。

更新:对于好奇。我已经弄清楚为什么用一个足够大的缓冲区调用/proc/<pid>/cwd来存储整个路径是不行的。

查看do_proc_readlink的实现。请注意,它不直接使用提供的readlink("/proc/self/fd/<fd>", ...。相反,它会分配一个页面,并在调用d_path时将其用作临时缓冲区。换句话说,无论buffer有多大,buffer始终会限制为页面大小。这是amd64上的4096字节。与d_path相同!当PATH_MAX用完所提到的页面时,-ENAMETOOLONG本身就会返回。

2 个答案:

答案 0 :(得分:1)

readlink 可以与长度超过PATH_MAX的链接目标一起使用。有两个限制:链接本身的名称必须短于PATH_MAX(检查,"/proc/self/fd/<fd>"大约20个字符),并且提供的输出缓冲区必须足够大。您可能希望首先调用lstat以确定输出缓冲区应该有多大,或者只是通过增长缓冲区重复调用readlink

答案 1 :(得分:0)

由于unix(或linux,从现在开始)需要绑定传递给内核的参数大小这一事实,PATH_MAX诞生了。文件层次结构的扩展程度没有限制,并且始终可以访问所有文件,而不管它们在文件系统层次结构中的深度。实际上有限的是您可以从代表文件名的内核传递或接收的字符串的长度。这意味着您无法创建(因为您必须传递目标路径)一个长度超过此长度的符号链接,但您可以轻松地使路径远远超过此限制。

当您将文件名传递给内核时,您可以这样做有两个原因:命名文件(或设备,或套接字,或fifo,或其他),打开它等等。你做这个和你的文件名首先转到将该路径转换为inode的例程(这是内核实际管理的)。该例程开始从文件系统层次结构中的两个可能点开始扫描。这些点是根inode的inode引用和当前工作的工作方向的inode引用。选择哪个inode用作出发inode取决于路径开头是否存在前导/字符。从这一点来说,每次都会处理最多PATH_MAX个字符,但这可能会让我们深入到足以让我们无法一步到达根目录......

假设您使用路径更改当前目录,并执行chdir A/B/C/D/E/.../Z。在那里,你创建新的目录并做同样的事情,chdir AA/AB/AC/AD/AE/.../AZ,然后chdir BA/BB/BC/BD/...等等......系统中没有任何东西禁止你在文件系统中如此深入(你可以尝试你自己,我以前做过和测试过的)你可以成长到远远超过PATH_MAX的地图。但这只意味着您无法直接从文件系统根目录到达那里。您可以按照系统允许的步骤进行操作,具体取决于您修复根目录的位置(通过chroot(2)系统调用)或当前目录(通过chdir(2)系统调用)

可能你已经注意到(或没有)没有系统调用来从root获取你当前的工作目录路径......有几个原因:

  • root inode和curren working inode是两个本地到进程的概念。同一系统中的两个进程可以具有不同的工作目录,也可以有不同的根目录,直到它们能够共享任何共同点,并且无法从一个目录到达另一个目录。
  • inode路径可能不明确。嗯,这不适用于目录,因为不允许两个硬链接指向同一目录inode(这在较旧的unices中是可能的,其中必须使用mknod(2)系统调用创建目录,如果您可以访问某些hp-ux v6或旧的Unix SysV R4,您可以使用...条目创建目录---指向目录或类似事物的颗粒状,只是root并且知道如何使用{ {1}}系统调用)这个想法是当两个链接指向同一个inode时,哪个(或两个)指向根,哪一个是从根inode到当前目录的正确路径?
  • curren inode和root可以用足够远的路径分开,不适合mknod(2)限制。
  • 在获取根目录时可能涉及多个不同的文件系统(和文件系统类型)。因此,只有了解磁盘中存储的数据才能获得这些信息,您必须知道安装表。

由于这些原因,内核中没有直接支持来了解文件的根路径。并且没有办法获得路径(这是PATH_MAX命令所做的)而不是跟随pwd(1)条目并到达父目录并在那里搜索到到inode号的链接当前dir ...并重复此操作,直到父inode与最后访问的inode相同。只有这样你才会进入根目录(根目录,与其他进程根目录不同)

试试这个练习:

..

并查看您可以从层次结构中的根目录走多远。

注1

无法从开放描述符获取文件路径的另一个原因是您只能访问inode(您使用的路径i=0 while [ "$i" -lt 10000 ] do mkdir dir-$i cd dir-$i i=$(expr "$i" + 1) done 它与实际根路径无关,因为您可以使用符号链接和相对于工作目录,或者在打开调用和您想要访问路径的时间之间更改根目录,它甚至可以不存在,因为您可以open(2) d) inode信息没有引用inode的路径,因为文件可以有多个(甚至数百万)个路径。在inode中,您只有一个引用计数,这意味着实际在该inode上完成的路径数。