Linux программирование в примерах - Роббинс Арнольд
Шрифт:
Интервал:
Закладка:
$ <b>ch12-mktemp</b> /* Запуск программы */
Filename is /tmp/myfileQES4WA /* Вывод имени файла */
$ <b>cat /tmp/myfileQES4WA</b>
Here's lookin' at you, kid' /* Содержит то, что ожидалось */
$ <b>ls -l /tmp/myfileQES4WA</b> /* To же с владельцем и доступом */
-rw------- 1 arnold devel 28 Sep 18 09:27 /tmp/myfileQES4WA
$ <b>rm /tmp/myfileQES4WA</b> /* Удалить его */
$ <b>ch12-mktemp</b> / * Используется ли повторно то же имя? */
Filename is /tmp/myfileic7xCy /* Нет. Это хорошо */
$ <b>cat /tmp/myfileic7xCy</b> /* Снова проверить содержание */
Here's lookin' at you, kid!
$ <b>ls -l /tmp/myfileic7xCy</b> /* Снова проверить владельца и доступ */
-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 генерируют во время компоновки сообщение:
$ <b>cc ch12-mktemp.c -о ch12-mktemp</b> /* Компилировать программу */
/tmp/cc1XCvD9.о(.text+0x35): In function 'main':
: the use of 'mktemp' is dangerous, better use 'mkstemp'
(Мы рассмотрим mkstemp() в следующем подразделе.)
Если бы в вашей системе не было mkstemp(), подумайте, как вы могли бы использовать эти интерфейсы для ее эмулирования. (См. также «Упражнения» для главы 12 в конце.)
12.3.2. Создание и открывание временных файлов (хорошо)
Есть две функции, не имеющие проблем состояния гонки. Одна из них предназначена для использования с библиотекой <stdio.h>:
#include <stdio.h> /* ISO С */
FILE *tmpfile(void);
Другая функция для использования с системными вызовами на основе дескрипторов файлов:
#include <stdio.h> /* XSI */
int mkstemp(char* template);
tmpfile() возвращает значение FILE*, представляющее уникальный открытый временный файл. Файл открывается в режиме "w+b". w+ означает «открыть для чтения и записи, сначала урезав файл», a b означает двоичный, а не текстовый режим. (На системах GNU/Linux или Unix нет разницы, но на других системах есть.) Файл автоматически удаляется, когда закрывается указатель файла; нет способа получить имя файла, чтобы сохранить его содержимое. Программа в ch12-tmpfile.c демонстрирует tmpfile():
/* ch12-tmpfile.с --- демонстрация tmpfile().
Проверка ошибок для краткости опущена */
#include <stdio.h>
int main(void) {
static char mesg[] =