Книга: Linux программирование в примерах

12.3.1. Создание временных имен файлов (плохо)

12.3.1. Создание временных имен файлов (плохо)

Имеются три функции, назначением которых является создание имени уникального, не существующего файла. Получив такое имя, вы можете использовать его для создания временного файла. Поскольку имя уникально, вам «гарантируется» исключительное использование файла. Вот объявления функций:

#include <stdio.h>
char *tmpnam(char *s); /* ISO С */
char *tempnam(const char *dir, const char *pfx); /* XSI */
char *mktemp(char *template); /* ISO С */

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

char *tmpnam(char *s)

Генерирует уникальное имя файла. Если s не равен NULL, он должен быть размером по крайней мере L_tmpnam байтов, и в него копируется уникальное имя. Если s равен NULL, имя генерируется во внутреннем статическом буфере, который может быть переписан при последующих вызовах. Префикс каталогов в пути будет P_tmpdir. Как P_tmpdir, так и L_tmpnam определены в <stdio.h>.

char *tempnam(const char *dir, const char *pfx)

Подобно tmpnam() дает вам возможность указать префикс каталогов. Если dir равен NULL, используется P_tmpdir. Аргумент pfx, если он не равен NULL, определяет до пяти символов для использования в качестве начальных символов имени файла tempnam() выделяет память для имен файлов, которые она генерирует. Возвращенный указатель может впоследствии использоваться с free() (и это следует сделать, если хотите избежать утечек памяти).

char *mktemp(char *template)

Генерирует уникальные имена файлов на основе шаблона. Последними шестью символами template должны быть 'ХХХХХХ'; эти символы замещаются уникальным суффиксом.

ЗАМЕЧАНИЕ. Аргумент template функции mktemp() переписывается. Поэтому он не должен быть строковой константой. Многие компиляторы, предшествовавшие стандарту С, помещают строковые константы в сегмент данных вместе с обычными глобальными переменными. Хотя в исходном коде они определены как константы, их можно переписать, таким образом, нередко встречался такой код:

/* Код в старом стиле: не используйте его */
char *tfile = mktemp("/tmp/myprogXXXXXX");
/* ...использование файла... */

На современных системах такой код. вероятно, завершится неудачей, в наши дни строковые константы находятся в сегментах памяти только для чтения.

Использование этих функций довольно просто. Файл ch12-mktemp.c демонстрирует mktemp(); нетрудно изменить его для использования других функций:

1  /* ch12-mktemp.с --- демонстрирует простое использование mktemp().
2     Для краткости проверка ошибок опущена */
3
4  #include <stdio.h>
5  #include <fcntl.h> /* для флагов открытия */
6  #include <limits.h> /* для PATH_MAX */
7
8  int main(void)
9  {
10  static char template[] = "/tmp/myfileXXXXXX";
11  char fname[PATH_MAX];
12  static char mesg[] =
13   "Here's lookin' at you, kid'n"; /* вместо "hello, world" */
14  int fd;
15
16  strcpy(fname, template);
17  mktemp(fname);
18
19  /* ОКНО СОСТОЯНИЯ ГОНКИ ОТКРЫВАЕТСЯ */
20
21  printf("Filename is %sn", fname);
22
23  /* ОКНО СОСТОЯНИЯ ГОНКИ ТЯНЕТСЯ ДОСЮДА */
24
25  fd = open(fname, O_CREAT|O_RDWR|O_TRUNC, 0600);
26  write(fd, mesg, strlen(mesg));
27  close(fd);
28
29  /* unlink(fname); */
30
31  return 0;
32 }

Переменная template (строка 10) определяет шаблон имени файла; 'ХХХХХХ' будет заменен уникальным значением. Строка 16 копирует шаблон в fname, которая не является константой: ее можно изменить. Строка 18 вызывает mktemp() для генерирования имени файла, а строка 21 выводит ее, так, чтобы мы могли видеть, что это такое. (Вскоре мы объясним комментарии в строках 19 и 23.)

Строка 25 открывает файл, создавая его при необходимости. Строка 26 записывает сообщение в mesg, а строка 27 закрывает файл. В программе, в которой файл должен быть удален после завершения работы с ним, строка 29 была бы не закомментирована. (Иногда временный файл не следует удалять; например, если файл после полной записи будет переименован.) Мы закомментировали ее, чтобы можно было запустить эту программу и посмотреть на файл впоследствии. Вот что происходит при запуске программы:

