From f6e693f0f553041c4c80d644dc47293426ceee7e Mon Sep 17 00:00:00 2001 From: ed Date: Wed, 24 Apr 2024 21:18:26 +0000 Subject: [PATCH] reevaluate support for sparse files periodically if a given filesystem were to disappear (e.g. removable storage) followed by another filesystem appearing at the same location, this would not get noticed by up2k in a timely manner fix this by discarding the mtab cache after `--mtab-age` seconds and rebuild it from scratch, unless the previous values are definitely correct (as indicated by identical output from `/bin/mount`) probably reduces windows performance by an acceptable amount --- copyparty/__main__.py | 1 + copyparty/fsutil.py | 30 +++++++++++++++++++++++++----- copyparty/up2k.py | 2 +- tests/util.py | 2 +- 4 files changed, 28 insertions(+), 7 deletions(-) diff --git a/copyparty/__main__.py b/copyparty/__main__.py index 931e187f..17289afa 100755 --- a/copyparty/__main__.py +++ b/copyparty/__main__.py @@ -860,6 +860,7 @@ def add_fs(ap): ap2.add_argument("--rm-retry", metavar="T/R", type=u, default=rm_re_def, help="if a file cannot be deleted because it is busy, continue trying for \033[33mT\033[0m seconds, retry every \033[33mR\033[0m seconds; disable with 0/0 (volflag=rm_retry)") ap2.add_argument("--mv-retry", metavar="T/R", type=u, default=rm_re_def, help="if a file cannot be renamed because it is busy, continue trying for \033[33mT\033[0m seconds, retry every \033[33mR\033[0m seconds; disable with 0/0 (volflag=mv_retry)") ap2.add_argument("--iobuf", metavar="BYTES", type=int, default=256*1024, help="file I/O buffer-size; if your volumes are on a network drive, try increasing to \033[32m524288\033[0m or even \033[32m4194304\033[0m (and let me know if that improves your performance)") + ap2.add_argument("--mtab-age", metavar="SEC", type=int, default=60, help="rebuild mountpoint cache every \033[33mSEC\033[0m to keep track of sparse-files support; keep low on servers with removable media") def add_upload(ap): diff --git a/copyparty/fsutil.py b/copyparty/fsutil.py index 5bf5ab6b..5ba9df43 100644 --- a/copyparty/fsutil.py +++ b/copyparty/fsutil.py @@ -1,6 +1,7 @@ # coding: utf-8 from __future__ import print_function, unicode_literals +import argparse import os import re import time @@ -17,20 +18,26 @@ if True: # pylint: disable=using-constant-test class Fstab(object): - def __init__(self, log: "RootLogger"): + def __init__(self, log: "RootLogger", args: argparse.Namespace): self.log_func = log + self.warned = False self.trusted = False self.tab: Optional[VFS] = None + self.oldtab: Optional[VFS] = None + self.srctab = "a" self.cache: dict[str, str] = {} self.age = 0.0 + self.maxage = args.mtab_age def log(self, msg: str, c: Union[int, str] = 0) -> None: self.log_func("fstab", msg, c) def get(self, path: str) -> str: - if len(self.cache) > 9000: - self.age = time.time() + now = time.time() + if now - self.age > self.maxage or len(self.cache) > 9000: + self.age = now + self.oldtab = self.tab or self.oldtab self.tab = None self.cache = {} @@ -75,7 +82,7 @@ class Fstab(object): self.trusted = False def build_tab(self) -> None: - self.log("building tab") + self.log("inspecting mtab for changes") sptn = r"^.*? on (.*) type ([^ ]+) \(.*" if MACOS: @@ -84,6 +91,7 @@ class Fstab(object): ptn = re.compile(sptn) so, _ = chkcmd(["mount"]) tab1: list[tuple[str, str]] = [] + atab = [] for ln in so.split("\n"): m = ptn.match(ln) if not m: @@ -91,6 +99,15 @@ class Fstab(object): zs1, zs2 = m.groups() tab1.append((str(zs1), str(zs2))) + atab.append(ln) + + # keep empirically-correct values if mounttab unchanged + srctab = "\n".join(sorted(atab)) + if srctab == self.srctab: + self.tab = self.oldtab + return + + self.log("mtab has changed; reevaluating support for sparse files") tab1.sort(key=lambda x: (len(x[0]), x[0])) path1, fs1 = tab1[0] @@ -99,6 +116,7 @@ class Fstab(object): tab.add(fs, path.lstrip("/")) self.tab = tab + self.srctab = srctab def relabel(self, path: str, nval: str) -> None: assert self.tab @@ -133,7 +151,9 @@ class Fstab(object): self.trusted = True except: # prisonparty or other restrictive environment - self.log("failed to build tab:\n{}".format(min_ex()), 3) + if not self.warned: + self.warned = True + self.log("failed to build tab:\n{}".format(min_ex()), 3) self.build_fallback() assert self.tab diff --git a/copyparty/up2k.py b/copyparty/up2k.py index a62ad77d..db993541 100644 --- a/copyparty/up2k.py +++ b/copyparty/up2k.py @@ -186,7 +186,7 @@ class Up2k(object): t = "could not initialize sqlite3, will use in-memory registry only" self.log(t, 3) - self.fstab = Fstab(self.log_func) + self.fstab = Fstab(self.log_func, self.args) self.gen_fk = self._gen_fk if self.args.log_fk else gen_filekey if self.args.hash_mt < 2: diff --git a/tests/util.py b/tests/util.py index 8a5a75e6..2cc02e17 100644 --- a/tests/util.py +++ b/tests/util.py @@ -122,7 +122,7 @@ class Cfg(Namespace): ex = "hash_mt srch_time u2abort u2j" ka.update(**{k: 1 for k in ex.split()}) - ex = "reg_cap s_thead s_tbody th_convt" + ex = "mtab_age reg_cap s_thead s_tbody th_convt" ka.update(**{k: 9 for k in ex.split()}) ex = "db_act df k304 loris re_maxage rproxy rsp_jtr rsp_slp s_wr_slp snap_wri theme themes turbo"