osutil: add Libc.futimens() wrapper for futimens() call

Python has no wrapper for a futime*() call so we need to implement
it in the `util.linux` package.
This commit is contained in:
Michael Vogt 2023-12-12 15:11:40 +01:00 committed by Simon de Vlieger
parent ca9f4038c8
commit f52cabc3c1
2 changed files with 50 additions and 0 deletions

View file

@ -422,6 +422,14 @@ def fcntl_flock(fd: int, lock_type: int, wait: bool = False):
fcntl.fcntl(fd, lock_cmd, arg_flock64)
class c_timespec(ctypes.Structure):
_fields_ = [('tv_sec', ctypes.c_long), ('tv_nsec', ctypes.c_long)]
class c_timespec_times2(ctypes.Structure):
_fields_ = [('atime', c_timespec), ('mtime', c_timespec)]
class Libc:
"""Safe Access to libc
@ -434,6 +442,10 @@ class Libc:
RENAME_NOREPLACE = ctypes.c_uint(1)
RENAME_WHITEOUT = ctypes.c_uint(4)
# see /usr/include/x86_64-linux-gnu/bits/stat.h
UTIME_NOW = ctypes.c_long(((1 << 30) - 1))
UTIME_OMIT = ctypes.c_long(((1 << 30) - 2))
_lock = threading.Lock()
_inst = None
@ -462,6 +474,22 @@ class Libc:
setattr(proto, "errcheck", self._errcheck_errno)
setattr(proto, "__name__", "renameat2")
self.renameat2 = proto
# prototype: futimens
proto = ctypes.CFUNCTYPE(
ctypes.c_int,
ctypes.c_int,
ctypes.POINTER(c_timespec_times2),
use_errno=True,
)(
("futimens", self._lib),
(
(1, "fd"),
(1, "timespec"),
),
)
setattr(proto, "errcheck", self._errcheck_errno)
setattr(proto, "__name__", "futimens")
self.futimens = proto
@staticmethod
def make() -> "Libc":

View file

@ -2,6 +2,7 @@
# Tests for the `osbuild.util.linux` module.
#
import ctypes
import os
import subprocess
import tempfile
@ -240,3 +241,24 @@ def test_proc_boot_id():
bootid3 = linux.proc_boot_id("foobar")
assert bootid.int != bootid3.int
def test_libc_futimens_errcheck():
libc = linux.Libc.default()
with pytest.raises(OSError):
libc.futimens(-1, None)
def test_libc_futimes_works(tmpdir):
libc = linux.Libc.default()
stamp_file = os.path.join(tmpdir, "foo")
with open(stamp_file, "w") as fp:
fp.write("meep")
mtime1 = os.stat(stamp_file).st_mtime
with open(stamp_file, "w") as fp:
libc.futimens(fp.fileno(), ctypes.byref(linux.c_timespec_times2(
atime=linux.c_timespec(tv_sec=3, tv_nsec=300*1000*1000),
mtime=linux.c_timespec(tv_sec=0, tv_nsec=libc.UTIME_OMIT),
)))
assert os.stat(stamp_file).st_atime == 3.3
assert os.stat(stamp_file).st_mtime == mtime1