From 2a570bb4caae793989f3f19da4ee5256afadaf72 Mon Sep 17 00:00:00 2001 From: ed Date: Fri, 18 Oct 2024 18:14:35 +0000 Subject: [PATCH] fix `--df` for webdav; closes #107 PUT uploads, as used by webdav, would stat the absolute path of the file to be created, which would throw ENOENT strip components until the path is an existing directory and also try to enforce disk space / volume size limits even when the incoming file is of unknown size --- copyparty/authsrv.py | 17 ++++++++++++++--- copyparty/httpcli.py | 2 +- copyparty/metrics.py | 2 +- copyparty/util.py | 24 ++++++++++++++++-------- 4 files changed, 32 insertions(+), 13 deletions(-) diff --git a/copyparty/authsrv.py b/copyparty/authsrv.py index a3f4b7f47..f67b68706 100644 --- a/copyparty/authsrv.py +++ b/copyparty/authsrv.py @@ -165,8 +165,11 @@ class Lim(object): self.chk_rem(rem) if sz != -1: self.chk_sz(sz) - self.chk_vsz(broker, ptop, sz, volgetter) - self.chk_df(abspath, sz) # side effects; keep last-ish + else: + sz = 0 + + self.chk_vsz(broker, ptop, sz, volgetter) + self.chk_df(abspath, sz) # side effects; keep last-ish ap2, vp2 = self.rot(abspath) if abspath == ap2: @@ -206,7 +209,15 @@ class Lim(object): if self.dft < time.time(): self.dft = int(time.time()) + 300 - self.dfv = get_df(abspath)[0] or 0 + + df, du, err = get_df(abspath, True) + if err: + t = "failed to read disk space usage for [%s]: %s" + self.log(t % (abspath, err), 3) + self.dfv = 0xAAAAAAAAA # 42.6 GiB + else: + self.dfv = df or 0 + for j in list(self.reg.values()) if self.reg else []: self.dfv -= int(j["size"] / (len(j["hash"]) or 999) * len(j["need"])) diff --git a/copyparty/httpcli.py b/copyparty/httpcli.py index 926b09b33..d00da5cee 100644 --- a/copyparty/httpcli.py +++ b/copyparty/httpcli.py @@ -5105,7 +5105,7 @@ class HttpCli(object): self.log("#wow #whoa") if not self.args.nid: - free, total = get_df(abspath) + free, total, _ = get_df(abspath, False) if total is not None: h1 = humansize(free or 0) h2 = humansize(total) diff --git a/copyparty/metrics.py b/copyparty/metrics.py index 2539a5e19..af54c93f4 100644 --- a/copyparty/metrics.py +++ b/copyparty/metrics.py @@ -128,7 +128,7 @@ class Metrics(object): addbh("cpp_disk_size_bytes", "total HDD size of volume") addbh("cpp_disk_free_bytes", "free HDD space in volume") for vpath, vol in allvols: - free, total = get_df(vol.realpath) + free, total, _ = get_df(vol.realpath, False) if free is None or total is None: continue diff --git a/copyparty/util.py b/copyparty/util.py index 21f50b845..61264e7d8 100644 --- a/copyparty/util.py +++ b/copyparty/util.py @@ -213,6 +213,9 @@ except: ansi_re = re.compile("\033\\[[^mK]*[mK]") +BOS_SEP = ("%s" % (os.sep,)).encode("ascii") + + surrogateescape.register_surrogateescape() if WINDOWS and PY2: FS_ENCODING = "utf-8" @@ -2492,23 +2495,28 @@ def wunlink(log: "NamedLogger", abspath: str, flags: dict[str, Any]) -> bool: return _fs_mvrm(log, abspath, "", False, flags) -def get_df(abspath: str) -> tuple[Optional[int], Optional[int]]: +def get_df(abspath: str, prune: bool) -> tuple[Optional[int], Optional[int], str]: try: - # some fuses misbehave - assert ctypes # type: ignore # !rm + ap = fsenc(abspath) + while prune and not os.path.isdir(ap) and BOS_SEP in ap: + # strip leafs until it hits an existing folder + ap = ap.rsplit(BOS_SEP, 1)[0] + if ANYWIN: + assert ctypes # type: ignore # !rm + abspath = fsdec(ap) bfree = ctypes.c_ulonglong(0) ctypes.windll.kernel32.GetDiskFreeSpaceExW( # type: ignore ctypes.c_wchar_p(abspath), None, None, ctypes.pointer(bfree) ) - return (bfree.value, None) + return (bfree.value, None, "") else: - sv = os.statvfs(fsenc(abspath)) + sv = os.statvfs(ap) free = sv.f_frsize * sv.f_bfree total = sv.f_frsize * sv.f_blocks - return (free, total) - except: - return (None, None) + return (free, total, "") + except Exception as ex: + return (None, None, repr(ex)) if not ANYWIN and not MACOS: