shutil: ignore errors from copystat in copy2;

ntfs on linux can be picky about cloning mtime onto a new file;
generally we don't care if that fails, however, we also want the
speedup that CopyFile2 can offer, so cannot use copyfile directly

this avoids the following issue:

up2k:3537 <_symlink>: shutil.copy2(fsenc(csrc), fsenc(dst))
shutil:437 <copy2>: copystat(src, dst, follow_symlinks=follow_sym[...]
shutil:376 <copystat>: lookup("utime")(dst, ns=(st.st_atime_ns, s[...]
[PermissionError] [Errno 1] Operation not permitted, '/windows/videos'
This commit is contained in:
ed 2025-09-08 20:21:12 +00:00
parent 25749b4b5f
commit e09f3c9e2c
2 changed files with 23 additions and 4 deletions

View file

@ -60,6 +60,7 @@ from .util import (
sfsenc,
spack,
statdir,
trystat_shutil_copy2,
ub64enc,
unhumanize,
vjoin,
@ -2768,7 +2769,7 @@ class Up2k(object):
cur.close()
db.close()
shutil.copy2(fsenc(db_path), fsenc(bak))
trystat_shutil_copy2(self.log, fsenc(db_path), fsenc(bak))
return self._orz(db_path)
def _read_ver(self, cur: "sqlite3.Cursor") -> Optional[int]:
@ -3591,7 +3592,7 @@ class Up2k(object):
t = "BUG: no valid sources to link from! orig(%r) fsrc(%r) link(%r)"
self.log(t, 1)
raise Exception(t % (src, fsrc, dst))
shutil.copy2(fsenc(csrc), fsenc(dst))
trystat_shutil_copy2(self.log, fsenc(csrc), fsenc(dst))
if lmod and (not linked or SYMTIME):
bos.utime_c(self.log, dst, int(lmod), False)
@ -4427,7 +4428,7 @@ class Up2k(object):
b1, b2 = fsenc(sabs), fsenc(dabs)
is_link = os.path.islink(b1) # due to _relink
try:
shutil.copy2(b1, b2)
trystat_shutil_copy2(self.log, b1, b2)
except:
try:
wunlink(self.log, dabs, dvn.flags)
@ -4733,7 +4734,7 @@ class Up2k(object):
b1, b2 = fsenc(sabs), fsenc(dabs)
is_link = os.path.islink(b1) # due to _relink
try:
shutil.copy2(b1, b2)
trystat_shutil_copy2(self.log, b1, b2)
except:
try:
wunlink(self.log, dabs, dvn.flags)

View file

@ -2639,6 +2639,24 @@ def set_fperms(f: Union[typing.BinaryIO, typing.IO[Any]], vf: dict[str, Any]) ->
os.fchown(fno, vf["uid"], vf["gid"])
def trystat_shutil_copy2(log: "NamedLogger", src: bytes, dst: bytes) -> bytes:
try:
return shutil.copy2(src, dst)
except:
# ignore failed mtime on linux+ntfs; for example:
# shutil.py:437 <copy2>: copystat(src, dst, follow_symlinks=follow_symlinks)
# shutil.py:376 <copystat>: lookup("utime")(dst, ns=(st.st_atime_ns, st.st_mtime_ns),
# [PermissionError] [Errno 1] Operation not permitted, '/windows/_videos'
_, _, tb = sys.exc_info()
for _, _, fun, _ in traceback.extract_tb(tb):
if fun == "copystat":
if log:
t = "warning: failed to retain some file attributes (timestamp and/or permissions) during copy from %r to %r:\n%s"
log(t % (src, dst, min_ex()), 3)
return dst # close enough
raise
def _fs_mvrm(
log: "NamedLogger", src: str, dst: str, atomic: bool, flags: dict[str, Any]
) -> bool: