From 60ceea4b425858e52aa27c0a7ff91b16bfc3d1bf Mon Sep 17 00:00:00 2001 From: ed Date: Sun, 18 Jan 2026 22:15:34 +0000 Subject: [PATCH] add nospawn, assert_root --- copyparty/__main__.py | 2 ++ copyparty/authsrv.py | 35 +++++++++++++++++++++++++++++++++++ copyparty/cfg.py | 4 ++++ tests/util.py | 2 +- 4 files changed, 42 insertions(+), 1 deletion(-) diff --git a/copyparty/__main__.py b/copyparty/__main__.py index fe2e4a2f..4db2fcc3 100644 --- a/copyparty/__main__.py +++ b/copyparty/__main__.py @@ -1585,6 +1585,8 @@ def add_safety(ap): ap2.add_argument("--ls", metavar="U[,V[,F]]", type=u, default="", help="do a sanity/safety check of all volumes on startup; arguments \033[33mUSER\033[0m,\033[33mVOL\033[0m,\033[33mFLAGS\033[0m (see \033[33m--help-ls\033[0m); example [\033[32m**,*,ln,p,r\033[0m]") ap2.add_argument("--xvol", action="store_true", help="never follow symlinks leaving the volume root, unless the link is into another volume where the user has similar access (volflag=xvol)") ap2.add_argument("--xdev", action="store_true", help="stay within the filesystem of the volume root; do not descend into other devices (symlink or bind-mount to another HDD, ...) (volflag=xdev)") + ap2.add_argument("--vol-nospawn", action="store_true", help="if a volume's folder does not exist on the HDD, then do not create it (continue with warning) (volflag=nospawn)") + ap2.add_argument("--vol-or-crash", action="store_true", help="if a volume's folder does not exist on the HDD, then burst into flames (volflag=assert_root)") ap2.add_argument("--no-dot-mv", action="store_true", help="disallow moving dotfiles; makes it impossible to move folders containing dotfiles") ap2.add_argument("--no-dot-ren", action="store_true", help="disallow renaming dotfiles; makes it impossible to turn something into a dotfile") ap2.add_argument("--no-logues", action="store_true", help="disable rendering .prologue/.epilogue.html into directory listings") diff --git a/copyparty/authsrv.py b/copyparty/authsrv.py index 3975f675..e858cac5 100644 --- a/copyparty/authsrv.py +++ b/copyparty/authsrv.py @@ -2090,6 +2090,41 @@ class AuthSrv(object): t = "WARNING: the account [%s] is not mentioned in any volume definitions and thus has the same access-level and privileges that guests have; please see --help-accounts for details. For example, if you intended to give that user full access to the current directory, you could do this: -v .::A,%s" self.log(t % (usr, usr), 1) + dropvols = [] + errors = False + for vol in vfs.all_vols.values(): + if ( + not vol.realpath + or ( + "assert_root" not in vol.flags + and "nospawn" not in vol.flags + and not self.args.vol_or_crash + and not self.args.vol_nospawn + ) + or bos.path.exists(vol.realpath) + ): + pass + elif "assert_root" in vol.flags or self.args.vol_or_crash: + t = "ERROR: volume [/%s] root folder %r does not exist on server HDD; will now crash due to volflag 'assert_root'" + self.log(t % (vol.vpath, vol.realpath), 1) + errors = True + else: + t = "WARNING: volume [/%s] root folder %r does not exist on server HDD; volume will be unavailable due to volflag 'nospawn'" + self.log(t % (vol.vpath, vol.realpath), 3) + dropvols.append(vol) + if errors: + sys.exit(1) + for vol in dropvols: + vol.realpath = "" + vol.axs = AXS() + vol.uaxs = {} + vfs.all_vols.pop(vol.vpath, None) + vfs.all_nodes.pop(vol.vpath, None) + for zv in vfs.all_nodes.values(): + zs = next((x for x, y in zv.nodes.items() if y == vol), "") + if zs: + zv.nodes.pop(zs) + promote = [] demote = [] for vol in vfs.all_vols.values(): diff --git a/copyparty/cfg.py b/copyparty/cfg.py index 163fd4ad..29c05990 100644 --- a/copyparty/cfg.py +++ b/copyparty/cfg.py @@ -27,6 +27,8 @@ def vf_bmap() -> dict[str, str]: "no_thumb": "dthumb", "no_vthumb": "dvthumb", "no_athumb": "dathumb", + "vol_nospawn": "nospawn", + "vol_or_crash": "assert_root", } for k in ( "dedup", @@ -427,6 +429,8 @@ flagcats = { "cachectl=no-cache": "controls caching in webbrowsers", "mv_retry": "ms-windows: timeout for renaming busy files", "rm_retry": "ms-windows: timeout for deleting busy files", + "nospawn": "don't create volume's folder if not exist", + "assert_root": "crash on startup if volume's folder not exist", "davauth": "ask webdav clients to login for all folders", "davrt": "show lastmod time of symlink destination, not the link itself\n(note: this option is always enabled for recursive listings)", }, diff --git a/tests/util.py b/tests/util.py index f3ccca87..3afba6bc 100644 --- a/tests/util.py +++ b/tests/util.py @@ -143,7 +143,7 @@ class Cfg(Namespace): def __init__(self, a=None, v=None, c=None, **ka0): ka = {} - ex = "allow_flac allow_wav chpw cookie_lax daw dav_auth dav_mac dav_rt dlni e2d e2ds e2dsa e2t e2ts e2tsr e2v e2vu e2vp early_ban ed emp exp force_js getmod grid gsel hardlink hardlink_only http_no_tcp ih ihead localtime log_badxml magic md_no_br nid nih no_acode no_athumb no_bauth no_clone no_cp no_dav no_db_ip no_del no_dirsz no_dupe no_dupe_m no_fnugg no_lifetime no_logues no_mv no_pipe no_poll no_readme no_robots no_sb_md no_sb_lg no_scandir no_tail no_tarcmp no_thumb no_vthumb no_u2abrt no_zip no_zls nrand nsort nw og og_no_head og_s_title ohead opds q rand re_dirsz reflink rm_partial rmagic rss smb srch_dbg srch_excl srch_icase stats ui_noacci ui_nocpla ui_noctxb ui_nolbar ui_nombar ui_nonav ui_notree ui_norepl ui_nosrvi uqe usernames vague_403 vc ver wo_up_readme write_uplog xdev xlink xvol zipmaxu zs" + ex = "allow_flac allow_wav chpw cookie_lax daw dav_auth dav_mac dav_rt dlni e2d e2ds e2dsa e2t e2ts e2tsr e2v e2vu e2vp early_ban ed emp exp force_js getmod grid gsel hardlink hardlink_only http_no_tcp ih ihead localtime log_badxml magic md_no_br nid nih no_acode no_athumb no_bauth no_clone no_cp no_dav no_db_ip no_del no_dirsz no_dupe no_dupe_m no_fnugg no_lifetime no_logues no_mv no_pipe no_poll no_readme no_robots no_sb_md no_sb_lg no_scandir no_tail no_tarcmp no_thumb no_vthumb no_u2abrt no_zip no_zls nrand nsort nw og og_no_head og_s_title ohead opds q rand re_dirsz reflink rm_partial rmagic rss smb srch_dbg srch_excl srch_icase stats ui_noacci ui_nocpla ui_noctxb ui_nolbar ui_nombar ui_nonav ui_notree ui_norepl ui_nosrvi uqe usernames vague_403 vc ver vol_nospawn vol_or_crash wo_up_readme write_uplog xdev xlink xvol zipmaxu zs" ka.update(**{k: False for k in ex.split()}) ex = "dav_inf dedup dotpart dotsrch hook_v no_dhash no_fastboot no_fpool no_htp no_rescan no_sendfile no_ses no_snap no_up_list no_voldump wram re_dhash see_dots plain_ip"