utimensat(2)
๐ utimensat(2) โ the System Calls Manual page, but make it cute ๐
okay bestie, so this is the page for utimensat(2) and futimens from the System Calls Manual. it's the one about changing file timestamps with โจnanosecond precisionโจ and honestly? she's giving precision. she's giving "i care about your atime AND your mtime down to the literal nanosecond". periodt.
๐ธ NAME ๐ธ
utimensat, futimens โ change file timestamps with nanosecond precision
๐ LIBRARY ๐
Standard C library (libc, -lc)
โจ SYNOPSIS โจ
#include <fcntl.h> /* Definition of AT_* constants */
#include <sys/stat.h>
int utimensat(int dirfd, const char *path,
const struct timespec times[_Nullable 2], int flags);
int futimens(int fd, const struct timespec times[_Nullable 2]);
๐ Feature Test Macro Requirements for glibc (see feature_test_macros(7)): ๐
utimensat():
Since glibc 2.10:
_POSIX_C_SOURCE >= 200809L
Before glibc 2.10:
_ATFILE_SOURCE
futimens():
Since glibc 2.10:
_POSIX_C_SOURCE >= 200809L
Before glibc 2.10:
_GNU_SOURCE
like, yes, you do have to define the right macro before glibc 2.10, iykyk ๐ญ
๐ DESCRIPTION ๐
utimensat() and futimens() update the timestamps of a file with nanosecond precision. and listen, this is a glow-up compared to the historical utime(2) and utimes(2), which only do second and microsecond precision respectively when setting file timestamps. we've upgraded, bestie. we're not living like that anymore.
with utimensat() you point at the file using the pathname in path. with futimens() you point at the file using an open file descriptor, fd. both valid, both iconic, pick your fighter.
for both calls, the new file timestamps live in the array times:
times[0]= the new "last access time" (atime)times[1]= the new "last modification time" (mtime)
each element of times gives a time as the number of seconds and nanoseconds since the Epoch, 1970-01-01 00:00:00 +0000 (UTC). this info travels in a timespec(3) structure. slay.
updated file timestamps get set to the greatest value the filesystem supports that is not greater than the specified time. no overshooting, we stay in budget ๐
now here's the fun part โ the special values:
- if the
tv_nsecfield of one of thetimespecstructures has the special valueUTIME_NOW, then that corresponding file timestamp is set to the current time. - if the
tv_nsecfield of one of thetimespecstructures has the special valueUTIME_OMIT, then that corresponding file timestamp is left unchanged.
and in both of these cases, the value of the corresponding tv_sec field is ignored. bye felicia.
if times is NULL, then both timestamps get set to the current time.
and the status change time (ctime) will be set to the current time, even if the other time stamps don't actually change. she's committed. she showed up anyway. respect.
๐ Permissions requirements ๐
to set both file timestamps to the current time (i.e., times is NULL, or both tv_nsec fields specify UTIME_NOW), you need either:
- the caller must have write access to the file;
- the caller's effective user ID must match the owner of the file; or
- the caller must have appropriate privileges.
to make any change other than setting both timestamps to the current time (i.e., times is not NULL, and neither tv_nsec field is UTIME_NOW and neither tv_nsec field is UTIME_OMIT), then condition 2 or 3 above must apply. no shortcuts bestie.
if both tv_nsec fields are specified as UTIME_OMIT, then no file ownership or permission checks are performed, and the file timestamps are not modified โ but other error conditions may still be detected. sneaky. watch out. ๐
๐ฆ utimensat() specifics ๐ฆ
if path is relative, then by default it's interpreted relative to the directory referred to by the open file descriptor, dirfd โ rather than relative to the current working directory of the calling process, which is what utimes(2) does for a relative pathname. see openat(2) for an explanation of why this can be useful.
if path is relative and dirfd is the special value AT_FDCWD, then path is interpreted relative to the current working directory of the calling process (like utimes(2)).
if path is absolute, then dirfd is ignored. she said "i don't need you".
the flags argument is a bit mask created by ORing together zero or more of these values defined in <fcntl.h>:
AT_EMPTY_PATH(since Linux 5.8) โ ifpathis an empty string, operate on the file referred to bydirfd(which may have been obtained using theopen(2)O_PATHflag). in this case,dirfdcan refer to any type of file, not just a directory. ifdirfdisAT_FDCWD, the call operates on the current working directory. this flag is Linux-specific; define_GNU_SOURCEto obtain its definition.AT_SYMLINK_NOFOLLOWโ ifpathspecifies a symbolic link, then update the timestamps of the link itself, rather than the file to which it refers.
๐ซถ RETURN VALUE ๐ซถ
on success, utimensat() and futimens() return 0. on error, -1 is returned and errno is set to indicate the error. okayyy ๐ญ
๐ ERRORS ๐
EACCESโtimesisNULL, or bothtv_nsecvalues areUTIME_NOW, and the effective user ID of the caller does not match the owner of the file, the caller does not have write access to the file, and the caller is not privileged (Linux: does not have either theCAP_FOWNERor theCAP_DAC_OVERRIDEcapability).EBADFโ (futimens())fdis not a valid file descriptor.EBADFโ (utimensat())pathis relative butdirfdis neitherAT_FDCWDnor a valid file descriptor.EFAULTโtimespointed to an invalid address; or,dirfdwasAT_FDCWD, andpathisNULLor an invalid address.EINVALโ invalid value inflags.EINVALโ invalid value in one of thetv_nsecfields (value outside range[0, 999,999,999], and notUTIME_NOWorUTIME_OMIT); or an invalid value in one of thetv_secfields.EINVALโpathisNULL,dirfdis notAT_FDCWD, andflagscontainsAT_SYMLINK_NOFOLLOW.ELOOPโ (utimensat()) too many symbolic links were encountered in resolvingpath.ENAMETOOLONGโ (utimensat())pathis too long.ENOENTโ (utimensat()) a component ofpathdoes not refer to an existing directory or file, orpathis an empty string.ENOTDIRโ (utimensat())pathis relative, butdirfdis neitherAT_FDCWDnor a file descriptor referring to a directory; or, one of the prefix components ofpathis not a directory.EPERMโ the caller attempted to change one or both timestamps to a value other than the current time, or to change one of the timestamps to the current time while leaving the other timestamp unchanged, (i.e.,timesis notNULL, neithertv_nsecfield isUTIME_NOW, and neithertv_nsecfield isUTIME_OMIT) and either:- the caller's effective user ID does not match the owner of file, and the caller is not privileged (Linux: does not have the
CAP_FOWNERcapability); or, - the file is marked append-only or immutable (see
chattr(1)).
- the caller's effective user ID does not match the owner of file, and the caller is not privileged (Linux: does not have the
EROFSโ the file is on a read-only filesystem.ESRCHโ (utimensat()) search permission is denied for one of the prefix components ofpath.
๐ ATTRIBUTES ๐
for an explanation of the terms used in this section, see attributes(7).
- Interface:
utimensat(),futimens() - Attribute: Thread safety
- Value:
MT-Safe
she's MT-Safe. she can handle multiple threads at once. main character energy fr fr ๐ฅ
๐ VERSIONS ๐
๐ง C library/kernel ABI differences ๐ง
on Linux, futimens() is a library function implemented on top of the utimensat() system call. to support this, the Linux utimensat() system call implements a nonstandard feature: if path is NULL, then the call modifies the timestamps of the file referred to by the file descriptor dirfd (which may refer to any type of file). using this feature, the call futimens(fd, times) is implemented as:
utimensat(fd, NULL, times, 0);
note, however, that the glibc wrapper for utimensat() disallows passing NULL as the value for path: the wrapper function returns the error EINVAL in this case. so like, the kernel is fine with it, the wrapper said "no โค๏ธ". watch out for that.
โ ๏ธ bestie, don't try to lean on that NULL-pathtrick through glibc'sutimensat()wrapper โ she will hand you anEINVALand walk away.
๐ STANDARDS ๐
POSIX.1-2024.
๐ฐ๏ธ HISTORY ๐ฐ๏ธ
utimensat()โ Linux 2.6.22, glibc 2.6. POSIX.1-2008.futimens()โ glibc 2.6. POSIX.1-2008.
๐ NOTES ๐
utimensat() obsoletes futimesat(2). she's the newer model, the upgrade, the reboot. it's giving evolution.
on Linux, timestamps cannot be changed for a file marked immutable, and the only change permitted for files marked append-only is to set the timestamps to the current time. (this is consistent with the historical behavior of utime(2) and utimes(2) on Linux.)
if both tv_nsec fields are specified as UTIME_OMIT, then the Linux implementation of utimensat() succeeds even if the file referred to by dirfd and path does not exist. yes really. no file? no problem. nothing to change! ๐
๐ BUGS ๐
several bugs afflict utimensat() and futimens() before Linux 2.6.26. these bugs are either nonconformances with the POSIX.1 draft specification or inconsistencies with historical Linux behavior. ancient drama. we're talking about it anyway because history matters, bestie.
- POSIX.1 specifies that if one of the
tv_nsecfields has the valueUTIME_NOWorUTIME_OMIT, then the value of the correspondingtv_secfield should be ignored. instead, the value of thetv_secfield is required to be 0 (or the errorEINVALresults). - various bugs mean that for the purposes of permission checking, the case where both
tv_nsecfields are set toUTIME_NOWisn't always treated the same as specifyingtimesasNULL, and the case where onetv_nsecvalue isUTIME_NOWand the other isUTIME_OMITisn't treated the same as specifyingtimesas a pointer to an array of structures containing arbitrary time values. as a result, in some cases:- file timestamps can be updated by a process that shouldn't have permission to perform updates;
- file timestamps can't be updated by a process that should have permission to perform updates; and
- the wrong
errnovalue is returned in case of an error.
- POSIX.1 says that a process that has write access to the file can make a call with
timesasNULL, or withtimespointing to an array of structures in which bothtv_nsecfields areUTIME_NOW, in order to update both timestamps to the current time. however,futimens()instead checks whether the access mode of the file descriptor allows writing.
๐ SEE ALSO ๐
chattr(1), touch(1), futimesat(2), openat(2), stat(2), utimes(2), futimes(3), timespec(3), inode(7), path_resolution(7), symlink(7)
okay that's the whole page bestie, you're now officially fluent in nanosecond-precision timestamp manipulation ๐
and a lil sign-off for the credits moment: this beauty came from Linux man-pages 6.20, dated 2025-10-29, and her name is utimensat(2). go set some timestamps. slay. ๐โจ๐ฆ
๐ฆ where this page comes from
- arch rolling: man-pages 6.20-1 ยท BSD-2-Clause AND BSD-3-Clause AND BSD-4.3TAHOE AND BSD-4-Clause-UC AND GPL-1.0-or-later AND GPL-2.0-only AND GPL-2.0-or-later AND GPL-3.0-or-later AND LGPL-3.0-linking-exception AND LGPL-3.0-or-later AND LicenseRef-Public-Domain AND LicenseRef-UltraPermissive AND Linux-man-pages-1-para AND Linux-man-pages-copyleft AND Linux-man-pages-copyleft-2-para AND Linux-man-pages-copyleft-var AND MIT AND Spencer-94
- nixpkgs unstable: man-pages 6.20
- ๐ the page's license: Linux-man-pages-copyleft
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms