7

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

通常,这不是问题 -fanotify_event_metadata包含一个文件描述符fd,所以我可以调用readlink/proc/self/fd/<fd>获取我的路径。

但是,如果路径超出,PATH_MAX readlink则无法再使用 - 它会失败并显示ENAMETOOLONG. 我想知道在这种情况下是否有办法获取文件路径。

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

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

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

有没有办法在不遍历整个文件系统的情况下从文件描述符中获取绝对路径?最好是与内核 2.6.32/glibc 2.11 一起工作的那个。

更新:对于好奇。我已经弄清楚为什么readlink("/proc/self/fd/<fd>", ...使用足够大的缓冲区来存储整个路径的调用不起作用。

查看do_proc_readlink的实现。请注意,它不buffer直接使用提供。相反,它分配一个页面并在调用d_path时将其用作临时缓冲区。换句话说,不管有多大bufferd_path总是会被限制在一个页面的大小。在 amd64 上是 4096 字节。一样PATH_MAX!当它用完提到的页面时,它-ENAMETOOLONG本身由prepend返回。

4

2 回答 2

1

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

于 2016-10-26T12:40:47.353 回答
0

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

当您将文件名传递给内核时,您可以这样做有两个原因,命名文件(或设备,或套接字,或 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)系统调用)的位置

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

  • 根 inode 和当前工作 inode 是两个本地到进程的概念。同一系统中的两个进程可以有不同的工作目录,也可以有不同的根目录,直到它们无法共享任何共同点,也无法从一个目录到达另一个。
  • inode 路径可能不明确。好吧,这对于目录来说不是这样,因为不允许两个硬链接指向同一个目录 inode(这在较旧的 unice 中是可能的mknod(2),如果您有权访问某些目录,则必须使用系统调用创建目录hp-ux v6 或旧的 Unix SysV R4 您可以创建带有...条目的目录---指向目录的祖父或类似的东西,只是作为 root 并且知道如何使用mknod(2)系统调用)这个想法是当两个链接指向相同的inode,然后哪个(或两者)进入根,哪一个是从根inode到当前目录的正确路径?
  • 当前 inode 和 root 可以被一条足够远的路径分开,以至于不适合PATH_MAX限制。
  • 获取根目录可能涉及多个不同的文件系统(和文件系统类型)。所以这不是只知道磁盘中存储的数据就可以得到的,你必须知道挂载表。

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

试试这个练习:

i=0
while [ "$i" -lt 10000 ]
do
    mkdir dir-$i
    cd dir-$i
    i=$(expr "$i" + 1)
done

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

注1

无法从打开的描述符中获取文件路径的另一个原因是您只能访问 inode(您使用的路径open(2)与实际的根路径无关,因为您可以使用符号链接和相对于工作目录,或在打开调用和您想要访问路径的时间之间更改的根目录,它甚至可以不存在,因为您可以拥有unlink(2)它)inode 信息没有引用到 inode 的路径,如一个文件可以有多个(甚至数百万个)路径。在 inode 中,您只有一个 ref 计数,这意味着在该 inode 上实际完成的路径数。

于 2016-10-26T21:22:11.260 回答