diff --git a/copyparty/up2k.py b/copyparty/up2k.py index 3acf3918..3f8e8f95 100644 --- a/copyparty/up2k.py +++ b/copyparty/up2k.py @@ -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) diff --git a/copyparty/util.py b/copyparty/util.py index c476a2ea..da6bbce4 100644 --- a/copyparty/util.py +++ b/copyparty/util.py @@ -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 : copystat(src, dst, follow_symlinks=follow_symlinks) + # shutil.py:376 : 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: