首页 > 解决方案 > 如何在 C 中创建指向现有文件或目录的硬链接?

问题描述

我正在寻找一个指向已经存在的文件或目录的硬链接。例如,用户选择创建指向名为“路径名”的文件的硬链接,并且用户可以选择链接文件的名称。

标签: chardlink

解决方案


在大多数现代版本的 Unix(或 Unix 变体)上,您不能创建指向目录的硬链接。如果您有足够的权限并且系统支持它,POSIX 允许它,但某些(我相信大多数)系统不允许它。

要创建硬链接,需要使用link()函数(系统调用):

if (link(existing_file, new_name) != 0)
    …link failed…

ln请注意,与命令不同,新名称必须完整。您不能将目录指定为新名称;您必须在目录中指定文件名。


您能否更具体地了解该link()功能的工作原理?

如果调用 as ,则link(source, target)某种文件必须存在,名称为in ,但指向文件的所有目录都必须存在。假设前提条件满足,系统调用成功后,可以通过 in或 in 的名称来引用相同的文件内容。sourceroottargettargetsourcetarget

例如,如果我要求用户选择现有路径,然后为正在创建的硬链接选择一个名称,我将如何处理?

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.csrc/libsoq子目录stderr.h中可用。它们是我报告程序错误的方式。一些系统有一个头文件和各种各样的功能,这些功能可以完成我的包所做的一些事情——玩得开心(我不喜欢它们,但也有一个强烈的 NIH 综合症案例)。还有其他方法可以连接文件名组件;一种是使用而不是 3次操作。<err.h>sprintf(name, "%s/%s", target, slash)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


推荐阅读