Роббинс Арнольд
Шрифт:
Открывание ссылки с помощью
open
для чтения ее с использованием read
не будет работать, open
следует по ссылке на указываемый файл. Таким образом, символические ссылки сделали необходимым дополнительный системный вызов, который называется readlink
: #include <unistd.h> /* POSIX */
int readlink(const char *path, char *buf, size_t bufsiz);
readlink
помещает содержимое символической ссылки, на имя которой указывает path
, в буфер, на который указывает buf
. Копируется не более bufsiz
символов. Возвращаемое значение равно числу символов, помещенных в buf
, либо -1, если возникла ошибка, readlink
не вставляет завершающий нулевой байт. Обратите внимание, что если буфер, переданный
readlink
, слишком маленький, информация будет потеряна; полное имя указываемого файла будет недоступно. Чтобы использовать readlink
должным образом, вы должны делать следующее: 1. Используйте
lstat
, чтобы убедиться, что это символическая ссылка. 2. Убедитесь, что ваш буфер для содержимого символической ссылки составляет по крайней мере '
sbuf.st_size + 1
' байтов; '+ 1
' нужно для завершающего нулевого байта, чтобы сделать буфер годной к употреблению строкой С. 3. Вызовите
readlink
. Не мешает проверить, что возвращенное значение равно sbuf.st_size
. 4. Добавьте '
\0
' к байту после содержимого ссылки, чтобы превратить его в строку С. Код для всего этого мог бы выглядеть примерно так: /* Проверка ошибок для краткости опущена */
int count;
char linkfile[PATH_MAX], realfile[PATH_MAX]; /* PATH_MAX в <limits.h> */
strut stat sbuf;
/* ...поместить в linkfile путь к нужной символической ссылке... */
lstat(linkfile, &sbuf); /* Получить сведения от stat */
if (!S_ISLNK(sbuf.st_mode)) /* Проверить, что это ссылка */
/* не символическая ссылка, обработать это */
if (sbuf.st_size + 1 > PATH_МАХ) /* Проверить размер буфера */
/* обработать проблемы с размером буфера */
count = readlink(linkfile, realfile, PATH_MAX);
/* Прочесть ссылку */
if (count != sbuf.st_size)
/* происходит что-то странное, обработать это */
realfile(count) = '\0'; /* Составить строку С */
Данный пример для простоты представления использует буферы фиксированного размера. Реальный код мог бы использовать для выделения буфера нужного размера
malloc
, поскольку массивы фиксированного размера могли бы оказаться слишком маленькими. Файл lib/xreadlink.c
в GNU Coreutils делает именно это. Он читает содержимое символической ссылки в память, выделенную malloc
. Мы покажем здесь саму функцию, большая часть файла представляет собой стереотипные определения. Номера строк относятся к началу файла: 55 /* Вызвать readlink для получения значения ссылки FILENAME.
56 Вернуть указатель на завершенную NUL строку в выделенной malloc памяти.
57 При ошибке readlink вернуть NULL (использовать errno для диагноза).
58 При ошибке realloc или если значение ссылки больше SIZE_MAX,
59 выдать диагностику и выйти. */
60
61 char*
62 xreadlink(char const* filename)
63 {
64 /* Начальный размер буфера для ссылки. Степень 2 обнаруживает
65 арифметическое переполнение раньше, но не рекомендуется. */
66 size_t buf_size = 128;
67
68 while(1)
69 {
70 char *buffer = xmalloc(buf_size);
71 ssize_t link_length = readlink(filename, buffer, buf_size);
72
73 if (link_length < 0)
74 {
75 int saved_errno = errno;
76 free(buffer);
77 errno = saved_errno;
78 return NULL;
79 }
80
81 if ((size_t)link_length < buf_size)
82 {
83 buffer[link_length] = 0;
84 return buffer;
85 }
86
87 free(buffer);