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);
88 buf_size *= 2;
89 if (SSIZE_MAX < buf_size || (SIZE_MAX / 2 < SSIZE_MAX && buf_size == 0))
90 xalloc_die();
91 }
92 }
Тело функции состоит из бесконечного цикла (строки 68–91), разрываемого в строке 84, которая возвращает выделенный буфер. Цикл начинается выделением первоначального буфера (строка 70) и чтения ссылки (строка 71). Строки 73–79 обрабатывают случай ошибки, сохраняя и восстанавливая errno таким образом, что она может корректно использоваться вызывающим кодом.
Строки 81–85 обрабатывают случай «успеха», при котором размер содержимого ссылки меньше размера буфера. В этом случае добавляется завершающий ноль (строка 83), а затем буфер возвращается, прерывая бесконечный цикл. Это гарантирует, что в буфер помещено все содержимое ссылки, поскольку у readlink()
нет возможности сообщить о «недостаточном размере буфера».
Строки 87–88 освобождают буфер и удваивают размер буфера для следующей попытки в начале цикла. Строки 89–90 обрабатывают случай, при котором размер ссылки слишком велик: buf_size
больше, чем SSIZE_MAX
, или SSIZE_MAX
больше, чем значение, которое может быть представлено в знаковом целом того же размера, который использовался для хранения SIZE_MAX
, и buf_size
обернулся в ноль. (Это маловероятные условия, но странные вещи все же случаются.) Если одно из этих условий верно, программа завершается с сообщением об ошибке. В противном случае функция возвращается в начало цикла, чтобы сделать еще одну попытку выделить буфер и прочесть ссылку.
Некоторое дополнительное разъяснение: условие 'SIZE_MAX / 2 < SSIZE_MAX
' верно лишь на системах, в которых 'SIZE_MAX < 2 * SSIZE_MAX
'; мы не знаем таких, но лишь на таких системах buf_size
может обернуться в ноль. Поскольку на практике это условие не может быть истинным, компилятор может оптимизировать все выражение, включив следующую проверку 'buf_size == 0
'. После прочтения этого кода вы можете спросить: «Почему не использовать lstat()
для получения размера символической ссылки, не выделить буфер нужного размера с помощью malloc()
, и все?» На это есть несколько причин.[61]
• lstat()
является системным вызовом — лучше избежать накладных расходов по его вызову, поскольку содержимое большинства символических ссылок поместится в первоначальный размер буфера в 128.
• Вызов lstat()
создает условие состязания: ссылка может измениться между исполнением lstat()
и readlink()
, в любом случае вынуждая повторение.
• Некоторые системы не заполняют должным образом член st_size
для символической ссылки. (Печально, но верно.) Сходным образом, как мы увидим в разделе 8.4.2 «Получение текущего каталога: getcwd()
», Linux в /proc
предоставляет специальные символические ссылки, у которых st_size
равен нулю, но для которых readlink()
возвращает действительное содержимое.
Наконец, буфер не слишком большой, xreadlink()
использует free()
и malloc()
с большим размером вместо realloc()
, чтобы избежать бесполезного копирования, которое делает realloc()
. (Поэтому комментарий в строке 58 устарел, поскольку realloc()
не используется; это исправлено в версии Coreutils после 5.0.)
5.5. Смена владельца, прав доступа и времени изменения
Несколько других системных вызовов дают вам возможность изменять другие относящиеся к файлу сведения: в частности, владельца и группу файла, права доступа к файлу и времена доступа и изменения файла.
5.5.1. Смена владельца файла: chown()
, fchown()
и lchown()
Владелец и группа файла изменяются с помощью трех сходных системных вызовов.
#include
#include
int chown(const char *path, uid_t owner, gid_t group);