clamp utime to filesystem limits (#539)

This commit is contained in:
ed 2025-09-04 23:31:05 +00:00
parent e6755aa8a1
commit eeb7738b53
5 changed files with 54 additions and 26 deletions

View file

@ -2,18 +2,22 @@
from __future__ import print_function, unicode_literals from __future__ import print_function, unicode_literals
import os import os
import time
from ..util import SYMTIME, fsdec, fsenc from ..util import SYMTIME, fsdec, fsenc
from . import path as path from . import path as path
if True: # pylint: disable=using-constant-test if True: # pylint: disable=using-constant-test
from typing import Any, Optional from typing import Any, Optional, Union
from ..util import NamedLogger
MKD_755 = {"chmod_d": 0o755} MKD_755 = {"chmod_d": 0o755}
MKD_700 = {"chmod_d": 0o700} MKD_700 = {"chmod_d": 0o700}
UTIME_CLAMPS = ((max, -2147483647), (max, 1), (min, 4294967294), (min, 2147483646))
_ = (path, MKD_755, MKD_700) _ = (path, MKD_755, MKD_700, UTIME_CLAMPS)
__all__ = ["path", "MKD_755", "MKD_700"] __all__ = ["path", "MKD_755", "MKD_700", "UTIME_CLAMPS"]
# grep -hRiE '(^|[^a-zA-Z_\.-])os\.' . | gsed -r 's/ /\n/g;s/\(/(\n/g' | grep -hRiE '(^|[^a-zA-Z_\.-])os\.' | sort | uniq -c # grep -hRiE '(^|[^a-zA-Z_\.-])os\.' . | gsed -r 's/ /\n/g;s/\(/(\n/g' | grep -hRiE '(^|[^a-zA-Z_\.-])os\.' | sort | uniq -c
# printf 'os\.(%s)' "$(grep ^def bos/__init__.py | gsed -r 's/^def //;s/\(.*//' | tr '\n' '|' | gsed -r 's/.$//')" # printf 'os\.(%s)' "$(grep ^def bos/__init__.py | gsed -r 's/^def //;s/\(.*//' | tr '\n' '|' | gsed -r 's/.$//')"
@ -99,6 +103,40 @@ def utime(
return os.utime(fsenc(p), times) return os.utime(fsenc(p), times)
def utime_c(
log: Union["NamedLogger", Any], p: str, ts: int, follow_symlinks: bool = True, throw: bool = False
) -> Optional[int]:
clamp = 0
ov = ts
bp = fsenc(p)
now = int(time.time())
while True:
try:
if SYMTIME:
os.utime(bp, (now, ts), follow_symlinks=follow_symlinks)
else:
os.utime(bp, (now, ts))
if clamp:
t = "filesystem rejected utime(%r); clamped %s to %s"
log(t % (p, ov, ts))
return ts
except Exception as ex:
pv = ts
while clamp < len(UTIME_CLAMPS):
fun, cv = UTIME_CLAMPS[clamp]
ts = fun(ts, cv)
clamp += 1
if ts != pv:
break
if clamp >= len(UTIME_CLAMPS):
if throw:
raise
else:
t = "could not utime(%r) to %s; %s, %r"
log(t % (p, ov, ex, ex))
return None
if hasattr(os, "lstat"): if hasattr(os, "lstat"):
def lstat(p: str) -> os.stat_result: def lstat(p: str) -> os.stat_result:

View file

@ -409,12 +409,8 @@ class FtpFs(AbstractedFS):
return st return st
def utime(self, path: str, timeval: float) -> None: def utime(self, path: str, timeval: float) -> None:
try: ap = self.rv2a(path, w=True)[0]
ap = self.rv2a(path, w=True)[0] bos.utime_c(logging.warning, ap, int(timeval), False)
return bos.utime(ap, (int(time.time()), int(timeval)))
except Exception as ex:
logging.error("ftp.utime: %s, %r", ex, ex)
raise
def lstat(self, path: str) -> os.stat_result: def lstat(self, path: str) -> os.stat_result:
ap = self.rv2a(path)[0] ap = self.rv2a(path)[0]

View file

@ -2329,12 +2329,7 @@ class HttpCli(object):
at = mt = time.time() - lifetime at = mt = time.time() - lifetime
cli_mt = self.headers.get("x-oc-mtime") cli_mt = self.headers.get("x-oc-mtime")
if cli_mt: if cli_mt:
try: bos.utime_c(self.log, path, int(cli_mt), False)
mt = int(cli_mt)
times = (int(time.time()), mt)
bos.utime(path, times, False)
except:
pass
if nameless and "magic" in vfs.flags: if nameless and "magic" in vfs.flags:
try: try:

View file

@ -373,7 +373,7 @@ class SMB(object):
t = "blocked utime (no-write-acc %s): /%s @%s" t = "blocked utime (no-write-acc %s): /%s @%s"
yeet(t % (vfs.axs.uwrite, vpath, uname)) yeet(t % (vfs.axs.uwrite, vpath, uname))
return bos.utime(ap, times) bos.utime_c(info, ap, int(times[1]), False)
def _p_exists(self, vpath: str) -> bool: def _p_exists(self, vpath: str) -> bool:
# ap = "?" # ap = "?"

View file

@ -3435,10 +3435,9 @@ class Up2k(object):
cur.connection.commit() cur.connection.commit()
ap = djoin(job["ptop"], job["prel"], job["name"]) ap = djoin(job["ptop"], job["prel"], job["name"])
times = (int(time.time()), int(cj["lmod"])) mt = bos.utime_c(self.log, ap, int(cj["lmod"]), False, True)
bos.utime(ap, times, False)
self.log("touched %r from %d to %d" % (ap, job["lmod"], cj["lmod"])) self.log("touched %r from %d to %d" % (ap, job["lmod"], mt))
except Exception as ex: except Exception as ex:
self.log("umod failed, %r" % (ex,), 3) self.log("umod failed, %r" % (ex,), 3)
@ -3593,8 +3592,7 @@ class Up2k(object):
shutil.copy2(fsenc(csrc), fsenc(dst)) shutil.copy2(fsenc(csrc), fsenc(dst))
if lmod and (not linked or SYMTIME): if lmod and (not linked or SYMTIME):
times = (int(time.time()), int(lmod)) bos.utime_c(self.log, dst, int(lmod), False)
bos.utime(dst, times, False)
def handle_chunks( def handle_chunks(
self, ptop: str, wark: str, chashes: list[str] self, ptop: str, wark: str, chashes: list[str]
@ -3767,10 +3765,8 @@ class Up2k(object):
times = (int(time.time()), int(job["lmod"])) times = (int(time.time()), int(job["lmod"]))
t = "no more chunks, setting times %s (%d) on %r" t = "no more chunks, setting times %s (%d) on %r"
self.log(t % (times, bos.path.getsize(dst), dst)) self.log(t % (times, bos.path.getsize(dst), dst))
try: bos.utime_c(self.log, dst, times[1], False)
bos.utime(dst, times) # the above logmsg (and associated logic) is retained due to unforget.py
except:
self.log("failed to utime (%r, %s)" % (dst, times))
zs = "prel name lmod size ptop vtop wark dwrk host user addr" zs = "prel name lmod size ptop vtop wark dwrk host user addr"
z2 = [job[x] for x in zs.split()] z2 = [job[x] for x in zs.split()]
@ -4919,7 +4915,10 @@ class Up2k(object):
mt = bos.path.getmtime(slabs, False) mt = bos.path.getmtime(slabs, False)
flags = self.flags.get(ptop) or {} flags = self.flags.get(ptop) or {}
atomic_move(self.log, sabs, slabs, flags) atomic_move(self.log, sabs, slabs, flags)
bos.utime(slabs, (int(time.time()), int(mt)), False) try:
bos.utime(slabs, (int(time.time()), int(mt)), False)
except:
self.log("relink: failed to utime(%r, %s)" % (slabs, mt), 3)
self._symlink(slabs, sabs, flags, False, is_mv=True) self._symlink(slabs, sabs, flags, False, is_mv=True)
full[slabs] = (ptop, rem) full[slabs] = (ptop, rem)
sabs = slabs sabs = slabs