Linux программирование в примерах
ЗАМЕЧАНИЕ. В новом коде вы можете захотеть использовать вызов
(обратите внимание на s в имени), который описан далее в книге, в разделе 14.3.2 «Файловое время в микросекундах:utimes()»utimes()5.5.3.1. Подделка
utime(file, NULL)Некоторые более старые системы не устанавливают значения времени доступа и изменения равным текущему времени, когда второй аргумент
равенutime(). Однако код более высокого уровня (такой, как GNUNULL) проще, если он может полагаться на один стандартизованный интерфейс.touchПоэтому библиотека GNU Coreutils содержит замещающую функцию для
, которая обрабатывает этот случай, которую потом может вызвать код более высокого уровня. Это отражает принцип проектирования «выбор лучшего интерфейса для работы», который мы описали в разделе 1.5 «Возвращаясь к переносимости».utime()Замещающая функция находится в файле
в дистрибутиве Coreutils Следующий код является версией из Coreutils 5.0. Номера строк относятся к началу файла:lib/utime.c24 #include <sys/types.h>2526 #ifdef HAVE_UTIME_H27 # include <utime.h>28 #endif3930 #include "full-write.h"31 #include "safe-read.h"3233 /* Некоторые системы (даже имеющие <utime.h>) нигде не объявляют34 эту структуру. */35 #ifndef HAVE_STRUCT_UTIMBUF36 struct utimbuf37 {38 long actime;39 long modtime;40 };41 #endif4243 /* Эмулировать utime(file, NULL) для систем (подобных 4.3BSD),44 которые не устанавливают в этом случае текущее время для времени45 доступа и изменения file. Вернуть 0, если успешно, -1 если нет. */4647 static int48 utime_null(const char *file)49 {50 #if HAVE_UTIMES_NULL51 return utimes(file, 0);52 #else53 int fd;54 char c;55 int status = 0;56 struct stat sb;5758 fd = open(file, O_RDWR);59 if (fd < 060 || fstat(fd, &sb) < 061 || safe_read(fd, &c, sizeof c) == SAFE_READ_ERROR62 || lseek(fd, (off_t)0, SEEK_SET) < 063 || full_write(fd, &c, sizeof c) != sizeof с64 /* Можно сделать - это необходимо на SunOS4.1.3 с некоторой комбинацией65 заплат, но та система не использует этот код: у нее есть utimes.66 || fsync(fd) < 067 */68 || (st.st_size == 0 && ftruncate(fd, st.st_size) < 0)69 || close(fd) < 0)70 status = -1;71 return status;72 #endif73 }7475 int76 rpl_utime(const char *file, const struct utimbuf *times)77 {78 if (times)79 return utime(file, times);8081 return utime_null(file);82 }Строки 33–41 определяют структуру
; как сказано в комментарии, некоторые системы не объявляют эту структуру. Работу осуществляет функцияstruct utimbuf. Используется системный вызовutime_null(), если он доступен (utimes()является сходным, но более развитым системным вызовом, который рассматривается в разделе 14.3.2 «Файловое время в микросекундах:utimes().» Он допускает также в качестве второго аргументаutimes(), что означает использование текущего времени.)NULLВ случае, когда время должно обновляться вручную, код осуществляет обновление, прочитав сначала из файла байт, а затем записав его обратно. (Первоначальный touch Unix работал таким способом.) Операции следующие:
1. Открыть файл, строка 58.
2. Вызвать для файла
, строка 60.stat()3. Прочесть один байт, строка 61 Для наших целей
действует подобноsafe_read(); это объясняется в разделе 10.4.4 «Повторно запускаемые системные вызовы»).read()4. Переместиться обратно на начало файла с помощью
, строка 62. Это сделано для записи только что прочитанного байта обратно поверх себя.lseek()5. Записать байт обратно, строка 63.
действует подобноfull_write(); это также рассматривается в разделе 10.4.4 «Повторно запускаемые системные вызовы»).write()6. Если файл имеет нулевой размер, использовать
для установки его размера в ноль (строка 68). Это не изменяет файл, но имеет побочный эффект обновления времени доступа и изменения (ftruncate()была описана в разделе 4 8 «Установка длины файла».)ftruncate()7. Закрыть файл, строка 69.
Все эти шаги осуществляются в одной длинной последовательной цепи проверок внутри
. Проверки сделаны так, что если любое сравнение неверно,ifвозвращает -1, как обычный системный вызов,utime_null()автоматически устанавливается системой для использования кодом более высокого уровня.errno