Skip to content

Commit

Permalink
Add nanosecond precision to File.utime on Unix (#15335)
Browse files Browse the repository at this point in the history
`Crystal::System::File.utime` uses `LibC.utimensat` when available, otherwise falls back to `utimes`.

The `utimensat` syscall is specified in POSIX.1-2008 and was introduced in the following OS releases:

- Linux 2.6.22, glibc 2.6
- macOS 10.14
- FreeBSD 10.3
- OpenBSD 5.0

To keep some backward compatibility, the x86_64-darwin bindings do not
declare it. We can however assume the syscalls to be present when
compiling for aarch64-darwin.

Co-authored-by: Kubo Takehiro <kubo@jiubao.org>
  • Loading branch information
ysbaddaden and kubo authored Jan 14, 2025
1 parent 07967e7 commit 2458e35
Show file tree
Hide file tree
Showing 42 changed files with 74 additions and 50 deletions.
17 changes: 13 additions & 4 deletions src/crystal/system/unix/file.cr
Original file line number Diff line number Diff line change
Expand Up @@ -185,10 +185,19 @@ module Crystal::System::File
end

def self.utime(atime : ::Time, mtime : ::Time, filename : String) : Nil
timevals = uninitialized LibC::Timeval[2]
timevals[0] = Crystal::System::Time.to_timeval(atime)
timevals[1] = Crystal::System::Time.to_timeval(mtime)
ret = LibC.utimes(filename, timevals)
ret =
{% if LibC.has_method?("utimensat") %}
timespecs = uninitialized LibC::Timespec[2]
timespecs[0] = Crystal::System::Time.to_timespec(atime)
timespecs[1] = Crystal::System::Time.to_timespec(mtime)
LibC.utimensat(LibC::AT_FDCWD, filename, timespecs, 0)
{% else %}
timevals = uninitialized LibC::Timeval[2]
timevals[0] = Crystal::System::Time.to_timeval(atime)
timevals[1] = Crystal::System::Time.to_timeval(mtime)
LibC.utimes(filename, timevals)
{% end %}

if ret != 0
raise ::File::Error.from_errno("Error setting time on file", file: filename)
end
Expand Down
1 change: 1 addition & 0 deletions src/lib_c/aarch64-android/c/fcntl.cr
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ lib LibC
O_RDONLY = 0o0
O_RDWR = 0o2
O_WRONLY = 0o1
AT_FDCWD = -100

fun fcntl(__fd : Int, __cmd : Int, ...) : Int
fun open(__path : Char*, __flags : Int, ...) : Int
Expand Down
1 change: 1 addition & 0 deletions src/lib_c/aarch64-android/c/sys/stat.cr
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,5 @@ lib LibC
fun mkdir(__path : Char*, __mode : ModeT) : Int
fun stat(__path : Char*, __buf : Stat*) : Int
fun umask(__mask : ModeT) : ModeT
fun utimensat(fd : Int, path : Char*, times : Timespec[2], flag : Int) : Int
end
1 change: 1 addition & 0 deletions src/lib_c/aarch64-darwin/c/fcntl.cr
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ lib LibC
O_RDONLY = 0x0000
O_RDWR = 0x0002
O_WRONLY = 0x0001
AT_FDCWD = -2

struct Flock
l_start : OffT
Expand Down
1 change: 1 addition & 0 deletions src/lib_c/aarch64-darwin/c/sys/stat.cr
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,5 @@ lib LibC
fun mknod(x0 : Char*, x1 : ModeT, x2 : DevT) : Int
fun stat = stat64(x0 : Char*, x1 : Stat*) : Int
fun umask(x0 : ModeT) : ModeT
fun utimensat(fd : Int, path : Char*, times : Timespec[2], flag : Int) : Int
end
3 changes: 1 addition & 2 deletions src/lib_c/aarch64-darwin/c/sys/time.cr
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,5 @@ lib LibC
end

fun gettimeofday(x0 : Timeval*, x1 : Void*) : Int
fun utimes(path : Char*, times : Timeval[2]) : Int
fun futimes(fd : Int, times : Timeval[2]) : Int
fun futimens(fd : Int, times : Timespec[2]) : Int
end
1 change: 1 addition & 0 deletions src/lib_c/aarch64-linux-gnu/c/fcntl.cr
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ lib LibC
O_RDONLY = 0o0
O_RDWR = 0o2
O_WRONLY = 0o1
AT_FDCWD = -100

struct Flock
l_type : Short
Expand Down
1 change: 1 addition & 0 deletions src/lib_c/aarch64-linux-gnu/c/sys/stat.cr
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,5 @@ lib LibC
fun mknod(path : Char*, mode : ModeT, dev : DevT) : Int
fun stat(file : Char*, buf : Stat*) : Int
fun umask(mask : ModeT) : ModeT
fun utimensat(fd : Int, path : Char*, times : Timespec[2], flag : Int) : Int
end
1 change: 0 additions & 1 deletion src/lib_c/aarch64-linux-gnu/c/sys/time.cr
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,5 @@ lib LibC
end

fun gettimeofday(tv : Timeval*, tz : Void*) : Int
fun utimes(path : Char*, times : Timeval[2]) : Int
fun futimens(fd : Int, times : Timespec[2]) : Int
end
1 change: 1 addition & 0 deletions src/lib_c/aarch64-linux-musl/c/fcntl.cr
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ lib LibC
O_RDONLY = 0o0
O_RDWR = 0o2
O_WRONLY = 0o1
AT_FDCWD = -100

struct Flock
l_type : Short
Expand Down
1 change: 1 addition & 0 deletions src/lib_c/aarch64-linux-musl/c/sys/stat.cr
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,5 @@ lib LibC
fun mknod(x0 : Char*, x1 : ModeT, x2 : DevT) : Int
fun stat(x0 : Char*, x1 : Stat*) : Int
fun umask(x0 : ModeT) : ModeT
fun utimensat(fd : Int, path : Char*, times : Timespec[2], flag : Int) : Int
end
1 change: 0 additions & 1 deletion src/lib_c/aarch64-linux-musl/c/sys/time.cr
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,5 @@ lib LibC
end

fun gettimeofday(x0 : Timeval*, x1 : Void*) : Int
fun utimes(path : Char*, times : Timeval[2]) : Int
fun futimens(fd : Int, times : Timespec[2]) : Int
end
1 change: 1 addition & 0 deletions src/lib_c/arm-linux-gnueabihf/c/fcntl.cr
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ lib LibC
O_RDONLY = 0o0
O_RDWR = 0o2
O_WRONLY = 0o1
AT_FDCWD = -100

struct Flock
l_type : Short
Expand Down
1 change: 1 addition & 0 deletions src/lib_c/arm-linux-gnueabihf/c/sys/stat.cr
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,5 @@ lib LibC
fun mknod(path : Char*, mode : ModeT, dev : DevT) : Int
fun stat(file : Char*, buf : Stat*) : Int
fun umask(mask : ModeT) : ModeT
fun utimensat(fd : Int, path : Char*, times : Timespec[2], flag : Int) : Int
end
1 change: 0 additions & 1 deletion src/lib_c/arm-linux-gnueabihf/c/sys/time.cr
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,5 @@ lib LibC
end

fun gettimeofday(tv : Timeval*, tz : Void*) : Int
fun utimes(path : Char*, times : Timeval[2]) : Int
fun futimens(fd : Int, times : Timespec[2]) : Int
end
1 change: 1 addition & 0 deletions src/lib_c/i386-linux-gnu/c/fcntl.cr
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ lib LibC
O_RDONLY = 0o0
O_RDWR = 0o2
O_WRONLY = 0o1
AT_FDCWD = -100

struct Flock
l_type : Short
Expand Down
1 change: 1 addition & 0 deletions src/lib_c/i386-linux-gnu/c/sys/stat.cr
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,5 @@ lib LibC
fun mknod(path : Char*, mode : ModeT, dev : DevT) : Int
fun stat = stat64(file : Char*, buf : Stat*) : Int
fun umask(mask : ModeT) : ModeT
fun utimensat(fd : Int, path : Char*, times : Timespec[2], flag : Int) : Int
end
1 change: 0 additions & 1 deletion src/lib_c/i386-linux-gnu/c/sys/time.cr
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,5 @@ lib LibC
end

fun gettimeofday(tv : Timeval*, tz : Void*) : Int
fun utimes(path : Char*, times : Timeval[2]) : Int
fun futimens(fd : Int, times : Timespec[2]) : Int
end
1 change: 1 addition & 0 deletions src/lib_c/i386-linux-musl/c/fcntl.cr
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ lib LibC
O_RDONLY = 0o0
O_RDWR = 0o2
O_WRONLY = 0o1
AT_FDCWD = -100

struct Flock
l_type : Short
Expand Down
1 change: 1 addition & 0 deletions src/lib_c/i386-linux-musl/c/sys/stat.cr
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,5 @@ lib LibC
fun mknod(x0 : Char*, x1 : ModeT, x2 : DevT) : Int
fun stat(x0 : Char*, x1 : Stat*) : Int
fun umask(x0 : ModeT) : ModeT
fun utimensat(fd : Int, path : Char*, times : Timespec[2], flag : Int) : Int
end
1 change: 0 additions & 1 deletion src/lib_c/i386-linux-musl/c/sys/time.cr
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,5 @@ lib LibC
end

fun gettimeofday(x0 : Timeval*, x1 : Void*) : Int
fun utimes(path : Char*, times : Timeval[2]) : Int
fun futimens(fd : Int, times : Timespec[2]) : Int
end
33 changes: 17 additions & 16 deletions src/lib_c/x86_64-dragonfly/c/fcntl.cr
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,23 @@ require "./sys/stat"
require "./unistd"

lib LibC
F_GETFD = 1
F_SETFD = 2
F_GETFL = 3
F_SETFL = 4
FD_CLOEXEC = 1
O_CLOEXEC = 0x20000
O_EXCL = 0x0800
O_TRUNC = 0x0400
O_CREAT = 0x0200
O_NOFOLLOW = 0x0100
O_SYNC = 0x0080
O_APPEND = 0x0008
O_NONBLOCK = 0x0004
O_RDWR = 0x0002
O_WRONLY = 0x0001
O_RDONLY = 0x0000
F_GETFD = 1
F_SETFD = 2
F_GETFL = 3
F_SETFL = 4
FD_CLOEXEC = 1
O_CLOEXEC = 0x20000
O_EXCL = 0x0800
O_TRUNC = 0x0400
O_CREAT = 0x0200
O_NOFOLLOW = 0x0100
O_SYNC = 0x0080
O_APPEND = 0x0008
O_NONBLOCK = 0x0004
O_RDWR = 0x0002
O_WRONLY = 0x0001
O_RDONLY = 0x0000
AT_FDCWD = 0xFFFAFDCD

struct Flock
l_start : OffT
Expand Down
1 change: 1 addition & 0 deletions src/lib_c/x86_64-dragonfly/c/sys/stat.cr
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,5 @@ lib LibC
fun mknod(x0 : Char*, x1 : ModeT, x2 : DevT) : Int
fun stat(x0 : Char*, x1 : Stat*) : Int
fun umask(x0 : ModeT) : ModeT
fun utimensat(fd : Int, path : Char*, times : Timespec[2], flag : Int) : Int
end
1 change: 0 additions & 1 deletion src/lib_c/x86_64-dragonfly/c/sys/time.cr
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,5 @@ lib LibC
end

fun gettimeofday(x0 : Timeval*, x1 : Timezone*) : Int
fun utimes(path : Char*, times : Timeval[2]) : Int
fun futimens(fd : Int, times : Timespec[2]) : Int
end
1 change: 1 addition & 0 deletions src/lib_c/x86_64-freebsd/c/fcntl.cr
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ lib LibC
O_RDONLY = 0x0000
O_RDWR = 0x0002
O_WRONLY = 0x0001
AT_FDCWD = -100

struct Flock
l_start : OffT
Expand Down
1 change: 1 addition & 0 deletions src/lib_c/x86_64-freebsd/c/sys/stat.cr
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,5 @@ lib LibC
fun mknod(x0 : Char*, x1 : ModeT, x2 : DevT) : Int
fun stat(x0 : Char*, x1 : Stat*) : Int
fun umask(x0 : ModeT) : ModeT
fun utimensat(fd : Int, path : Char*, times : Timespec[2], flag : Int) : Int
end
1 change: 0 additions & 1 deletion src/lib_c/x86_64-freebsd/c/sys/time.cr
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,5 @@ lib LibC
end

fun gettimeofday(x0 : Timeval*, x1 : Timezone*) : Int
fun utimes(path : Char*, times : Timeval[2]) : Int
fun futimens(fd : Int, times : Timespec[2]) : Int
end
1 change: 1 addition & 0 deletions src/lib_c/x86_64-linux-gnu/c/fcntl.cr
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ lib LibC
O_RDONLY = 0o0
O_RDWR = 0o2
O_WRONLY = 0o1
AT_FDCWD = -100

struct Flock
l_type : Short
Expand Down
1 change: 1 addition & 0 deletions src/lib_c/x86_64-linux-gnu/c/sys/stat.cr
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,5 @@ lib LibC
fun stat(file : Char*, buf : Stat*) : Int
fun __xstat(ver : Int, file : Char*, buf : Stat*) : Int
fun umask(mask : ModeT) : ModeT
fun utimensat(fd : Int, path : Char*, times : Timespec[2], flag : Int) : Int
end
1 change: 0 additions & 1 deletion src/lib_c/x86_64-linux-gnu/c/sys/time.cr
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,5 @@ lib LibC
end

fun gettimeofday(tv : Timeval*, tz : Void*) : Int
fun utimes(path : Char*, times : Timeval[2]) : Int
fun futimens(fd : Int, times : Timespec[2]) : Int
end
1 change: 1 addition & 0 deletions src/lib_c/x86_64-linux-musl/c/fcntl.cr
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ lib LibC
O_RDONLY = 0o0
O_RDWR = 0o2
O_WRONLY = 0o1
AT_FDCWD = -100

struct Flock
l_type : Short
Expand Down
1 change: 1 addition & 0 deletions src/lib_c/x86_64-linux-musl/c/sys/stat.cr
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,5 @@ lib LibC
fun mknod(x0 : Char*, x1 : ModeT, x2 : DevT) : Int
fun stat(x0 : Char*, x1 : Stat*) : Int
fun umask(x0 : ModeT) : ModeT
fun utimensat(fd : Int, path : Char*, times : Timespec[2], flag : Int) : Int
end
1 change: 0 additions & 1 deletion src/lib_c/x86_64-linux-musl/c/sys/time.cr
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,5 @@ lib LibC
end

fun gettimeofday(x0 : Timeval*, x1 : Void*) : Int
fun utimes(path : Char*, times : Timeval[2]) : Int
fun futimens(fd : Int, times : Timespec[2]) : Int
end
1 change: 1 addition & 0 deletions src/lib_c/x86_64-netbsd/c/fcntl.cr
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ lib LibC
O_RDONLY = 0x0000
O_RDWR = 0x0002
O_WRONLY = 0x0001
AT_FDCWD = -100

struct Flock
l_start : OffT
Expand Down
1 change: 1 addition & 0 deletions src/lib_c/x86_64-netbsd/c/sys/stat.cr
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,5 @@ lib LibC
fun mknod = __mknod50(x0 : Char*, x1 : ModeT, x2 : DevT) : Int
fun stat = __stat50(x0 : Char*, x1 : Stat*) : Int
fun umask(x0 : ModeT) : ModeT
fun utimensat(fd : Int, path : Char*, times : Timespec[2], flag : Int) : Int
end
1 change: 0 additions & 1 deletion src/lib_c/x86_64-netbsd/c/sys/time.cr
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,5 @@ lib LibC
end

fun gettimeofday = __gettimeofday50(x0 : Timeval*, x1 : Timezone*) : Int
fun utimes = __utimes50(path : Char*, times : Timeval[2]) : Int
fun futimens(fd : Int, times : Timespec[2]) : Int
end
1 change: 1 addition & 0 deletions src/lib_c/x86_64-openbsd/c/fcntl.cr
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ lib LibC
O_RDONLY = 0x0000
O_RDWR = 0x0002
O_WRONLY = 0x0001
AT_FDCWD = -100

struct Flock
l_start : OffT
Expand Down
1 change: 1 addition & 0 deletions src/lib_c/x86_64-openbsd/c/sys/stat.cr
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,5 @@ lib LibC
fun mknod(x0 : Char*, x1 : ModeT, x2 : DevT) : Int
fun stat(x0 : Char*, x1 : Stat*) : Int
fun umask(x0 : ModeT) : ModeT
fun utimensat(fd : Int, path : Char*, times : Timespec[2], flag : Int) : Int
end
1 change: 0 additions & 1 deletion src/lib_c/x86_64-openbsd/c/sys/time.cr
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,5 @@ lib LibC
end

fun gettimeofday(x0 : Timeval*, x1 : Timezone*) : Int
fun utimes(path : Char*, times : Timeval[2]) : Int
fun futimens(fd : Int, times : Timespec[2]) : Int
end
33 changes: 17 additions & 16 deletions src/lib_c/x86_64-solaris/c/fcntl.cr
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,23 @@ require "./sys/stat"
require "./unistd"

lib LibC
F_GETFD = 1
F_SETFD = 2
F_GETFL = 3
F_SETFL = 4
FD_CLOEXEC = 1
O_CLOEXEC = 0x800000
O_CREAT = 0x100
O_NOFOLLOW = 0x20000
O_TRUNC = 0x200
O_EXCL = 0x400
O_APPEND = 0x08
O_NONBLOCK = 0x80
O_SYNC = 0x10
O_RDONLY = 0
O_RDWR = 2
O_WRONLY = 1
F_GETFD = 1
F_SETFD = 2
F_GETFL = 3
F_SETFL = 4
FD_CLOEXEC = 1
O_CLOEXEC = 0x800000
O_CREAT = 0x100
O_NOFOLLOW = 0x20000
O_TRUNC = 0x200
O_EXCL = 0x400
O_APPEND = 0x08
O_NONBLOCK = 0x80
O_SYNC = 0x10
O_RDONLY = 0
O_RDWR = 2
O_WRONLY = 1
AT_FDCWD = 0xffd19553

struct Flock
l_type : Short
Expand Down
1 change: 1 addition & 0 deletions src/lib_c/x86_64-solaris/c/sys/stat.cr
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,5 @@ lib LibC
fun mknod(x0 : Char*, x1 : ModeT, x2 : DevT) : Int
fun stat(x0 : Char*, x1 : Stat*) : Int
fun umask(x0 : ModeT) : ModeT
fun utimensat(x0 : Int, x1 : Char*, x2 : Timespec[2], x3 : Int) : Int
end
1 change: 0 additions & 1 deletion src/lib_c/x86_64-solaris/c/sys/time.cr
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,5 @@ lib LibC
end

fun gettimeofday(x0 : Timeval*, x1 : Void*) : Int
fun utimes(path : Char*, times : Timeval[2]) : Int
fun futimens(fd : Int, times : Timespec[2]) : Int
end

0 comments on commit 2458e35

Please sign in to comment.