Linux программирование в примерах - Роббинс Арнольд
Шрифт:
Интервал:
Закладка:
ЗАМЕЧАНИЕ. В новом коде вы можете захотеть использовать вызов 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 автоматически устанавливается системой для использования кодом более высокого уровня.