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
import os
import time
from ..util import SYMTIME, fsdec, fsenc
from . import path as path
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_700 = {"chmod_d": 0o700}
UTIME_CLAMPS = ((max, -2147483647), (max, 1), (min, 4294967294), (min, 2147483646))
_ = (path, MKD_755, MKD_700)
__all__ = ["path", "MKD_755", "MKD_700"]
_ = (path, MKD_755, MKD_700, UTIME_CLAMPS)
__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
# 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)
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"):
def lstat(p: str) -> os.stat_result:

View file

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

View file

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

View file

@ -373,7 +373,7 @@ class SMB(object):
t = "blocked utime (no-write-acc %s): /%s @%s"
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:
# ap = "?"

View file

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