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


130

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

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


2
应该..指向哪里?尤其是在删除指向该目录的硬链接之后,在..?指向的目录中。它需要指向某个地方。
托尔比约恩Ravn的安德森

2
..不需要实际存在于任何驱动器上。无论如何,跟踪当前工作目录是操作系统的工作,因此,保持与每个进程的cwd相关联的inode列表并在看到使用时引用它也应该相对简单..。当然,这意味着需要牢记创建符号链接,但是您已经必须小心不要破坏符号链接,而且我认为附加规则不会使它们失去作用。
Parthian Shot

我喜欢这个解释。简洁易读和/或略读。
特雷弗·博伊德·史密斯

Answers:


143

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

允许对目录的硬链接将破坏文件系统的有向无环图结构,可能会创建目录循环并悬挂目录子树,这将使fsck文件树和其他文件树更容易出错。

首先,要了解这一点,让我们讨论一下索引节点。文件系统中的数据保存在磁盘上的块中,这些块由一个索引节点收集在一起。您可以将inode视为THE文件。索引节点缺少文件名。那就是链接进入的地方。

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

硬链接只是指向该索引节点的额外目录条目。当您使用时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

现在,您可以清楚地看到没有硬链接之类的东西。硬链接与常规名称相同。在上面的示例test或中test2,哪个是原始文件,哪个是硬链接?最后,您无法真正分辨(即使按时间戳记),因为两个名称都指向相同的内容,相同的索引节点:

% 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具有相同的inode编号,但test3具有不同的inode编号。

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

