简体   繁体   English

如何在C中创建到现有文件或目录的硬链接?

[英]How to create a hard link to an existing file or directory in C?

I am looking to create a hard link to a file or directory that already exists. 我正在寻找创建到已经存在的文件或目录的硬链接。 For instance a user chooses to create a hard link to a file named "pathname" and the user can choose the name of the link file. 例如,用户选择创建到名为“ pathname”的文件的硬链接,并且用户可以选择链接文件的名称。

On most modern versions of Unix (or variants of Unix), you cannot create a hard link to a directory. 在大多数现代版本的Unix(或Unix的变体)上,您不能创建指向目录的硬链接。 POSIX allows it if you have sufficient privileges and the system supports it, but some (I believe most) systems do not allow it. 如果您具有足够的特权并且系统支持它,则POSIX允许它,但是某些(我认为大多数)系统不允许它。

To create a hard link, you need to use the link() function (system call): 要创建硬链接,您需要使用link()函数(系统调用):

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

Note that the new name must be complete, unlike the ln command. 请注意,与ln命令不同,新名称必须完整。 You cannot specify a directory as the new name; 您不能将目录指定为新名称。 you must specify the file name within the directory. 您必须在目录中指定文件名。


Can you be a bit more specific about how the link() function works? 您能否更具体地了解link()函数的工作方式?

If invoked as link(source, target) , then a file of some sort must exist with the name in source (it can't be a directory unless perhaps you have 'appropriate privileges' — which means superuser or root privileges), and there must not be a file with the name in target , but all the directories leading to the file named by target must exist. 如果作为link(source, target)调用,则必须以source的名称存在某种文件(除非您具有“适当的特权”,即超级用户或root特权,否则它不能是目录),并且在那里不得是名称为target的文件,但所有指向target命名的文件的目录都必须存在。 Assuming the preconditions are met, after the system call is successful, then you can refer to the same file contents by either the name in source or the name in target . 假设满足前提条件,则在系统调用成功之后,您可以通过source的名称或target的名称来引用相同的文件内容。

For instance if I ask a user to choose an existing path and then choose a name for the hard link being created, how would I go about this? 例如,如果我要求用户选择一个现有路径,然后为要创建的硬链接选择一个名称,我将如何处理?

FWIW, don't bother with prompting — use command line arguments, like the ln command does. FWIW,不要打扰提示-使用命令行参数,就像ln命令一样。

I also read that I have to unlink the original file. 我还读到我必须取消原始文件的链接。

You probably don't, but that depends on the semantics of what you want to achieve, and what you mean by 'original file'. 您可能不需要,但这取决于要实现的语义以及“原始文件”的含义。 It would be possible to write code that removes the target file if it already exists (like ln -f source target would remove the target file if it already exists). 可以编写代码删除目标文件(如果已存在)(例如ln -f source target会删除目标文件(如果已存在))。 It would be possible to write code that removes the source file name after the link is successful (like mv source target — note the different command name). 链接成功后,可能会编写删除源文件名的代码(例如mv source target ,请注意不同的命令名)。 It would be possible to write code that attempts to ensure that all the directories leading to the target are created if they don't already exist (like mkdir -p $(dirname target) ). 可以编写代码来尝试确保所有导致目标的目录(如果它们不存在)都已创建(例如mkdir -p $(dirname target) )。 Etc. You could decide to allow a target directory to be specified by a command line option, instead of just using the last argument as the target directory. 等等,您可以决定允许通过命令行选项指定目标目录,而不仅仅是使用最后一个参数作为目标目录。 Etc. There are lots of possibilities — you just have to decide what semantics you want and implement them. 等等,有很多可能性-您只需要确定所需的语义并实现它们即可。 Note that the rules for symbolic (soft) links and the symlink() function are different from the rules for hard links. 请注意,符号(软)链接的规则和symlink()函数与硬链接的规则不同。

Here's some code (source file link37.c ): 这是一些代码(源文件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;
}

The free() at the end is not really necessary. 最后的free()并不是必须的。 The functions starting err_ are available in my SOQ (Stack Overflow Questions) repository on GitHub as files stderr.c and stderr.h in the src/libsoq sub-directory. 起始于err_的函数在GitHub上的我的SOQ (堆栈溢出问题)存储库中以src / libsoq子目录中的文件stderr.cstderr.h 可用 They're how I report errors in programs. 它们就是我报告程序错误的方式。 Some systems have a header <err.h> and a motley assortment of functions that do some of what my package does — have fun with them (I don't like them, but there's a strong case of NIH syndrome too). 某些系统具有标头<err.h>和各种各样的功能,这些功能可以完成我的程序包所执行的某些操作-可以与它们一起玩(我不喜欢它们,但也有很多NIH综合征的情况)。 There are other ways to concatenate the file name components; 还有其他方法可以串联文件名组件; one would be to use sprintf(name, "%s/%s", target, slash) instead of 3 memmove() operations. 一种方法是使用sprintf(name, "%s/%s", target, slash)而不是3个memmove()操作。

Sample run: 样品运行:

$ 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
$

I had a directory doc already in existence (so the mkdir -p doc didn't do anything), but the rmdir doc at the end did no damage either. 我已经有一个目录doc (因此mkdir -p doc没有执行任何操作),但是末尾的rmdir doc也没有损坏。 That's why I didn't use rm -fr doc though — I have information I want to keep in doc . 这就是为什么我没有使用rm -fr doc的原因-我有要保留在doc

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM