为什么 UNIX/Linux 中不允许目录硬链接?

为什么 UNIX/Linux 中不允许目录硬链接?

我在教科书中读到,Unix/Linux 不允许目录的硬链接,但允许软链接。是因为,当我们有循环时,如果我们创建硬链接,并且在一段时间后我们删除原始文件,它会指向一些垃圾值?

如果循环是不允许硬链接的唯一原因,那么为什么允许到目录的软链接呢?

答案1

这只是一个坏主意,因为无法区分硬链接和原始名称。

允许目录的硬链接会破坏文件系统的有向非循环图结构,可能会创建目录循环和悬空目录子树,这将使fsck任何其他文件树遍历器容易出错。

首先,为了理解这一点,我们来谈谈索引节点。文件系统中的数据保存在磁盘上的块中,这些块由索引节点收集在一起。您可以将索引节点视为文件。不过,索引节点缺少文件名。这就是链接发挥作用的地方。

链接只是指向 inode 的指针。目录是保存链接的索引节点。目录中的每个文件名只是一个索引节点的链接。在 Unix 中打开文件也会创建链接,但它是不同类型的链接(不是命名链接)。

硬链接只是指向该 inode 的额外目录条目。当您 时ls -l,权限后面的数字是指定链接计数。大多数常规文件都有一个链接。创建一个新的文件硬链接将使两个文件名指向同一个 inode。笔记:

% ls -l test
ls: test: No such file or directory
% touch test
% ls -l test
-rw-r--r--  1 danny  staff  0 Oct 13 17:58 test
% ln test test2
% ls -l test*
-rw-r--r--  2 danny  staff  0 Oct 13 17:58 test
-rw-r--r--  2 danny  staff  0 Oct 13 17:58 test2
% touch test3
% ls -l test*
-rw-r--r--  2 danny  staff  0 Oct 13 17:58 test
-rw-r--r--  2 danny  staff  0 Oct 13 17:58 test2
-rw-r--r--  1 danny  staff  0 Oct 13 17:59 test3
            ^
            ^ this is the link count

现在,您可以清楚地看到不存在硬链接之类的东西。硬链接与常规名称相同。在上面的例子中,testor test2,哪个是原始文件,哪个是硬链接?到最后,你无法真正分辨出来(即使通过时间戳),因为两个名称都指向相同的内容,相同的 inode:

% ls -li test*  
14445750 -rw-r--r--  2 danny  staff  0 Oct 13 17:58 test
14445750 -rw-r--r--  2 danny  staff  0 Oct 13 17:58 test2
14445892 -rw-r--r--  1 danny  staff  0 Oct 13 17:59 test3

-i标志ls显示行开头的 inode 编号。请注意testtest2具有相同的索引节点号,但test3具有不同的索引节点号。

现在,如果允许您对目录执行此操作,则文件系统中不同点的两个不同目录可能指向同一事物。事实上,子目录可以指向它的祖目录,从而创建一个循环。

为什么这个循环值得关注?因为当您遍历时,无法检测到您正在循环(在遍历时不跟踪 inode 编号)。想象一下,您正在编写du命令,该命令需要递归子目录以了解磁盘使用情况。如何du知道它何时进入循环?du为了完成这个简单的任务,它很容易出错,并且需要做大量的簿记工作。

符号链接是一种完全不同的野兽,因为它们是许多文件文件系统 API 倾向于自动遵循的特殊类型的“文件”。请注意,符号链接可以指向不存在的目标,因为它们是按名称指向的,而不是直接指向 inode。这个概念对于硬链接来说没有意义,因为“硬链接”的存在就意味着文件存在。

那么为什么可以du轻松处理符号链接而不是硬链接呢?我们可以从上面看到硬链接与普通目录条目没有区别。然而,符号链接是特殊的、可检测的且可跳过的!  du注意到符号链接是一个符号链接,并完全跳过它!

