Читаем Linux программирование в примерах полностью

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

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

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

C++ Primer Plus
C++ Primer Plus

C++ Primer Plus is a carefully crafted, complete tutorial on one of the most significant and widely used programming languages today. An accessible and easy-to-use self-study guide, this book is appropriate for both serious students of programming as well as developers already proficient in other languages.The sixth edition of C++ Primer Plus has been updated and expanded to cover the latest developments in C++, including a detailed look at the new C++11 standard.Author and educator Stephen Prata has created an introduction to C++ that is instructive, clear, and insightful. Fundamental programming concepts are explained along with details of the C++ language. Many short, practical examples illustrate just one or two concepts at a time, encouraging readers to master new topics by immediately putting them to use.Review questions and programming exercises at the end of each chapter help readers zero in on the most critical information and digest the most difficult concepts.In C++ Primer Plus, you'll find depth, breadth, and a variety of teaching techniques and tools to enhance your learning:• A new detailed chapter on the changes and additional capabilities introduced in the C++11 standard• Complete, integrated discussion of both basic C language and additional C++ features• Clear guidance about when and why to use a feature• Hands-on learning with concise and simple examples that develop your understanding a concept or two at a time• Hundreds of practical sample programs• Review questions and programming exercises at the end of each chapter to test your understanding• Coverage of generic C++ gives you the greatest possible flexibility• Teaches the ISO standard, including discussions of templates, the Standard Template Library, the string class, exceptions, RTTI, and namespaces

Стивен Прата

Программирование, программы, базы данных