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 a3f4b7f4..f67b6870 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 926b09b3..d00da5ce 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 2539a5e1..af54c93f 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 21f50b84..61264e7d 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: