为什么不允许在目录中使用硬链接?

我在使用Ubuntu 12.04。当我尝试为任何目录创建硬链接时,它都失败了。我可以为文件系统范围内的文件创建硬链接。我知道不能为超出文件系统的文件创建硬链接的原因。
我尝试了以下命令:
$ ln /Some/Directory /home/nischay/Hard-Directory
hard link not allowed for directory
$ sudo ln /Some/Directory /home/nischay/Hard-Directory
[sudo] password for nischay: 
hard link not allowed for directory

我只想知道背后的原因。这是所有GNU/Linux发行版和Unix版本(BSD,Solaris,HP-UX,IBM AIX)都一样吗?还是只有在Ubuntu或Linux中出现?

10http://unix.stackexchange.com/questions/22394/why-hard-links-not-allowed-to-directories-in-unix-linux - Nanne
2尝试使用ln -F <src> <dst>命令,或许会起作用。确实,在旧版本的Unix中,超级用户可以使用这个命令。有人还记得是UCB还是System V吗?是的,可能会发生一些不好的事情,但通常不会。据我回忆,rmdir命令知道在硬链接处停止删除。然而,用户可能会因为混淆而错误地删除东西。 - Steve Pitchers
@StevePitchers rmdir 如何以特殊方式处理硬链接?硬链接只是普通链接的附加链接。甚至很难找出是否存在不寻常的额外链接,而不需要额外的记录。 - Volker Siegel
1每个节点存储指向它的硬链接数量:只有当没有剩余链接时,内容才会被释放。因此,rmdir 可以判断目录是否有来自其他位置的链接。递归删除 rm -r 必须小心编码,以确保即使出现“权限被拒绝”等错误,它也能正确执行。顺便说一下,UCB = BSD,呃! - Steve Pitchers
2我已经在目录上执行了ln -F命令,并且它能够正常工作。但是你不敢在之后删除该目录,因为担心会破坏文件系统。 - Edward Falk
6个回答

目录硬链接以多种方式破坏文件系统

它们允许您创建循环

对目录的硬链接可以链接到其自身的父目录,从而创建一个文件系统循环。例如,以下命令可以使用反向链接l创建一个循环:

mkdir -p /tmp/a/b
cd /tmp/a/b
ln -d /tmp/a l

一个带有目录循环的文件系统具有无限深度。
cd /tmp/a/b/l/b/l/b/l/b/l/b

避免在遍历这样的目录结构时陷入无限循环有些困难(尽管例如POSIX要求find避免这种情况)。
具有这种硬链接的文件系统不再是一棵树,因为树在定义上不能包含循环。

它们破坏了父目录的明确性

通过文件系统循环,存在多个父目录:
cd /tmp/a/b
cd /tmp/a/b/l/b

在第一个案例中,/tmp/a/tmp/a/b 的父目录。
在第二个案例中,/tmp/a/b/l/tmp/a/b/l/b 的父目录,也就是说和 /tmp/a/b 是一样的。
所以它有两个父目录。
即使没有循环,对同一个目录创建多个硬链接也会产生模糊的父目录。

它们复制文件

文件通过路径来识别,解析符号链接后得到的结果。

/tmp/a/b/foo.txt
/tmp/a/b/l/b/foo.txt

是不同的文件。
文件有无限多个进一步的路径。当然,它们的inode号码是相同的。但是如果你没有明确期望循环,就没有理由去检查。

一个目录硬链接也可以指向一个子目录,或者一个既不是子目录也不是父目录的深度。在这种情况下,链接的子文件将被复制为两个文件,通过两个路径进行标识。

你的例子

$ ln /Some/Directory /home/nischay/Hard-Directory
$ echo foo > /home/nischay/Hard-Directory/foobar.txt
$ diff -s /Some/Directory/foobar.txt /home/nischay/Hard-Directory/foobar.txt
Files /Some/Directory/foobar.txt and /home/nischay/Hard-Directory/foobar.txt are identical
$ echo bar >> /Some/Directory/foobar.txt
$ diff -s /Some/Directory/foobar.txt /home/nischay/Hard-Directory/foobar.txt
Files /Some/Directory/foobar.txt and /home/nischay/Hard-Directory/foobar.txt are identical
$ cat /Some/Directory/foobar.txt
foo
bar

如何使软链接到目录起作用呢?
通常,一个可能包含软链接甚至软链接目录循环的路径仅用于标识和打开文件。它可以被用作一个正常的线性路径。
但是在其他情况下,当路径用于比较文件时,路径中的符号链接可以首先被解析,将其转换为一个最小且通常约定的表示,创建一个规范路径:
这是可能的,因为软链接可以扩展为不带链接的路径。在处理路径中的所有软链接后,剩余的路径是树的一部分,其中路径始终是唯一的。
命令 "readlink" 可以将路径解析为其规范路径:
$ readlink -f /some/symlinked/path

软链接与文件系统使用的链接不同。
软链接不能引起所有的麻烦,因为它与文件系统内部的链接不同。如果需要,可以将其与硬链接区分开,并解析为不包含符号链接的路径。
从某种意义上说,添加符号链接不会改变基本的文件系统结构 - 它保持原样,但是增加了类似应用层的更多结构。
man readlink中得到的信息:
 NAME
        readlink - print resolved symbolic links or canonical
        file names
 
 SYNOPSIS
        readlink [OPTION]... FILE...
 
 DESCRIPTION
        Print value of a symbolic link or canonical file name
 
        -f, --canonicalize
               canonicalize by  following  every  symlink  in
               every component of the given name recursively;
               all but the last component must exist
        [  ...  ]

1为什么软链接不能完成所有这些任务? - dev
1@Tanay 对的,将其与软链接的类似情况进行比较可能有助于解释。我会尝试一下。 - Volker Siegel
1这与仅限于目录有何关联?依我理解,这些问题也同样适用于硬链接文件。此外,我认为硬链接是一种简便的方法,可以改变给定目录的权限,允许其他人进入,而无需同时允许他们进入父链。如果你没有添加/修改组的能力,听起来非常有用。 - inetknght
很好的回答,但是...苹果是如何解决Time Machine的这些问题的呢? - Damien
听起来很有趣!但是我对这个问题一无所知 - 你能给我一个提示吗? - Volker Siegel
1你所说的“它们会复制文件”也适用于文件的硬链接,然而这些仍然是允许的。 - Ruslan
你无论如何都不能使用FUSE创建循环吗? - flarn2006
@flarn2006 说得好。我猜你指的是涉及到FUSE文件系统和底层文件系统的循环。你知道它会发生什么,或者你见过它失败吗? - Volker Siegel
"他们打破了父目录的不明确性":鉴于父目录在路径上是向上一级,根据您的例子,不应该存在"不明确性"。是否需要重新表述这个例子? - nvidot

“通常情况下,你不应该使用硬链接”这种说法太过笼统。你需要理解硬链接和符号链接之间的区别,并根据实际情况使用它们。每种链接都有自己的优点和缺点:
符号链接可以: - 指向目录 - 指向不存在的对象 - 指向位于不同文件系统的文件和目录
硬链接可以: - 防止被引用的文件被删除
硬链接在执行“写时复制”应用程序时特别有用。它们允许你在两个版本之间保留一个目录结构的备份副本,同时只使用那些发生变化的文件所占用的空间。请注意,实现此功能必须首先断开链接(否则修改也会应用到原始文件上!)。
命令`cp -al`在这方面特别有用。它可以创建一个完整的目录结构副本,其中所有文件都通过硬链接与原始文件关联。然后,你可以更新结构中的文件(在仅创建这些文件的实际副本之后),只有你更新的文件将占用额外的空间。这在维护多代备份时特别有用。

52关于最后一段,如果您编辑“复制”的硬链接文件,则原始文件也会更改-请参见http://unix.stackexchange.com/questions/70531/cp-al-snapshot-whose-hard-links-get-directed-to-a-new-file-when-edited - marcin
42对硬链接的描述有些误导。硬链接确实“防止被引用的文件被删除”,但这只是硬链接的一个副作用。绝对不可能在一个目录中创建硬链接,然后改变“原始”文件,并期望硬链接仍然指向旧内容。事实上,硬链接的核心真相是它根本不是一个链接,至少不比原始的“文件”更多,它只是指向同一个文件的另一个名称而已。硬链接只是指向同一文件的另一个名称而已。 - matty
7备份的想法很好,实际上我经常使用它,但我认为应该警告用户改变文件也会改变备份。 - Mark
6嘿,符号链接不一定要指向任何东西。ln -s "Don't use this directory" README 是合法的。事实上,如果你仔细想想,一个目录可以被用作关系数据库,而不包含任何实际文件。 - Edward Falk
有点离题,但如果你正在寻找一种利用链接的备份解决方案,可以看看https://github.com/laurent22/rsync-time-backup -- 它创建了时间点快照,并且会对未更改的文件进行硬链接。 - STW
9这并没有回答问题,而且有些信息是错误的。 - wjandrea
2硬链接不会“写时复制”。 - ceving

FYI,您可以通过使用mount来实现目录的硬链接相同的功能:
mount -t bind /var/www /home/user/workspace/www

这非常危险,因为大多数工具和程序都不会意识到这种绑定。我曾经做过类似上面例子中的事情,然后继续执行了rm -rf /home/user。幸运的是,在/var/www中没有相关内容。

10我使用了 mount --bind <src> <dest>。小心使用,不要把 src 给搞丢了 ;) - kachar
5我得到:mount: 未知的文件系统类型 'bind' - Wizek
6在 Busybox 中,它是 mount -o bind src dest - Mat M
@MatM 和 Debian 一样。 - hanshenrik
6如果你只需要挂载用于读取,可以在挂载点上设置权限,避免rm -rf问题的发生。https://superuser.com/questions/320415/linux-mount-device-with-specific-user-rights - zanerock
好的,但是(递归)挂载是否能够像硬链接一样破坏文件系统? - FantomX1
1不回答问题。 - Jim Balter
错误的命令行选项,-t 需要 fstype,而 bind 不是。你应该使用 -o bind--bind。你应该更正这个答案。 - mook765

不允许创建硬链接目录的原因有些技术性。基本上,它们会破坏文件系统结构。通常情况下,你不应该使用硬链接。符号链接可以提供大部分相同的功能,而不会引起问题(例如 `ln -s target link`)。

29硬链接有很多实际应用场景。说你通常不应该使用它们有点太笼统了。 - Sander Steffann
6提供实际回答原帖问题(以及我的问题)的链接可以得到+2分,但发表意见("你通常不应该使用硬链接" - 如果有支持的链接,那就没问题了)会被扣1分。这很好,因为我无论如何都不能给+2分。;D - msb
10请尝试在回答中概括链接的内容,并将链接保留为参考。这是Stack Exchange的良好做法,以避免链接失效,谢谢。 - Oxwivi
1@EliranMalka 或许文件系统结构没有被破坏,但显然超文本结构却受到了影响。 - Manchineel

这不会是一个硬链接。

目录本质上是一组指向父目录的数据块的索引节点,包括 ..(用于子目录将其作为超级/父目录),目录本身,.,子目录和文件。

硬链接不应该改变索引节点所指向的数据块中的数据。

对目录的硬链接要么需要添加另一个父目录(另一个 .. 索引节点),从而违反了上述规则(即硬链接不应该改变被链接到的索引节点所索引的数据块),要么需要使硬链接的目录与原始目录不同(它将具有指向用户视角下的非明显父目录的父目录的索引节点)。

澄清

假设您有一个名为dir-1a的目录,其父目录是/。然后,您在/的子目录之一dir-1b内创建了一个硬链接,但未添加第二个..到链接的目录中(因为这将改变数据块)。

现在,如果您查看 dir-1b 中的硬链接,它的父目录将不是 dir-1b,而是根目录 /。这意味着您可以区分硬链接和原始文件。

硬链接并不打算与原始文件有所区别。

这种情况会对期望硬链接与原始文件无法区分的过程产生连锁效应。目录被设计为将 inode 包含在自身以及其硬链接(父目录)的目录中。


这里的其他答案都很好,但是这里有一个简单实用的原因。假设你有一个名为fred的目录,里面有一堆文件和子目录,比如:
  • fred/barney/wilma
  • fred/betty
现在你创建了一个名为homer的硬链接指向fred。然后你执行rm -rf fred。然后你尝试执行cd homer/barney/wilma,结果告诉你homer/barney/wilma不存在。然后你尝试打开homer/betty,结果也找不到了!事实上,homer是完全空的!
为什么?那么,rm -rf 做了什么呢?它不仅删除目录,还删除目录中的内容。所以它首先删除 fred/barney/wilma,然后删除 fred/barney,最后删除 fred/betty。由于 fredhomer 是同一个对象的两个名称,换句话说,它删除了 homer/barney/wilma,然后是 homer/barney,最后是 homer/betty。删除 fred 的内容意味着删除了 homer 的内容。
为什么 rm -rf 会这样删除所有文件呢?想象一下,如果在删除之前 fred 没有硬链接,会发生什么。那么文件 barney/wilmabetty 将成为孤立的文件,占用空间而无法删除,除非运行一个查找孤立文件的文件系统清理工具。
现在,可以编写一个能够识别硬链接的版本的rm,如果存在指向该目录的硬链接,则不会删除目录的内容,但您还必须确保任何其他可以删除目录的程序都能识别硬链接。我认为这是一场等待发生的灾难,而且好处也相当可疑。这也意味着硬链接不再像在标准的Unix文件系统中那样正常工作。