在 Linux 中,符号链接的值可以比 PATH_MAX 长吗?

in Linux, can the value of a symlink be longer than PATH_MAX?

幼儿园的每个 child 都知道,Linux 中的文件路径不能超过 PATH_MAX 个字符。

但是在我的系统上进行试验,命令

ln -s $(for i in {0..1024}; do printf dir/../; done)foobar foobar1

失败并显示错误消息 File name too long

我不太明白为什么。这里没有长文件名,只有文件 foobar1 的预期 内容 很长。甚至还没有人试图 遍历 符号 link 的内容以到达目标。当然,我可以拥有一个内容比 PATH_MAX 大得多的文件。

另一方面,像

这样的命令
for i in {0..4096}; do ln -s $i $(expr $i + 1); done

成功。只有当我试图遍历链时,系统才会抱怨。

但我对遍历任何东西都不感兴趣。我正在编写必须读取符号 link 值(无需遍历)的软件,我想知道是否需要考虑很长的值。

Linux 中哪里记录了不允许这样做?还是 file-system 依赖于实现并且可以更改?

对 symlink 的调用失败

ENAMETOOLONG (File name too long)

这是来自内核。它也依赖于文件系统,因此你找不到 SYMLINK_MAX 定义的任何地方。例如...

XFS

if (pathlen < 0 || pathlen > MAXPATHLEN) {
  xfs_alert(mp, "%s: inode (%llu) bad symlink length (%lld)",
  __func__, (unsigned long long) ip->i_ino,
  .......
}

和 MAXPATHLEN == 1024

reiserfs

item_len = ROUND_UP(strlen(symname));
if (item_len > MAX_DIRECT_ITEM_LEN(parent_dir->i_sb->s_blocksize)) {
  retval = -ENAMETOOLONG;
  .......
}

ext2

struct super_block * sb = dir->i_sb;
int err = -ENAMETOOLONG;
unsigned l = strlen(symname)+1;
.....
if (l > sb->s_blocksize)
   goto out;
....
return err;

symlink 应该可以在任何文件名所在的地方使用,因此它小于 PATH_MAX 是合乎逻辑的,这与它里面的内容无关。来自 Linux.

上的 symlink 手册页
ENAMETOOLONG
    target or linkpath was too long.

这是模糊的,因为文件系统定义了它。您可以达到的另一个限制是 _POSIX_SYMLINK_MAX.

#define _POSIX_SYMLINK_MAX  255 

要测试它,请在目录中创建以下两个 link。这个一定会成功

ln -s foo Fh5LNDZYm2vUlf3jypJtAaX1ElqHZAF4ivsuq8sHyKVLDrYi4zR3T2QcXwS5TPRTys9aUxuh3qtnlNnFQGInmLiM7GK1xpN78ZbcD4JWizfPa7VWwhR4XWpvaJLHCIONUTC6A8fjNVfhv434vWckuKDzTacno0LE13mBVHuj5RDgJIkmW1zUcMMh5E38VisPxSN7BGxBVXHtn0cUPmZLmYSzGrOJqEJzimvwh2uDi8uXEOwBLbsfYlxBOz1kw8P

这个会失败

ln -s foo Fh5LNDZYm2vUlf3jypJtAaX1ElqHZAF4ivsuq8sHyKVLDrYi4zR3T2QcXwS5TPRTys9aUxuh3qtnlNnFQGInmLiM7GK1xpN78ZbcD4JWizfPa7VWwhR4XWpvaJLHCIONUTC6A8fjNVfhv434vWckuKDzTacno0LE13mBVHuj5RDgJIkmW1zUcMMh5E38VisPxSN7BGxBVXHtn0cUPmZLmYSzGrOJqEJzimvwh2uDi8uXEOwBLbsfYlxBOz1kw8Pk

一个是 255 个字符长,另一个是 256 个字符。在你的问题中,这个命令可以工作,因为每个人 link 都有自己的 inode 并且名称很短。

for i in {0..4096}; do ln -s $i $(expr $i + 1); done