在大多数现代版本的 Unix(或 Unix 变体)上,您无法创建指向目录的硬链接。如果您有足够的权限并且系统支持,POSIX 允许它,但某些(我相信大多数)系统不允许它。
要创建硬链接,需要使用link()函数(系统调用):
if (link(existing_file, new_name) != 0)
…link failed…
请注意,与ln 命令不同,新名称必须完整。您不能将目录指定为新名称;您必须在目录中指定文件名。
您能否更具体地了解link() 函数的工作原理?
如果以link(source, target) 调用,则必须存在名称为source 的某种文件(它不能是目录,除非您可能具有“适当的权限”——这意味着超级用户或root 权限),并且不能有名称在target 中的文件,但指向target 名称的文件的所有目录都必须存在。假设前提条件满足,系统调用成功后,可以通过source中的名称或target中的名称来引用相同的文件内容。
例如,如果我要求用户选择现有路径,然后为正在创建的硬链接选择一个名称,我将如何处理?
FWIW,不要打扰提示 - 使用命令行参数,就像 ln 命令一样。
我还读到我必须取消链接原始文件。
您可能不会,但这取决于您想要实现的语义以及“原始文件”的含义。如果目标文件已经存在,则可以编写删除目标文件的代码(如ln -f source target 将删除目标文件,如果它已经存在)。可以编写在链接成功后删除源文件名的代码(如mv source target——注意不同的命令名)。可以编写代码来尝试确保创建通向目标的所有目录(如果它们尚不存在)(如mkdir -p $(dirname target))。等等。您可以决定允许通过命令行选项指定目标目录,而不是仅使用最后一个参数作为目标目录。等等。有很多可能性——你只需要决定你想要什么语义并实现它们。请注意,符号(软)链接和symlink() 函数的规则与硬链接的规则不同。
这是一些代码(源文件link37.c):
#include "stderr.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
int main(int argc, char **argv)
{
err_setarg0(argv[0]);
if (argc != 3)
err_usage("source target");
char *source = argv[1];
char *target = argv[2];
struct stat sb;
if (stat(source, &sb) != 0)
err_syserr("cannot access source file '%s': ", source);
if (stat(target, &sb) == 0)
{
if (!S_ISDIR(sb.st_mode))
err_error("name '%s' exists and is not a directory\n", target);
else
{
char *slash = strrchr(source, '/');
if (slash == 0)
slash = source;
else
slash++;
if (*slash == '\0')
err_error("name '%s' cannot end with a slash\n", source);
size_t baselen = strlen(target);
size_t filelen = strlen(slash);
size_t namelen = baselen + filelen + 2;
char *name = malloc(namelen);
if (name == 0)
err_syserr("failed to allocate %zu bytes memory: ", namelen);
memmove(name, target, baselen);
memmove(name + baselen, "/", 1);
memmove(name + baselen + 1, slash, filelen + 1);
target = name;
}
}
if (link(source, target) != 0)
err_syserr("failed to link '%s' to '%s': ", source, target);
if (target != argv[2])
free(target);
return 0;
}
最后的free() 并不是必须的。以 err_ 开头的函数在我的 GitHub 上的 SOQ(堆栈溢出问题)存储库中作为文件 stderr.c 和 stderr.h 在 src/libsoq 子目录中提供。它们是我报告程序错误的方式。一些系统有一个标题<err.h> 和各种各样的功能,这些功能可以完成我的包所做的一些事情——玩得开心(我不喜欢它们,但也有很强的 NIH 综合症案例)。还有其他方法可以连接文件名组件;一种是使用 sprintf(name, "%s/%s", target, slash) 而不是 3 个 memmove() 操作。
示例运行:
$ link37 link37.c chameleon
$ mkdir -p doc
$ link37 link37.c doc
$ link37 /Users/jonathanleffler/soq/ src
link37: name '/Users/jonathanleffler/soq/' cannot end with a slash
$ link37 /Users/jonathanleffler/soq src
link37: failed to link '/Users/jonathanleffler/soq' to 'src/soq': error (1) Operation not permitted
$ link37 link37.c chameleon
link37: name 'chameleon' exists and is not a directory
$ link37 /no/where/file.c /some/where/
link37: cannot access source file '/no/where/file.c': error (2) No such file or directory
$ link37 link37.c /some/where/
link37: failed to link 'link37.c' to '/some/where/': error (2) No such file or directory
$ rm -f doc/link37.c chameleon
$ rmdir doc 2>/dev/null
$
我有一个目录doc 已经存在(所以mkdir -p doc 没有做任何事情),但最后的rmdir doc 也没有损坏。这就是我没有使用rm -fr doc 的原因——我有一些信息想保留在doc 中。