Вспомните, что компоненты каталогов (имена файлов) — это ни что иное, как указатели на дисковые информационные узлы (on-disk inodes); почти вся важная информация, касающаяся файла, хранится в его inode. Вызов open()
позволяет процессу создавать компоненты каталогов, которые являются обычными файлами, но для создания файлов других типов и для манипулирования компонентами каталогов могут понадобиться другие функции. Функции, которые позволяют создавать, удалять и выполнять поиск каталогов, описаны в главе 14; файлы сокетов — в главе 17. В настоящем разделе раскрываются символические ссылки, файлы устройств и FIFO.
11.4.1. Создание входных точек устройств и именованных каналов
Процессы создают файлы устройств и именованных каналов в файловой системе с помощью вызова mknod()
.
#include <fcntl.h>
#include <unistd.h>
int mknod(const char *pathname, mode_t mode, dev_t dev);
pathname
— это имя файла, который нужно создать, mode
— это и режим доступа (который модифицируется текущим umask
), и тип нового файла (S_IFIFO
, S_IFBLK
, S_IFCHR
). Последний параметр, dev
, содержит старший (major) и младший (minor) номера создаваемого устройства. Тип устройства (символьное или блочное) и старший номер устройства сообщают ядру, какой драйвер устройств отвечает за операции с этим файлом устройства. Младший номер используется внутри драйвером устройства, чтобы различать отдельные устройства среди многих, которыми он управляет. Только пользователю root разрешено создавать файлы устройств; именованные же каналы могут создавать все пользователи.
Заголовочный файл <sys/sysmacros.h>
представляет три макроса для манипулирования значениями типа dev_t
. Макрос makedev()
принимает старшие номера в первом аргументе, младшие — во втором и возвращает значение dev_t
, ожидаемое mknod()
. Макросы major()
и minor()
принимают значение типа dev_t
в качестве единственного аргумента и возвращают, соответственно, старший и младший номер устройства.
Программа mknod
, доступная в Linux, предоставляет пользовательский интерфейс к системному вызову mknod()
(подробности см. в man 1 mknod
). Ниже приведена упрощенная реализация mknod
для иллюстрации системного вызова mknod()
. Следует отметить, что программа создает файл с режимом доступа 0666 (предоставляя право на чтение и запись всем пользователям) и зависит от системной установки umask процесса для получения прав доступа.
1: /* mknod.с */
2:
3: /* Создать устройство или именованный канал, указанный в командной строке.
4: См. подробности о параметрах командной строки
5: на man-странице mknod(1). */
6:
7: #include <errno.h>
8: #include <stdio.h>
9: #include <stdlib.h>
10: #include <string.h>
11: #include <sys/stat.h>
12: #include <sys/sysmacros.h>
13: #include <unistd.h>
14:
15: void usage(void) {
16: fprintf (stderr, "использование: mknod <путь> [b | с | u | p]"
17: "<старший> <младший>\n");
18: exit(1);
19: }
20:
21: int main(int argc, const char **argv) {
22: int major = 0, minor = 0;
23: const char *path;
24: int mode = 0666;
25: char *end;
26: int args;
27:
28: /* Всегда необходимы, как минимум, тип создаваемого inode
29: и путь к нему. */
30: if (argc < 3) usage();
31:
32: path = argv[1];
33:
34: /* второй аргумент указывает тип создаваемого узла */
35: if (!strcmp(argv[2], "b")) {
36: mode | = S_IFBLK;
37: args = 5;
38: } else if (!strcmp(argv[2] , "с") || !strcmp(argv[2], "u")) {
39: mode |= S_IFCHR;
40: args = 5;
41: } else if(!strcmp(argv[2], "p")) {
42: mode |= S_IFIFO;
43: args = 3;
44: } else {
45: fprintf(stderr, "неизвестный тип узла %s\n", argv[2]);
46: return 1;
47: }
48:
49: /* args сообщает, сколько аргументов ожидается, поскольку нам нужно
50: больше информации при создания устройств, чем именованных каналов*/
51: if (argc != args) usage();
52:
53: if (args == 5) {
54: /* получить старший и младший номера файла устройств,
55: который нужно создать */
56: major = strtol(argv[3], &end, 0);
57: if (*end) {
58: fprintf(stderr,"неверный старший номер %s\n", argv[3]);
59: return 1;
60: }
61:
62: minor = strtol(argv[4], &end, 0);
63: if (*end) {
64: fprintf(stderr, "неверный младший номер %s\n", argv[4]);
65: return 1;
66: }
67: }
68:
69: /* если создается именованный канал, то финальный параметр
70: игнорируется */
71: if (mknod(path, mode, makedev(major, minor))) {
72: fprintf(stderr, "вызов mknod не удался : %s\n", strerror(errno));
73: return 1;
74: }
75:
76: return 0;
77: }
11.4.2. Создание жестких ссылок
Когда множество имен файлов в файловой системе ссылаются на единственный inode, такие файлы называют жесткими ссылками (hard links) на него. Все эти имена должны располагаться на одном физическом носителе (обычно это значит, что они должны быть на одном устройстве). Когда файл имеет множество жестких ссылок, все они равны — нет способа узнать, с каким именем первоначально был создан файл. Одно из преимуществ такой модели заключается в том, что удаление одной жесткой ссылки не удаляет файл с устройства — он остается до тех пор, пока все ссылки на него не будут удалены. Системный вызов link()
связывает новое имя файла с существующим inode.
#include <unistd.h>
int link(const char *origpath, const char *newpath);
Параметр origpath
ссылается на существующее путевое имя, a newpath
представляет собой путь для новой жесткой ссылки. Любой пользователь может создавать ссылку на файл, к которому у него есть доступ по чтению, до тех пор, пока он имеет право записи в каталоге, в котором ссылка создается, и право выполнения в каталоге, в котором находится origpath
. Только пользователь root имеет право создавать жесткие ссылки на каталоги, но поступать так — обычно плохая идея, поскольку большинство файловых систем и некоторые утилиты не работают с ними достаточно хорошо — они полностью их отвергают.