NAME

utimensat, futimens - 나노초 정밀도로 파일 타임스탬프 바꾸기

SYNOPSIS

#include <fcntl.h> /* AT_* 상수 정의 */
#include <sys/stat.h>

int utimensat(int dirfd, const char *pathname,
              const struct timespec times[2], int flags);
int futimens(int fd, const struct timespec times[2]);

glibc 기능 확인 매크로 요건 (feature_test_macros(7) 참고):

utimensat():
glibc 2.10부터:
_POSIX_C_SOURCE >= 200809L
glibc 2.10 전:
_ATFILE_SOURCE
futimens():
glibc 2.10부터:
_POSIX_C_SOURCE >= 200809L
glibc 2.10 전:
_GNU_SOURCE

DESCRIPTION

utimensat()futimens()는 나노초 정밀도로 파일의 타임스탬프를 갱신한다. 이와 대비되는 것이 과거의 utime(2)utimes(2)인데, 파일 타임스탬프를 설정할 때 각각 초 단위 정밀도와 마이크로초 정밀도만 허용한다.

utimensat()에서는 pathname으로 주는 경로명을 통해 파일을 지정한다. futimens()에서는 열려 있는 파일 디스크립터 fd를 통해 타임스탬프를 갱신할 파일을 지정한다.

두 호출 모두에서 배열 times로 새 파일 타임스탬프를 지정한다. times[0]은 새로운 "최근 접근 시간"(atime)을 나타내고 times[1]은 새로운 "최근 수정 시간"(mtime)을 나타내다. times의 각 항목은 에포크, 즉 1970-01-01 00:00:00 +0000 (UTC) 후로 지난 초와 나노초 수로 시간을 나타내다. 이 정보를 다음 형식의 구조체로 전달한다.

struct timespec {
    time_t tv_sec;        /* 초 */
    long   tv_nsec;       /* 나노초 */
};

갱신되는 파일 타임스탬프는 파일 시스템에서 지원하는 지정한 시간보다 크지 않은 가장 큰 값으로 설정된다.

timespec 구조체의 tv_nsec 필드가 특수한 값 UTIME_NOW이면 대응하는 파일 타임스탬프를 현재 시간으로 설정한다. timespec 구조체의 tv_nsec 필드가 특수한 값 UTIME_OMIT이면 대응하는 파일 타임스탬프를 바꾸지 않고 놔둔다. 두 경우 모두에서 대응하는 tv_sec 필드의 값은 무시한다.

times가 NULL이면 두 타임스탬프 모두를 현재 시간으로 설정한다.

권한 요건

두 파일 타임스탬프 모두를 현재 시간으로 설정하려면 (즉 times가 NULL이거나 두 tv_nsec 필드 모두 UTIME_NOW 지정) 다음 중 하나여야 한다.

  1. 호출자가 그 파일에 쓰기 접근권을 가지고 있어야 한다.
  2. 호출자의 실효 사용자 ID가 파일 소유자와 일치해야 한다.
  3. 호출자가 적절한 특권을 가지고 있어야 한다.

두 타임스탬프 모두를 현재 시간으로 설정하는 것 외의 어떤 변경을 하려면 (즉 times가 NULL이 아니고 tv_nsec 필드 하나라도 UTIME_NOW이나 UTIME_OMIT가 아님) 위의 조건 2번과 3번 중 하나가 해당되어야 한다.

tv_nsec 필드가 모두 UTIME_OMIT으로 지정되어 있으면 파일 소유권 검사나 권한 검사를 수행하지 않으며 파일 타임스탬프가 변경되지 않는다. 하지만 그 경우에도 다른 오류 조건들을 탐지할 수 있다.

utimensat() 한정 사항

pathname이 상대적인 경우에는 기본적으로 (utimes(2)에서 하듯 호출 프로세스의 현재 작업 디렉터리 기준이 아니라) 열린 파일 디스크립터 dirfd가 가리키는 디렉터리를 기준으로 해석한다. 이것이 유용할 수 있는 이유에 대한 설명은 openat(2)을 보라.

pathname이 상대적이고 dirfd가 특수한 값 AT_FDCWD인 경우에는 (utimes(2)처럼) 호출 프로세스의 현재 작업 디렉터리를 기준으로 pathname을 해석한다.

pathname이 절대적인 경우에는 dirfd를 무시한다.

flags 필드는 비트 마스크이다. 0일 수도 있고 <fcntl.h>에 정의된 다음 상수를 포함할 수도 있다.

AT_SYMLINK_NOFOLLOW
pathname이 심볼릭 링크를 나타내는 경우에 가리키는 파일이 아니라 그 링크의 타임스탬프를 갱신한다.

RETURN VALUE

성공 시 utimensat()futimens()는 0을 반환한다. 오류 시 -1을 반환하며 오류를 나타내도록 errno를 설정한다.

ERRORS