$ ch12-mktemp /* Запуск программы */
Filename is /tmp/myfileQES4WA /* Вывод имени файла */
$ cat /tmp/myfileQES4WA
Here's lookin' at you, kid' /* Содержит то, что ожидалось */
$ ls -l /tmp/myfileQES4WA /* To же с владельцем и доступом */
-rw------- 1 arnold devel 28 Sep 18 09:27 /tmp/myfileQES4WA
$ rm /tmp/myfileQES4WA /* Удалить его */
$ ch12-mktemp / * Используется ли повторно то же имя? */
Filename is /tmp/myfileic7xCy /* Нет. Это хорошо */
$ cat /tmp/myfileic7xCy /* Снова проверить содержание */
Here's lookin' at you, kid!
$ ls -l /tmp/myfileic7xCy /* Снова проверить владельца и доступ */
-rw------- 1 arnold devel 28 Sep 18 09:28 /tmp/myfileic7xCy

Все кажется работающим замечательно, mktemp() возвращает уникальное имя, ch12-mktemp создает файл с нужными правами доступа, и содержание то самое, которое ожидалось. Так в чем же проблема со всеми этими функциями?

Исторически mktemp() использовала простой, предсказуемый алгоритм для создания замещающих символов для 'ХХХХХХ' в шаблоне. Более того, интервал между временем, когда генерируется имя файла, и временем, когда создается сам файл, создает состояние гонки.

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

Теперь рассмотрите программу профессора для отслеживания оценок студентов. Как профессор, так и злоумышленный студент в одно и то же время используют сильно нагруженную многопользовательскую систему. Программа профессора использует для создания временных файлов mktemp(), видевший в прошлом, как оценивающая программа создает и удаляет временные файлы, выяснил алгоритм, который использует mktemp(). (В версии GLIBC нет этой проблемы, но не все системы используют GLIBC!) Рис 12.2 иллюстрирует состояние гонки и то, как студент его использует.


Рис. 12.2. Состояние гонки с mktemp()

Вот что случилось.

1. Оценивающая программа использует mktemp() для создания имени файла. По возвращении из mktemp() открыто окно состояния гонки (строка 19 в ch12-.mktemp.c).

2. Ядро останавливает оценивающую программу, чтобы могли поработать другие программы в системе. Это происходит до вызова open().

Пока оценивающая программа остановлена, студент создает файл с тем же самым именем, которое вернула mktemp(). (Помните, выяснить алгоритм было просто.) Студент создает файл с дополнительной ссылкой, так что когда оценивающая программа удаляет файл, он все еще будет доступен для прочтения.

3. Теперь оценивающая программа открывает файл и записывает в него данные. Студент создал файл с правами доступа -rw-rw-rw-, поэтому это не представляет проблему.

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

Наш пример упрощенный; помимо простого похищения данных с оценками умный (хотя и аморальный) студент мог бы вместо этого изменить данные. Если бы профессор не проверял результаты работы своей программы дважды, никто бы ничего не узнал.

ЗАМЕЧАНИЕ. Мы не рекомендуем делать что-либо из этого! Если вы студент, не пытайтесь сделать что-либо подобное. Первое и самое главное, это неэтично. Во-вторых, вас могут выгнать из школы. В-третьих, ваши профессора, наверное, не сталь наивны, чтобы использовать mktemp() в своих программах. Этот пример лишь для иллюстрации!

По приведенным и другим причинам, все три описанные в данном разделе функции не следует никогда использовать. Они существуют в POSIX и GLIBC лишь для поддержки старых программ, которые были написаны до того, как была осознана опасность этих процедур С этой целью системы GNU/Linux генерируют во время компоновки сообщение:

$ cc ch12-mktemp.c -о ch12-mktemp /* Компилировать программу */
/tmp/cc1XCvD9.о(.text+0x35): In function 'main':
: the use of 'mktemp' is dangerous, better use 'mkstemp'

(Мы рассмотрим mkstemp() в следующем подразделе.)

Если бы в вашей системе не было mkstemp(), подумайте, как вы могли бы использовать эти интерфейсы для ее эмулирования. (См. также «Упражнения» для главы 12 в конце.)

Оглавление книги


Генерация: 1.179. Запросов К БД/Cache: 3 / 1
поделиться
Вверх Вниз