为什么要关注此循环?因为在遍历时,无法检测到是否在循环(遍历时不跟踪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    .

7
Allowing hard links to directories would break the directed acyclic graph structure of the filesystem。您能否使用硬链接解释有关循环问题的更多信息?为什么可以使用符号链接
user3539 2011年

33
他们似乎在Mac上通过将循环检测添加到link()系统调用中来允许它,并拒绝允许您创建目录硬链接(如果会创建循环)。似乎是一个合理的解决方案。
psusi 2011年

10
@psusi mkdir -pa / b; nocheckln ca; mv ca /​​ b; -nocheckln有一个理论上的ln,它不检查目录args,而只是传递给链接,并且因为没有循环,所以我们都擅长创建'c'。然后我们将'c'移至'a / b',然后从a / b / c创建一个循环-> a /-检查link()不够好
Danny Dulai

3
周期非常糟糕。Windows的“连接”是硬链接目录,存在此问题。如果您不小心将权限应用于整个配置文件,它将发现一系列创建无限循环的联结。在目录中递归递归直到路径长度限制停止它为止。
doug65536 2013年

4
@WhiteWinterWolf,根据此链接,他们特别添加了对时间机器的支持,但只允许root用户使用:superuser.com/questions/360926/…–
psusi

14

除挂载点外,每个目录只有一个父目录:..

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

如果允许使用目录的硬链接,则多个父级中的哪一个应该..指向?这就是为什么不允许硬链接到目录的一个令人信服的原因。

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


3
对此不太确定。如果我们认为..这是到父级的一种虚拟硬链接,则从技术上讲,链接的目标只能有一个其他链接。pwd只需使用其他算法即可解析路径。
Benubird 2014年

13

您可以使用绑定安装来模拟硬链接目录

sudo mount --bind /some/existing_real_contents /else/dummy_but_existing_directory
sudo umount /else/dummy_but_existing_directory

7

关于这个问题,我想补充几点。在Linux中允许目录的硬链接,但是这是受限制的。

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

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

 a
 `-- b

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

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

在这里我们可以发现目录“ a”具有三个硬链接。这是因为inode 797358具有三个硬链接,名称为“”。在“ 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”将只有2个硬链接。

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

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


1
好例子。这消除了我的怀疑。因此,以特殊方式处理这些情况以避免无限循环。对?
G Gill 2014年

1
由于我们使用有限的方式来允许目录的硬链接,即“ ..”和“。” 我们不会达到无限循环,因此我们不需要任何特殊方法来避免发生这种情况:)
Kannan Mohan 2014年

5

以下都不是不允许硬链接到目录的真正原因;每个问题都非常容易解决:

  • 树结构中的循环导致遍历困难
  • 多父母,那么哪个才是“真正的”父母?
  • 文件系统垃圾回收

真正原因(如通过暗示托尔比约恩@安德森Ravn的)来当你删除它有多个父目录,从目录指向..

..现在应该指向什么?

如果该目录已从其父目录中删除,但其链接数仍大于该目录数,则0必须有某些内容仍指向该目录。你不能..无所事事。许多程序都依赖于此..,因此系统必须遍历整个文件系统,直到找到指向已删除目录的第一件事,才进行更新..。要么,要么文件系统必须维护指向硬链接目录的所有目录的列表。

无论哪种方式,这都会带来性能开销,并给文件系统元数据和/或代码带来额外的复杂性,因此设计人员决定不允许这样做。


3
这也很容易解决:保留子目录的父目录,在添加或删除子目录的链接时会更新该目录。删除规范的父母(孩子的目标..)时,请更新..以指向列表中的其他父母之一。
2015年

2
我同意。没有火箭科学来解决。但是,这仍然会增加性能,并且会占用文件系统元数据中的一些额外空间并增加复杂性。因此,设计人员采用了一种简单,快速的方法-不允许链接到硬目录。
Lqueryvg

1
符号链接到目录“违反确定的语义和行为”,但仍然允许它们。因此,某些命令需要一些选项来控制是否遵循符号链接(例如,find和cp中的-L)。当程序后跟“ ..”时,会造成进一步的混乱,因此在遍历符号链接之后,pwd和/ bin / pwd的输出差异。没有“ Unix答案”。只是设计决策。正如我在回答中所说,这一点围绕“ ..”的含义展开。不幸的是,答案中甚至没有提到“ ..”,其他所有人都这么讨厌地投票。
Lqueryvg

顺便说一句,我并不是说我赞成与Dirs建立硬链接。一点也不。我不希望自己的日常工作变得比现在更辛苦。
Lqueryvg

这不是POSIX所说的,但IMO'..'绝不应该是文件系统的概念,而应在路径上通过句法解决,因此a/..始终意味着.。URL就是这样工作的,顺便说一句。是浏览器在甚至未到达服务器之前就解决了“ ..”。而且效果很好。
ybungalobill

3

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

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

我将其硬链接到/dir2

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

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

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

恕我直言,这是避免这种情况的充分充分的理由!

编辑:

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

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

我将重述一下:如果不做进一步的开发,硬链接的创建将是不可还原的(因为没有当前命令可以处理删除操作而又不会与当前行为保持一致)

如果我们没有更多的开发来处理这种情况,陷阱的数量以及数据丢失风险(如果您还不足以了解系统的工作方式),那么这种开发意味着IMHO是限制目录上的硬链接的充分理由。


那应该不成问题。对于您的情况,当我们创建到dir2的硬链接时,我们必须对到dir1中的所有内容进行硬链接,因此,如果我们重命名或删除dir2,则只会删除指向inode的额外链接。而且这不会影响dir1及其内容,因为到inode的链接至少为一个(dir1)。
坎南·莫汉

3
您的说法不正确。您只需取消链接即可,而不需要rm -rf。如果链接计数达到0,则系统将知道它也可以删除所有内容。
LtWorf

rm无论如何,这或多或少是底层的所有工作(取消链接)。请参阅:unix.stackexchange.com/questions/151951/…这确实不是问题,与硬链接文件相比,没有任何其他问题。取消链接只会删除命名的引用,并减少链接数。rmdir不会删除非空目录的事实是无关紧要的-它也不会这样做dir1 。硬链接不是数据的副本,它们是相同的实际文件,因此实际上“删除” dir2文件将删除dir1的目录列表。您将始终需要取消链接。
BryKKan

您不能像普通文件一样直接取消链接,因为如果rm目录不为空,则不要取消链接。请参阅编辑。
Pierre-Olivier Vares

1

这是一个很好的解释。关于“多名父母中的哪一位应该..指向?” 一种解决方案是使进程以inode或字符串的形式维护其完整wd路径。因为可以更改名称,所以inode会更健壮。至少在过去,每个打开的文件都有一个内核内索引节点,每当打开一个文件时,该索引节点就会增加,而在关闭时会减少。当它达到零时,它和它指向的存储将被释放。当该文件不再由任何人打开时,它将被丢弃(核心副本)。如果子目录位于另一个进程的路径中时,如果某个其他进程将目录移动到另一个目录,则这将使路径保持有效。与删除打开文件的方式类似,只是将其从目录中删除,

贝尔实验室UNIX中曾经免费允许使用硬链接目录,至少是V6和V7,不知道Berkeley或更高版本。不需要标志。你能做循环吗?是的,不要那样做。很明显,如果进行循环,您在做什么。如果您的另一端方便地挂在舱壁的钩子上,则在等待转身跳下飞机时,也不要在脖子上打结。

什么希望用它做今天是硬链接lhome回家,这样我可以有/家/可用与否ADMINIST /家被掩盖了超过家庭的automout,有一个名为ADMINIST到/ lhome符号链接是自动挂载/管理员。这使我拥有一个无论我的主home文件系统处于何种状态都可以运行的管理帐户。这针对Linux的实验,但是我认为一次学习基于UCB的SunOS的自动挂载是在ascii字符串级别完成的。很难看到如何将它们作为任意FS之上的一层来完成。

我在其他地方读过。和..也不再是目录中的文件。我确信所有这些都是有充分的理由的,并且由于这些原因,我们享受的很多东西(例如能够挂载NTFS)都是可能的,但是UNIX的某些优雅之处在于实现。这种优雅所提供的正是诸如通用性和可延展性之类的好处,使它如此坚固并且可以承受四十年。随着我们松散优雅的实现,它最终将变得像Windows(我希望我错了!)。然后,有人会基于优雅的原则创建一个新的OS。需要考虑的事情。也许我错了,我(显然)不熟悉当前的实现。这 令人惊奇的是,尽管大多数情况下30年前的理解在Linux上是多么有用...


我想,虽然我可能是错的,这...是不是在文件系统中的硬链接,为现代文件系统。但是,文件系统驱动程序会伪造它们。这些文件系统停止了硬链接目录。对于旧文件系统,这是可能的(但很危险)。要执行您尝试的操作,请查看mount --bind,另请参见mount --make…容器。
ctrl-alt-delor

0

据我所知,主要原因是能够更改目录名称而不会弄乱使用其工作目录引用其他文件的运行程序很有用。假设您使用Wine来运行~/.newwineprefix/drive_c/Program Files/Firefox/Firefox.exe,而您想将整个前缀移到~/.wine。如果出于某种奇怪的原因,Firefox drive_c/windows通过引用进行访问../../windows,则重命名~/.newwineprefix会中断该实现的实现,..该实现会将父目录作为文本字符串而不是inode进行跟踪。

存储单个父目录的索引节点比尝试跟踪每个路径(包括文本字符串和一系列索引节点)要简单得多。

另一个原因是行为不当的应用程序可能能够创建循环。行为正常的应用程序应该能够检查正在移动的目录的inode是否与要移动到的任何嵌套目录的inode相同,就像您不能将目录移动到它本身一样,但这可能不强制执行在文件系统级别。

另一个原因可能是,如果您可以硬链接目录,则希望防止硬链接无法修改的目录。find具有安全方面的考虑,因为它用于从临时目录中清除其他用户创建的文件,如果用户在find调用另一个命令时切换了符号链接的真实目录,则可能会导致问题。能够硬链接重要目录将迫使管理员添加额外的测试,find以避免影响它们。(好的,您已经不能对文件执行此操作,因此此原因无效。)

另一个原因是,在文件系统损坏或损坏的情况下,存储父目录的inode可能会提供额外的冗余。如果您想..列出所有与该目录硬链接的父目录,那么如果当前目录被取消链接,则很容易找到一个不同的任意父目录,这不仅违反了硬链接相等的想法,还必须更改文件系统存储并使用inode。让程序将路径视为一系列目录索引节点(每个硬链接都唯一)可以避免这种情况,但是在文件系统损坏的情况下,您将不会获得冗余。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.