% ls -l 
total 4
drwxr-xr-x  3 danny  staff  102 Oct 13 18:14 test1/
lrwxr-xr-x  1 danny  staff    5 Oct 13 18:13 test2@ -> test1
% du -ah
242M    ./test1/bigfile
242M    ./test1
4.0K    ./test2
242M    .

答案2

除了挂载点之外,每个目录都有一个且唯一的父目录:..

一种方法是pwd检查 device:inode 中是否有“.”和 '..'。如果它们相同,则您已到达文件系统的根目录。否则,在父目录中查找当前目录的名称,将其压入堆栈,然后开始比较“../.”先是“../..”,然后是“../../.”与“../../..”等。一旦找到根,就开始从堆栈中弹出并打印名称。该算法依赖于这样一个事实:每个目录都有且只有一个父目录。

如果允许到目录的硬链接,那么多个父目录应该..指向哪一个?这是不允许目录硬链接的一个令人信服的原因。

目录的符号链接不会导致该问题。如果程序愿意,它可以lstat()对路径名的每个部分执行 an 操作并检测何时遇到符号链接。该pwd算法将返回目标目录的真实绝对路径名。事实上,某处有一段文本(符号链接)指向目标目录,这一事实几乎是无关紧要的。这种符号链接的存在不会在图中创建循环。

答案3

我想就这个问题补充几点。 Linux 中允许目录的硬链接,但方式受到限制。

我们测试这一点的一种方法是,当我们列出目录的内容时,我们会发现两个特殊目录“.”。和 ”..”。据我们所知 ”。”指向同一目录,“..”指向父目录。

因此,让我们创建一个目录树,其中“a”是父目录,目录“b”作为其子目录。

 a
 `-- b

记下目录“a”的索引节点。当我们从目录“a”执行操作时,ls -la我们可以看到“.”。目录也指向同一个 inode。

797358 drwxr-xr-x 3 mkannan mkannan 4096 Sep 17 19:13 a

在这里我们可以发现目录“a”有三个硬链接。这是因为 inode 797358 有 3 个名为“.”的硬链接。在“a”目录中,名称为“..” 在目录“b”中,其名称为“a”。

$ ls -ali a/
797358 drwxr-xr-x 3 mkannan mkannan 4096 Sep 17 19:13 .

$ ls -ali a/b/
797358 drwxr-xr-x 3 mkannan mkannan 4096 Sep 17 19:13 ..

所以在这里我们可以理解,硬链接只是用于目录与其父目录和子目录的连接。因此,没有子目录的目录将仅具有 2 个硬链接,因此目录“b”将仅具有两个硬链接。

阻止自由目录硬链接的原因之一是避免无限引用循环,这会使遍历文件系统的程序感到困惑。

由于文件系统被组织为树并且树不能具有循环引用,因此应该避免这种情况。

答案4

目录上的硬链接创建将是不可恢复的。假设我们有:

/dir1
├──this.txt
├──directory
│  └──subfiles
└──etc

我将它硬链接到/dir2.

所以/dir2现在还包含所有这些文件和目录

如果我改变主意怎么办?我不能只是rmdir /dir2(因为它不是空的)

如果我递归地删除/dir2...,它也会被删除/dir1

恕我直言,这在很大程度上是避免这种情况的充分理由!

编辑 :

评论建议通过对目录执行操作来删除该目录rm。但rm在非空目录上会失败,并且无论该目录是否硬链接,此行为都必须保留。所以你不能只是rm取消链接。它需要一个新的参数rm,只是说“如果目录 inode 的引用计数 > 1,则仅取消链接目录”。

这反过来又打破了另一个最不令人惊讶的原则:这意味着删除我刚刚创建的目录硬链接与删除普通文件硬链接不同......

我将重新表述我的句子:如果没有进一步的开发,硬链接创建将是不可逆转的(因为当前命令无法在不与当前行为不一致的情况下处理删除)

如果我们允许更多的开发来处理这个案例,陷阱的数量和数据丢失的风险如果您对系统的工作原理不够了解,那么这样的发展意味着,恕我直言,这是限制目录硬链接的充分理由。

相关内容