Как создать жесткую ссылку на существующий файл или каталог в C? - PullRequest
0 голосов
/ 06 декабря 2018

Я хочу создать жесткую ссылку на файл или каталог, который уже существует.Например, пользователь решает создать жесткую ссылку на файл с именем «pathname», и пользователь может выбрать имя файла ссылки.

1 Ответ

0 голосов
/ 06 декабря 2018

В большинстве современных версий 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_, доступны в моем репозитории SOQ (Вопросы о переполнении стека) на GitHub в виде файлов 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.

...