Skip to content

Commit edba7ff

Browse files
committed
add landmarks (#182)
1 parent 21a96bc commit edba7ff

File tree

4 files changed

+64
-6
lines changed

4 files changed

+64
-6
lines changed

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1605,7 +1605,7 @@ config file example:
16051605
w: * # anyone can upload here
16061606
rw: ed # only user "ed" can read-write
16071607
flags:
1608-
e2ds: # filesystem indexing is required for many of these:
1608+
e2ds # filesystem indexing is required for many of these:
16091609
sz: 1k-3m # accept upload only if filesize in this range
16101610
df: 4g # free disk space cannot go lower than this
16111611
vmaxb: 1g # volume can never exceed 1 GiB
@@ -1662,6 +1662,8 @@ this can instead be kept in a single place using the `--hist` argument, or the `
16621662
16631663
by default, the per-volume `up2k.db` sqlite3-database for `-e2d` and `-e2t` is stored next to the thumbnails according to the `--hist` option, but the global-option `--dbpath` and/or volflag `dbpath` can be used to put the database somewhere else
16641664
1665+
if your storage backend is unreliable (NFS or bad HDDs), you can specify one or more "landmarks" to look for before doing anything database-related. A landmark is a file which is always expected to exist inside the volume. This avoids spurious filesystem rescans in the event of an outage. One line per landmark (see example below)
1666+
16651667
note:
16661668
* putting the hist-folders on an SSD is strongly recommended for performance
16671669
* markdown edits are always stored in a local `.hist` subdirectory
@@ -1679,6 +1681,8 @@ config file example:
16791681
flags:
16801682
hist: - # restore the default (/mnt/nas/pics/.hist/)
16811683
hist: /mnt/nas/cache/pics/ # can be absolute path
1684+
landmark: me.jpg # /mnt/nas/pics/me.jpg must be readable to enable db
1685+
landmark: info/a.txt^=ok # and this textfile must start with "ok"
16821686
```
16831687
16841688

copyparty/authsrv.py

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -386,20 +386,20 @@ def __init__(
386386
self.adot: dict[str, list[str]] = {}
387387
self.js_ls = {}
388388
self.js_htm = ""
389+
self.all_vols: dict[str, VFS] = {} # flattened recursive
390+
self.all_nodes: dict[str, VFS] = {} # also jumpvols/shares
389391

390392
if realpath:
391393
rp = realpath + ("" if realpath.endswith(os.sep) else os.sep)
392394
vp = vpath + ("/" if vpath else "")
393395
self.histpath = os.path.join(realpath, ".hist") # db / thumbcache
394396
self.dbpath = self.histpath
395-
self.all_vols = {vpath: self} # flattened recursive
396-
self.all_nodes = {vpath: self} # also jumpvols/shares
397+
self.all_vols[vpath] = self
398+
self.all_nodes[vpath] = self
397399
self.all_aps = [(rp, [self])]
398400
self.all_vps = [(vp, self)]
399401
else:
400402
self.histpath = self.dbpath = ""
401-
self.all_vols = {}
402-
self.all_nodes = {}
403403
self.all_aps = []
404404
self.all_vps = []
405405

@@ -868,6 +868,53 @@ def chk_ap(self, ap: str, st: Optional[os.stat_result] = None) -> Optional["VFS"
868868

869869
return self
870870

871+
def check_landmarks(self) -> bool:
872+
if self.dbv:
873+
return True
874+
875+
vps = self.flags.get("landmark") or []
876+
if not vps:
877+
return True
878+
879+
failed = ""
880+
for vp in vps:
881+
if "^=" in vp:
882+
vp, zs = vp.split("^=", 1)
883+
expect = zs.encode("utf-8")
884+
else:
885+
expect = b""
886+
887+
if self.log:
888+
t = "checking [/%s] landmark [%s]"
889+
self.log("vfs", t % (self.vpath, vp), 6)
890+
891+
ap = "?"
892+
try:
893+
ap = self.canonical(vp)
894+
with open(ap, "rb") as f:
895+
buf = f.read(4096)
896+
if not buf.startswith(expect):
897+
t = "file [%s] does not start with the expected bytes %s"
898+
failed = t % (ap, expect)
899+
break
900+
except Exception as ex:
901+
t = "%r while trying to read [%s] => [%s]"
902+
failed = t % (ex, vp, ap)
903+
break
904+
905+
if not failed:
906+
return True
907+
908+
if self.log:
909+
t = "WARNING: landmark verification failed; %s; will now disable up2k database for volume [/%s]"
910+
self.log("vfs", t % (failed, self.vpath), 3)
911+
912+
for rm in "e2d e2t e2v".split():
913+
self.flags = {k: v for k, v in self.flags.items() if not k.startswith(rm)}
914+
self.flags["d2d"] = True
915+
self.flags["d2t"] = True
916+
return False
917+
871918

872919
if WINDOWS:
873920
re_vol = re.compile(r"^([a-zA-Z]:[\\/][^:]*|[^:]*):([^:]*):(.*)$")
@@ -1501,7 +1548,7 @@ def _read_volflag(
15011548
flags[name] = True
15021549
return
15031550

1504-
zs = "ext_th mtp on403 on404 xbu xau xiu xbc xac xbr xar xbd xad xm xban"
1551+
zs = "ext_th landmark mtp on403 on404 xbu xau xiu xbc xac xbr xar xbd xad xm xban"
15051552
if name not in zs.split():
15061553
if value is True:
15071554
t = "└─add volflag [{}] = {} ({})"
@@ -2237,6 +2284,8 @@ def _reload(self, verbosity: int = 9) -> None:
22372284
t = "WARNING: volume [/%s]: invalid value specified for ext-th: %s"
22382285
self.log(t % (vol.vpath, etv), 3)
22392286

2287+
vol.check_landmarks()
2288+
22402289
# d2d drops all database features for a volume
22412290
for grp, rm in [["d2d", "e2d"], ["d2t", "e2t"], ["d2d", "e2v"]]:
22422291
if not vol.flags.get(grp, False):

copyparty/cfg.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,7 @@ def vf_cmap() -> dict[str, str]:
222222
"d2d": "disables all database stuff, overrides -e2*",
223223
"hist=/tmp/cdb": "puts thumbnails and indexes at that location",
224224
"dbpath=/tmp/cdb": "puts indexes at that location",
225+
"landmark=foo": "disable db if file foo doesn't exist",
225226
"scan=60": "scan for new files every 60sec, same as --re-maxage",
226227
"nohash=\\.iso$": "skips hashing file contents if path matches *.iso",
227228
"noidx=\\.iso$": "fully ignores the contents at paths matching *.iso",

copyparty/up2k.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1379,6 +1379,10 @@ def _build_file_index(self, vol: VFS, all_vols: list[VFS]) -> tuple[bool, bool]:
13791379
t = "volume /%s at [%s] is empty; will not be indexed as this could be due to an offline filesystem"
13801380
self.log(t % (vol.vpath, rtop), 6)
13811381
return True, False
1382+
if not vol.check_landmarks():
1383+
t = "volume /%s at [%s] will not be indexed due to bad landmarks"
1384+
self.log(t % (vol.vpath, rtop), 6)
1385+
return True, False
13821386

13831387
n_add, _, _ = self._build_dir(
13841388
db,

0 commit comments

Comments
 (0)