Тело функции состоит из бесконечного цикла (строки 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
для получения размера символической ссылки, не выделить буфер нужного размера с помощью
Спасибо Джиму Мейерингу (Jim Meyering) за объяснение проблем — Примеч. автора.
•
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 <sys/types.h> /* POSIX */
#include <unistd.h>
int chown(const char *path, uid_t owner, gid_t group);
int fchown(int fd, uid_t owner, gid_t group);
int lchown(const char *path, uid_t owner, gid_t group);
chown
работает с аргументом имени файла,
fchown
работает с открытым файлом, а
lchown
работает с символической ссылкой вместо файла, на который эта ссылка указывает. Во всех других отношениях эти три вызова работают идентично, возвращая 0 в случае успеха и -1 при ошибке.
Стоит заметить, что один системный вызов изменяет как владельца, так и группу файла. Чтобы изменить лишь владельца или лишь группу, передайте (-1) в качестве того идентификационного номера, который должен остаться без изменений.