EACCES
times가 NULL이거나 두 tv_nsec 값이 모두 UTIME_NOW이며, 호출자의 실효 사용자 ID가 파일 소유자와 일치하지 않고 호출자가 파일에 쓰기 접근권을 가지고 있지 않으며 호출자에게 특권이 없다 (리눅스: CAP_FOWNER 역능이나 CAP_DAC_OVERRIDE 역능을 가지고 있지 않다).
EBADF
(futimens()) fd가 유효한 파일 디스크립터가 아니다.
EBADF
(utimensat()) pathname이 상대 경로명인데 dirfdAT_FDCWD도 아니고 유효한 파일 디스크립터도 아니다.
EFAULT
times가 유효하지 않은 주소를 가리킨다. 또는 dirfdAT_FDCWD였는데 pathname이 NULL이거나 유효하지 않은 주소이다.
EINVAL
flags에 유효하지 않은 값.
EINVAL
tv_nsec 필드에 유효하지 않은 값 (0에서 999,999,999까지 범위 밖의 값이고 UTIME_NOWUTIME_OMIT이 아님). 또는 한 tv_sec 필드에 유효하지 않은 값.
EINVAL
pathname이 NULL이고, dirfdAT_FDCWD가 아니고, flagsAT_SYMLINK_NOFOLLOW를 담고 있다.
ELOOP
(utimensat()) pathname을 해석하는 동안 너무 많은 심볼릭 링크를 만났다.
ENAMETOOLONG
(utimensat()) pathname이 너무 길다.
ENOENT
(utimensat()) pathname의 어느 요소가 존재하는 디렉터리나 파일을 가리키지 않거나, pathname이 빈 문자열이다.
ENOTDIR
(utimensat()) pathname이 상대 경로인데 dirfdAT_FDCWD도 아니고 디렉터리를 가리키는 파일 디스크립터도 아니다. 또는 pathname의 한 선두 요소가 디렉터리가 아니다.
EPERM

호출자가 타임스탬프들 중 하나 또는 모두를 현재 시간 아닌 값으로 바꾸려 했거나, 타임스탬프 하나를 현재 시간으로 바꾸고 나머지 타임스탬프는 그대로 두려 했으며, 다음 중 하나이다.

  • 호출자의 실효 사용자 ID가 파일 소유자와 일치하지 않으며, 호출자에게 특권이 없다 (리눅스: CAP_FOWNER 역능을 가지고 있지 않다).

  • 파일이 덧붙임 전용이나 불변으로 표시되어 있다. (chattr(1) 참고)

EROFS
파일이 읽기 전용 파일 시스템 상에 있다.
ESRCH
(utimensat()) pathname의 한 선두 요소에 대해 탐색 권한이 거부되었다.

VERSIONS

리눅스 커널 2.6.22에서 utimensat()이 추가되었다. glibc 버전 2.6에서 지원이 추가되었다.

glibc 2.6에서 futimens() 지원이 처음 등장했다.

ATTRIBUTES

이 절에서 사용하는 용어들에 대한 설명은 attributes(7)를 보라.

인터페이스 속성
utimensat(), futimens() 스레드 안전성 MT-Safe

CONFORMING TO

futimens()utimensat()은 POSIX.1-2008에 명세되어 있다.

NOTES

utimensat()futimesat(2)을 구식화한다.

리눅스에서는 불변(immutable)으로 표시된 파일의 타임스탬프를 바꿀 수 없으며 덧붙임 전용(append-only)으로 표시된 파일에 유일하게 허용되는 변경은 타임스탬프를 현재 시간으로 설정하는 것이다. (이는 리눅스에서 utime(2)utimes(2)의 역사적 동작 방식과 일치한다.)

tv_nsec 필드가 모두 UTIME_OMIT으로 지정되어 있는 경우 리눅스의 utimensat() 구현은 dirfdpathname이 가리키는 파일이 존재하지 않아도 성공한다.

C 라이브러리/커널 ABI 차이

리눅스에서 futimens()utimensat() 시스템 호출 위에서 구현한 라이브러리 함수이다. 이를 지원하기 위해 리눅스의 utimensat() 시스템 호출에서는 비표준 기능을 한 가지 구현하고 있다. pathname이 NULL이면 호출에서 (어떤 종류의 파일도 가리킬 수 있는) 파일 디스크립터 dirfd가 가리키는 파일의 타임스탬프를 수정한다. 이 기능을 이용하여 futimens(fd, times) 호출을 다음과 같이 구현한다.

utimensat(fd, NULL, times, 0);

참고로 glibc의 utimensat()에서는 pathname 값으로 NULL을 주는 것을 허용하지 않는다. 이 경우 래퍼 함수가 오류 EINVAL을 반환한다.

BUGS

2.6.26 전의 커널들에는 utimensat()futimens()에 여러 버그들이 있다. 이 버그들은 POSIX.1 초안 명세와의 불일치이거나 리눅스의 역사적 동작 방식과의 불일치이다.

SEE ALSO

chattr(1), touch(1), futimesat(2), openat(2), stat(2), utimes(2), futimes(3), inode(7), path_resolution(7), symlink(7)


2021-03-22