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.)

<p>5.5. Смена владельца, прав доступа и времени изменения</p>

Несколько других системных вызовов дают вам возможность изменять другие относящиеся к файлу сведения: в частности, владельца и группу файла, права доступа к файлу и времена доступа и изменения файла.

<p>5.5.1. Смена владельца файла: <code>chown()</code>, <code>fchown()</code> и <code>lchown()</code></p>

Владелец и группа файла изменяются с помощью трех сходных системных вызовов.

#include /* POSIX */

#include

int chown(const char *path, uid_t owner, gid_t group);

Перейти на страницу:

Похожие книги