Linux программирование в примерах - Арнольд Роббинс
Шрифт:
Интервал:
Закладка:
5.5.3. Изменение временных отметок: utime()
Структура struct stat содержит три поля типа time_t:
st_atime Время последнего доступа к файлу (чтение)
st_mtime Время последнего изменения файла (запись).
st_ctime Время последнего изменения индекса файла (например, переименования)
Значение time_t представляет время в «секундах с начала эпохи». Эпоха является Началом Времени для компьютерных систем GNU/Linux и Unix используют в качестве начала Эпохи полночь 1 января 1970 г по универсальному скоординированному времени (UTC).[62] Системы Microsoft Windows используют в качестве начала Эпохи полночь 1 января 1980 г. (очевидно, местное время).
Значения time_t иногда называют временными отметками (timestamps). В разделе 6.1 «Время и даты» мы рассмотрим, как получаются эти данные и как они используются. Пока достаточно знать, чем является значение time_t и то, что оно представляет секунды с начала Эпохи.
Системный вызов utime() позволяет изменять отметки времени доступа к файлу и его изменения:
#include <sys/types.h> /* POSIX */
#include <utime.h>
int utime(const char *filename, struct utimbuf *buf);
Структура utimbuf выглядит следующим образом:
struct utimbuf {
time_t actime; /* время доступа */
time_t modtime; /* время изменения */
};
При успешном вызове возвращается 0, в противном случае возвращается -1. Если buf равен NULL, система устанавливает время доступа и время изменения равным текущему времени.
Чтобы изменить только одну временную отметку, используйте оригинальное значение из struct stat. Например.
/* Для краткости проверка ошибок опущена */
struct stat sbuf;
struct utimbuf ut;
time_t now;
time(&now); /* Получить текущее время дня, см. след. главу */
stat("/some/file", &sbuf); /* Заполнить sbuf */
ut.actime = sbuf.st_atime; /* Время доступа без изменений */
ut.modtime = now - (24 * 60 * 60);
/* Установить modtime на 24 часа позже */
utime("/some/file", &ut); /* Установить значения */
Вы можете спросить себя: «Почему может понадобиться кому-нибудь изменять времена доступа и изменения файла?» Хороший вопрос.
Чтобы на него ответить, рассмотрите случай программы, создающей дублирующие архивы, такой, как tar или cpio. Эти программы должны прочесть содержание файла, чтобы заархивировать его. Чтение файла, конечно, изменяет время доступа к файлу.
Однако, этот файл, возможно, не читался человеком в течение 10 лет. Некто, набрав 'ls -lu', что отображает время доступа (вместо времени изменения по умолчанию), увидел бы, что последний раз данный файл просматривали 10 лет назад. Поэтому программа архивации должна сохранить оригинальные значения времени доступа и изменения, прочесть файл для архивации, а затем восстановить первоначальное время с помощью utime().
Аналогичным образом, рассмотрите случай архивирующей программы, восстанавливающей файл из архива. В архиве хранятся первоначальные значения времени доступа и изменения. Однако, когда файл извлечен из архива во вновь созданную копию на диске, новый файл имеет текущие дату и время для значений времени доступа и изменения.
Однако полезнее, когда вновь созданный файл выглядит, как если бы он имел тот же возраст, что и оригинальный файл в архиве. Поэтому архиватор должен иметь возможность устанавливать значения времени доступа и изменения в соответствии со значениями в архиве.
ЗАМЕЧАНИЕ. В новом коде вы можете захотеть использовать вызов utimes() (обратите внимание на s в имени), который описан далее в книге, в разделе 14.3.2 «Файловое время в микросекундах: utimes()»
5.5.3.1. Подделка utime(file, NULL)
Некоторые более старые системы не устанавливают значения времени доступа и изменения равным текущему времени, когда второй аргумент utime() равен NULL. Однако код более высокого уровня (такой, как GNU touch) проще, если он может полагаться на один стандартизованный интерфейс.
Поэтому библиотека GNU Coreutils содержит замещающую функцию для utime(), которая обрабатывает этот случай, которую потом может вызвать код более высокого уровня. Это отражает принцип проектирования «выбор лучшего интерфейса для работы», который мы описали в разделе 1.5 «Возвращаясь к переносимости».
Замещающая функция находится в файле lib/utime.c в дистрибутиве Coreutils Следующий код является версией из Coreutils 5.0. Номера строк относятся к началу файла:
24 #include <sys/types.h>
25
26 #ifdef HAVE_UTIME_H
27 # include <utime.h>
28 #endif
39
30 #include "full-write.h"
31 #include "safe-read.h"
32
33 /* Некоторые системы (даже имеющие <utime.h>) нигде не объявляют
34 эту структуру. */
35 #ifndef HAVE_STRUCT_UTIMBUF
36 struct utimbuf
37 {
38 long actime;
39 long modtime;
40 };
41 #endif
42
43 /* Эмулировать utime(file, NULL) для систем (подобных 4.3BSD),
44 которые не устанавливают в этом случае текущее время для времени
45 доступа и изменения file. Вернуть 0, если успешно, -1 если нет. */
46
47 static int
48 utime_null(const char *file)
49 {
50 #if HAVE_UTIMES_NULL
51 return utimes(file, 0);
52 #else
53 int fd;
54 char c;
55 int status = 0;
56 struct stat sb;
57
58 fd = open(file, O_RDWR);
59 if (fd < 0
60 || fstat(fd, &sb) < 0
61 || safe_read(fd, &c, sizeof c) == SAFE_READ_ERROR
62 || lseek(fd, (off_t)0, SEEK_SET) < 0
63 || full_write(fd, &c, sizeof c) != sizeof с
64 /* Можно сделать - это необходимо на SunOS4.1.3 с некоторой комбинацией
65 заплат, но та система не использует этот код: у нее есть utimes.
66 || fsync(fd) < 0
67 */
68 || (st.st_size == 0 && ftruncate(fd, st.st_size) < 0)
69 || close(fd) < 0)
70 status = -1;
71 return status;
72 #endif
73 }
74
75 int
76 rpl_utime(const char *file, const struct utimbuf *times)
77 {
78 if (times)
79 return utime(file, times);
80
81 return utime_null(file);
82 }
Строки 33–41 определяют структуру struct utimbuf; как сказано в комментарии, некоторые системы не объявляют эту структуру. Работу осуществляет функция utime_null(). Используется системный вызов utimes(), если он доступен (utimes() является сходным, но более развитым системным вызовом, который рассматривается в разделе 14.3.2 «Файловое время в микросекундах: utimes().» Он допускает также в качестве второго аргумента NULL, что означает использование текущего времени.)
В случае, когда время должно обновляться вручную, код осуществляет обновление, прочитав сначала из файла байт, а затем записав его обратно. (Первоначальный touch Unix работал таким способом.) Операции следующие:
1. Открыть файл, строка 58.
2. Вызвать для файла stat(), строка 60.
3. Прочесть один байт, строка 61 Для наших целей safe_read() действует подобно read(); это объясняется в разделе 10.4.4 «Повторно запускаемые системные вызовы»).
4. Переместиться обратно на начало файла с помощью lseek(), строка 62. Это сделано для записи только что прочитанного байта обратно поверх себя.
5. Записать байт обратно, строка 63. full_write() действует подобно write(); это также рассматривается в разделе 10.4.4 «Повторно запускаемые системные вызовы»).
6. Если файл имеет нулевой размер, использовать ftruncate() для установки его размера в ноль (строка 68). Это не изменяет файл, но имеет побочный эффект обновления времени доступа и изменения (ftruncate() была описана в разделе 4 8 «Установка длины файла».)
7. Закрыть файл, строка 69.
Все эти шаги осуществляются в одной длинной последовательной цепи проверок внутри if. Проверки сделаны так, что если любое сравнение неверно, utime_null() возвращает -1, как обычный системный вызов, errno автоматически устанавливается системой для использования кодом более высокого уровня.
Функция rpl_utime() (строки 75–82) является «заместителем utime()». Если второй аргумент не равен NULL, она вызывает настоящую utime(). В противном случае она вызывает utime_null().
5.5.4. Использование fchown() и fchmod() для обеспечения безопасности