per-volume uid/gid; closes #265

This commit is contained in:
ed 2025-07-30 19:35:00 +00:00
parent a9d07c63ed
commit f195998865
13 changed files with 120 additions and 51 deletions

View file

@ -80,6 +80,7 @@ made in Norway 🇳🇴
* [periodic rescan](#periodic-rescan) - filesystem monitoring * [periodic rescan](#periodic-rescan) - filesystem monitoring
* [upload rules](#upload-rules) - set upload rules using volflags * [upload rules](#upload-rules) - set upload rules using volflags
* [compress uploads](#compress-uploads) - files can be autocompressed on upload * [compress uploads](#compress-uploads) - files can be autocompressed on upload
* [chmod and chown](#chmod-and-chown) - per-volume filesystem-permissions and ownership
* [other flags](#other-flags) * [other flags](#other-flags)
* [database location](#database-location) - in-volume (`.hist/up2k.db`, default) or somewhere else * [database location](#database-location) - in-volume (`.hist/up2k.db`, default) or somewhere else
* [metadata from audio files](#metadata-from-audio-files) - set `-e2t` to index tags on upload * [metadata from audio files](#metadata-from-audio-files) - set `-e2t` to index tags on upload
@ -1649,6 +1650,26 @@ some examples,
allows (but does not force) gz compression if client uploads to `/inc?pk` or `/inc?gz` or `/inc?gz=4` allows (but does not force) gz compression if client uploads to `/inc?pk` or `/inc?gz` or `/inc?gz=4`
## chmod and chown
per-volume filesystem-permissions and ownership
by default:
* all folders are chmod 755
* files are usually chmod 644 (umask-defined)
* user/group is whatever copyparty is running as
this can be configured per-volume:
* volflag `chmod_f` sets file permissions; default=`644` (usually)
* volflag `chmod_d` sets directory permissions; default=`755`
* volflag `uid` sets the owner user-id
* volflag `gid` sets the owner group-id
notes:
* `gid` can only be set to one of the groups which the copyparty process is a member of
* `uid` can only be set if copyparty is running as root (i appreciate your faith)
## other flags ## other flags
* `:c,magic` enables filetype detection for nameless uploads, same as `--magic` * `:c,magic` enables filetype detection for nameless uploads, same as `--magic`

View file

@ -1053,6 +1053,8 @@ def add_upload(ap):
ap2.add_argument("--use-fpool", action="store_true", help="force file-handle pooling, even when it might be dangerous (multiprocessing, filesystems lacking sparse-files support, ...)") ap2.add_argument("--use-fpool", action="store_true", help="force file-handle pooling, even when it might be dangerous (multiprocessing, filesystems lacking sparse-files support, ...)")
ap2.add_argument("--chmod-f", metavar="UGO", type=u, default="", help="unix file permissions to use when creating files; default is probably 644 (OS-decided), see --help-chmod. Examples: [\033[32m644\033[0m] = owner-RW + all-R, [\033[32m755\033[0m] = owner-RWX + all-RX, [\033[32m777\033[0m] = full-yolo (volflag=chmod_f)") ap2.add_argument("--chmod-f", metavar="UGO", type=u, default="", help="unix file permissions to use when creating files; default is probably 644 (OS-decided), see --help-chmod. Examples: [\033[32m644\033[0m] = owner-RW + all-R, [\033[32m755\033[0m] = owner-RWX + all-RX, [\033[32m777\033[0m] = full-yolo (volflag=chmod_f)")
ap2.add_argument("--chmod-d", metavar="UGO", type=u, default="755", help="unix file permissions to use when creating directories; see --help-chmod. Examples: [\033[32m755\033[0m] = owner-RW + all-R, [\033[32m777\033[0m] = full-yolo (volflag=chmod_d)") ap2.add_argument("--chmod-d", metavar="UGO", type=u, default="755", help="unix file permissions to use when creating directories; see --help-chmod. Examples: [\033[32m755\033[0m] = owner-RW + all-R, [\033[32m777\033[0m] = full-yolo (volflag=chmod_d)")
ap2.add_argument("--uid", metavar="N", type=int, default=-1, help="unix user-id to chown new files/folders to; default = -1 = do-not-change (volflag=uid)")
ap2.add_argument("--gid", metavar="N", type=int, default=-1, help="unix group-id to chown new files/folders to; default = -1 = do-not-change (volflag=gid)")
ap2.add_argument("--dedup", action="store_true", help="enable symlink-based upload deduplication (volflag=dedup)") ap2.add_argument("--dedup", action="store_true", help="enable symlink-based upload deduplication (volflag=dedup)")
ap2.add_argument("--safe-dedup", metavar="N", type=int, default=50, help="how careful to be when deduplicating files; [\033[32m1\033[0m] = just verify the filesize, [\033[32m50\033[0m] = verify file contents have not been altered (volflag=safededup)") ap2.add_argument("--safe-dedup", metavar="N", type=int, default=50, help="how careful to be when deduplicating files; [\033[32m1\033[0m] = just verify the filesize, [\033[32m50\033[0m] = verify file contents have not been altered (volflag=safededup)")
ap2.add_argument("--hardlink", action="store_true", help="enable hardlink-based dedup; will fallback on symlinks when that is impossible (across filesystems) (volflag=hardlink)") ap2.add_argument("--hardlink", action="store_true", help="enable hardlink-based dedup; will fallback on symlinks when that is impossible (across filesystems) (volflag=hardlink)")

View file

@ -140,6 +140,8 @@ class Lim(object):
self.reg: Optional[dict[str, dict[str, Any]]] = None # up2k registry self.reg: Optional[dict[str, dict[str, Any]]] = None # up2k registry
self.chmod_d = 0o755 self.chmod_d = 0o755
self.uid = self.gid = -1
self.chown = False
self.nups: dict[str, list[float]] = {} # num tracker self.nups: dict[str, list[float]] = {} # num tracker
self.bups: dict[str, list[tuple[float, int]]] = {} # byte tracker list self.bups: dict[str, list[tuple[float, int]]] = {} # byte tracker list
@ -302,6 +304,8 @@ class Lim(object):
# no branches yet; make one # no branches yet; make one
sub = os.path.join(path, "0") sub = os.path.join(path, "0")
bos.mkdir(sub, self.chmod_d) bos.mkdir(sub, self.chmod_d)
if self.chown:
os.chown(sub, self.uid, self.gid)
else: else:
# try newest branch only # try newest branch only
sub = os.path.join(path, str(dirs[-1])) sub = os.path.join(path, str(dirs[-1]))
@ -317,6 +321,8 @@ class Lim(object):
# make a branch # make a branch
sub = os.path.join(path, str(dirs[-1] + 1)) sub = os.path.join(path, str(dirs[-1] + 1))
bos.mkdir(sub, self.chmod_d) bos.mkdir(sub, self.chmod_d)
if self.chown:
os.chown(sub, self.uid, self.gid)
ret = self.dive(sub, lvs - 1) ret = self.dive(sub, lvs - 1)
if ret is None: if ret is None:
raise Pebkac(500, "rotation bug") raise Pebkac(500, "rotation bug")
@ -2181,7 +2187,7 @@ class AuthSrv(object):
if vf not in vol.flags: if vf not in vol.flags:
vol.flags[vf] = getattr(self.args, ga) vol.flags[vf] = getattr(self.args, ga)
zs = "forget_ip nrand tail_who u2abort u2ow ups_who zip_who" zs = "forget_ip gid nrand tail_who u2abort u2ow uid ups_who zip_who"
for k in zs.split(): for k in zs.split():
if k in vol.flags: if k in vol.flags:
vol.flags[k] = int(vol.flags[k]) vol.flags[k] = int(vol.flags[k])
@ -2218,8 +2224,17 @@ class AuthSrv(object):
if (is_d and zi != 0o755) or not is_d: if (is_d and zi != 0o755) or not is_d:
free_umask = True free_umask = True
vol.flags.pop("chown", None)
if vol.flags["uid"] != -1 or vol.flags["gid"] != -1:
vol.flags["chown"] = True
vol.flags.pop("fperms", None)
if "chown" in vol.flags or vol.flags.get("chmod_f"):
vol.flags["fperms"] = True
if vol.lim: if vol.lim:
vol.lim.chmod_d = vol.flags["chmod_d"] vol.lim.chmod_d = vol.flags["chmod_d"]
vol.lim.chown = "chown" in vol.flags
vol.lim.uid = vol.flags["uid"]
vol.lim.gid = vol.flags["gid"]
if vol.flags.get("og"): if vol.flags.get("og"):
self.args.uqe = True self.args.uqe = True

View file

@ -9,8 +9,11 @@ from . import path as path
if True: # pylint: disable=using-constant-test if True: # pylint: disable=using-constant-test
from typing import Any, Optional from typing import Any, Optional
_ = (path,) MKD_755 = {"chmod_d": 0o755}
__all__ = ["path"] MKD_700 = {"chmod_d": 0o700}
_ = (path, MKD_755, MKD_700)
__all__ = ["path", "MKD_755", "MKD_700"]
# grep -hRiE '(^|[^a-zA-Z_\.-])os\.' . | gsed -r 's/ /\n/g;s/\(/(\n/g' | grep -hRiE '(^|[^a-zA-Z_\.-])os\.' | sort | uniq -c # grep -hRiE '(^|[^a-zA-Z_\.-])os\.' . | gsed -r 's/ /\n/g;s/\(/(\n/g' | grep -hRiE '(^|[^a-zA-Z_\.-])os\.' | sort | uniq -c
# printf 'os\.(%s)' "$(grep ^def bos/__init__.py | gsed -r 's/^def //;s/\(.*//' | tr '\n' '|' | gsed -r 's/.$//')" # printf 'os\.(%s)' "$(grep ^def bos/__init__.py | gsed -r 's/^def //;s/\(.*//' | tr '\n' '|' | gsed -r 's/.$//')"
@ -20,11 +23,15 @@ def chmod(p: str, mode: int) -> None:
return os.chmod(fsenc(p), mode) return os.chmod(fsenc(p), mode)
def chown(p: str, uid: int, gid: int) -> None:
return os.chown(fsenc(p), uid, gid)
def listdir(p: str = ".") -> list[str]: def listdir(p: str = ".") -> list[str]:
return [fsdec(x) for x in os.listdir(fsenc(p))] return [fsdec(x) for x in os.listdir(fsenc(p))]
def makedirs(name: str, mode: int = 0o755, exist_ok: bool = True) -> bool: def makedirs(name: str, vf: dict[str, Any] = MKD_755, exist_ok: bool = True) -> bool:
# os.makedirs does 777 for all but leaf; this does mode on all # os.makedirs does 777 for all but leaf; this does mode on all
todo = [] todo = []
bname = fsenc(name) bname = fsenc(name)
@ -37,9 +44,13 @@ def makedirs(name: str, mode: int = 0o755, exist_ok: bool = True) -> bool:
if not exist_ok: if not exist_ok:
os.mkdir(bname) # to throw os.mkdir(bname) # to throw
return False return False
mode = vf["chmod_d"]
chown = "chown" in vf
for zb in todo[::-1]: for zb in todo[::-1]:
try: try:
os.mkdir(zb, mode) os.mkdir(zb, mode)
if chown:
os.chown(zb, vf["uid"], vf["gid"])
except: except:
if os.path.isdir(zb): if os.path.isdir(zb):
continue continue

View file

@ -114,6 +114,8 @@ def vf_vmap() -> dict[str, str]:
"unlist", "unlist",
"u2abort", "u2abort",
"u2ts", "u2ts",
"uid",
"gid",
"ups_who", "ups_who",
"zip_who", "zip_who",
"zipmaxn", "zipmaxn",
@ -175,6 +177,8 @@ flagcats = {
"nodupe": "rejects existing files (instead of linking/cloning them)", "nodupe": "rejects existing files (instead of linking/cloning them)",
"chmod_d=755": "unix-permission for new dirs/folders", "chmod_d=755": "unix-permission for new dirs/folders",
"chmod_f=644": "unix-permission for new files", "chmod_f=644": "unix-permission for new files",
"uid=573": "change owner of new files/folders to unix-user 573",
"gid=999": "change owner of new files/folders to unix-group 999",
"sparse": "force use of sparse files, mainly for s3-backed storage", "sparse": "force use of sparse files, mainly for s3-backed storage",
"nosparse": "deny use of sparse files, mainly for slow storage", "nosparse": "deny use of sparse files, mainly for slow storage",
"daw": "enable full WebDAV write support (dangerous);\nPUT-operations will now \033[1;31mOVERWRITE\033[0;35m existing files", "daw": "enable full WebDAV write support (dangerous);\nPUT-operations will now \033[1;31mOVERWRITE\033[0;35m existing files",

View file

@ -31,6 +31,7 @@ from .util import (
relchk, relchk,
runhook, runhook,
sanitize_fn, sanitize_fn,
set_fperms,
vjoin, vjoin,
wunlink, wunlink,
) )
@ -262,8 +263,8 @@ class FtpFs(AbstractedFS):
wunlink(self.log, ap, VF_CAREFUL) wunlink(self.log, ap, VF_CAREFUL)
ret = open(fsenc(ap), mode, self.args.iobuf) ret = open(fsenc(ap), mode, self.args.iobuf)
if w and "chmod_f" in vfs.flags: if w and "fperms" in vfs.flags:
os.fchmod(ret.fileno(), vfs.flags["chmod_f"]) set_fperms(ret, vfs.flags)
return ret return ret
@ -297,8 +298,7 @@ class FtpFs(AbstractedFS):
def mkdir(self, path: str) -> None: def mkdir(self, path: str) -> None:
ap, vfs, _ = self.rv2a(path, w=True) ap, vfs, _ = self.rv2a(path, w=True)
chmod = vfs.flags["chmod_d"] bos.makedirs(ap, vf=vfs.flags) # filezilla expects this
bos.makedirs(ap, chmod) # filezilla expects this
def listdir(self, path: str) -> list[str]: def listdir(self, path: str) -> list[str]:
vpath = join(self.cwd, path) vpath = join(self.cwd, path)

View file

@ -103,6 +103,7 @@ from .util import (
sanitize_vpath, sanitize_vpath,
sendfile_kern, sendfile_kern,
sendfile_py, sendfile_py,
set_fperms,
stat_resource, stat_resource,
ub64dec, ub64dec,
ub64enc, ub64enc,
@ -2086,7 +2087,7 @@ class HttpCli(object):
fdir, fn = os.path.split(fdir) fdir, fn = os.path.split(fdir)
rem, _ = vsplit(rem) rem, _ = vsplit(rem)
bos.makedirs(fdir, vfs.flags["chmod_d"]) bos.makedirs(fdir, vf=vfs.flags)
open_ka: dict[str, Any] = {"fun": open} open_ka: dict[str, Any] = {"fun": open}
open_a = ["wb", self.args.iobuf] open_a = ["wb", self.args.iobuf]
@ -2144,9 +2145,7 @@ class HttpCli(object):
if nameless: if nameless:
fn = vfs.flags["put_name2"].format(now=time.time(), cip=self.dip()) fn = vfs.flags["put_name2"].format(now=time.time(), cip=self.dip())
params = {"suffix": suffix, "fdir": fdir} params = {"suffix": suffix, "fdir": fdir, "vf": vfs.flags}
if "chmod_f" in vfs.flags:
params["chmod"] = vfs.flags["chmod_f"]
if self.args.nw: if self.args.nw:
params = {} params = {}
fn = os.devnull fn = os.devnull
@ -2195,7 +2194,7 @@ class HttpCli(object):
if self.args.nw: if self.args.nw:
fn = os.devnull fn = os.devnull
else: else:
bos.makedirs(fdir, vfs.flags["chmod_d"]) bos.makedirs(fdir, vf=vfs.flags)
path = os.path.join(fdir, fn) path = os.path.join(fdir, fn)
if not nameless: if not nameless:
self.vpath = vjoin(self.vpath, fn) self.vpath = vjoin(self.vpath, fn)
@ -2327,7 +2326,7 @@ class HttpCli(object):
if self.args.hook_v: if self.args.hook_v:
log_reloc(self.log, hr["reloc"], x, path, vp, fn, vfs, rem) log_reloc(self.log, hr["reloc"], x, path, vp, fn, vfs, rem)
fdir, self.vpath, fn, (vfs, rem) = x fdir, self.vpath, fn, (vfs, rem) = x
bos.makedirs(fdir, vfs.flags["chmod_d"]) bos.makedirs(fdir, vf=vfs.flags)
path2 = os.path.join(fdir, fn) path2 = os.path.join(fdir, fn)
atomic_move(self.log, path, path2, vfs.flags) atomic_move(self.log, path, path2, vfs.flags)
path = path2 path = path2
@ -2613,7 +2612,7 @@ class HttpCli(object):
dst = vfs.canonical(rem) dst = vfs.canonical(rem)
try: try:
if not bos.path.isdir(dst): if not bos.path.isdir(dst):
bos.makedirs(dst, vfs.flags["chmod_d"]) bos.makedirs(dst, vf=vfs.flags)
except OSError as ex: except OSError as ex:
self.log("makedirs failed %r" % (dst,)) self.log("makedirs failed %r" % (dst,))
if not bos.path.isdir(dst): if not bos.path.isdir(dst):
@ -3060,7 +3059,7 @@ class HttpCli(object):
raise Pebkac(405, 'folder "/%s" already exists' % (vpath,)) raise Pebkac(405, 'folder "/%s" already exists' % (vpath,))
try: try:
bos.makedirs(fn, vfs.flags["chmod_d"]) bos.makedirs(fn, vf=vfs.flags)
except OSError as ex: except OSError as ex:
if ex.errno == errno.EACCES: if ex.errno == errno.EACCES:
raise Pebkac(500, "the server OS denied write-access") raise Pebkac(500, "the server OS denied write-access")
@ -3102,8 +3101,8 @@ class HttpCli(object):
with open(fsenc(fn), "wb") as f: with open(fsenc(fn), "wb") as f:
f.write(b"`GRUNNUR`\n") f.write(b"`GRUNNUR`\n")
if "chmod_f" in vfs.flags: if "fperms" in vfs.flags:
os.fchmod(f.fileno(), vfs.flags["chmod_f"]) set_fperms(f, vfs.flags)
vpath = "{}/{}".format(self.vpath, sanitized).lstrip("/") vpath = "{}/{}".format(self.vpath, sanitized).lstrip("/")
self.redirect(vpath, "?edit") self.redirect(vpath, "?edit")
@ -3177,7 +3176,7 @@ class HttpCli(object):
) )
upload_vpath = "{}/{}".format(vfs.vpath, rem).strip("/") upload_vpath = "{}/{}".format(vfs.vpath, rem).strip("/")
if not nullwrite: if not nullwrite:
bos.makedirs(fdir_base, vfs.flags["chmod_d"]) bos.makedirs(fdir_base, vf=vfs.flags)
rnd, lifetime, xbu, xau = self.upload_flags(vfs) rnd, lifetime, xbu, xau = self.upload_flags(vfs)
zs = self.uparam.get("want") or self.headers.get("accept") or "" zs = self.uparam.get("want") or self.headers.get("accept") or ""
@ -3210,7 +3209,7 @@ class HttpCli(object):
if rnd: if rnd:
fname = rand_name(fdir, fname, rnd) fname = rand_name(fdir, fname, rnd)
open_args = {"fdir": fdir, "suffix": suffix} open_args = {"fdir": fdir, "suffix": suffix, "vf": vfs.flags}
if "replace" in self.uparam: if "replace" in self.uparam:
if not self.can_delete: if not self.can_delete:
@ -3272,11 +3271,8 @@ class HttpCli(object):
else: else:
open_args["fdir"] = fdir open_args["fdir"] = fdir
if "chmod_f" in vfs.flags:
open_args["chmod"] = vfs.flags["chmod_f"]
if p_file and not nullwrite: if p_file and not nullwrite:
bos.makedirs(fdir, vfs.flags["chmod_d"]) bos.makedirs(fdir, vf=vfs.flags)
# reserve destination filename # reserve destination filename
f, fname = ren_open(fname, "wb", fdir=fdir, suffix=suffix) f, fname = ren_open(fname, "wb", fdir=fdir, suffix=suffix)
@ -3380,7 +3376,7 @@ class HttpCli(object):
if nullwrite: if nullwrite:
fdir = ap2 = "" fdir = ap2 = ""
else: else:
bos.makedirs(fdir, vfs.flags["chmod_d"]) bos.makedirs(fdir, vf=vfs.flags)
atomic_move(self.log, abspath, ap2, vfs.flags) atomic_move(self.log, abspath, ap2, vfs.flags)
abspath = ap2 abspath = ap2
sz = bos.path.getsize(abspath) sz = bos.path.getsize(abspath)
@ -3501,8 +3497,8 @@ class HttpCli(object):
ft = "{}:{}".format(self.ip, self.addr[1]) ft = "{}:{}".format(self.ip, self.addr[1])
ft = "{}\n{}\n{}\n".format(ft, msg.rstrip(), errmsg) ft = "{}\n{}\n{}\n".format(ft, msg.rstrip(), errmsg)
f.write(ft.encode("utf-8")) f.write(ft.encode("utf-8"))
if "chmod_f" in vfs.flags: if "fperms" in vfs.flags:
os.fchmod(f.fileno(), vfs.flags["chmod_f"]) set_fperms(f, vfs.flags)
except Exception as ex: except Exception as ex:
suf = "\nfailed to write the upload report: {}".format(ex) suf = "\nfailed to write the upload report: {}".format(ex)
@ -3553,7 +3549,7 @@ class HttpCli(object):
lim = vfs.get_dbv(rem)[0].lim lim = vfs.get_dbv(rem)[0].lim
if lim: if lim:
fp, rp = lim.all(self.ip, rp, clen, vfs.realpath, fp, self.conn.hsrv.broker) fp, rp = lim.all(self.ip, rp, clen, vfs.realpath, fp, self.conn.hsrv.broker)
bos.makedirs(fp, vfs.flags["chmod_d"]) bos.makedirs(fp, vf=vfs.flags)
fp = os.path.join(fp, fn) fp = os.path.join(fp, fn)
rem = "{}/{}".format(rp, fn).strip("/") rem = "{}/{}".format(rp, fn).strip("/")
@ -3621,15 +3617,17 @@ class HttpCli(object):
zs = ub64enc(zb).decode("ascii")[:24].lower() zs = ub64enc(zb).decode("ascii")[:24].lower()
dp = "%s/md/%s/%s/%s" % (dbv.histpath, zs[:2], zs[2:4], zs) dp = "%s/md/%s/%s/%s" % (dbv.histpath, zs[:2], zs[2:4], zs)
self.log("moving old version to %s/%s" % (dp, mfile2)) self.log("moving old version to %s/%s" % (dp, mfile2))
if bos.makedirs(dp, vfs.flags["chmod_d"]): if bos.makedirs(dp, vf=vfs.flags):
with open(os.path.join(dp, "dir.txt"), "wb") as f: with open(os.path.join(dp, "dir.txt"), "wb") as f:
f.write(afsenc(vrd)) f.write(afsenc(vrd))
if "chmod_f" in vfs.flags: if "fperms" in vfs.flags:
os.fchmod(f.fileno(), vfs.flags["chmod_f"]) set_fperms(f, vfs.flags)
elif hist_cfg == "s": elif hist_cfg == "s":
dp = os.path.join(mdir, ".hist") dp = os.path.join(mdir, ".hist")
try: try:
bos.mkdir(dp, vfs.flags["chmod_d"]) bos.mkdir(dp, vfs.flags["chmod_d"])
if "chown" in vfs.flags:
bos.chown(dp, vfs.flags["uid"], vfs.flags["gid"])
hidedir(dp) hidedir(dp)
except: except:
pass pass
@ -3668,8 +3666,8 @@ class HttpCli(object):
wunlink(self.log, fp, vfs.flags) wunlink(self.log, fp, vfs.flags)
with open(fsenc(fp), "wb", self.args.iobuf) as f: with open(fsenc(fp), "wb", self.args.iobuf) as f:
if "chmod_f" in vfs.flags: if "fperms" in vfs.flags:
os.fchmod(f.fileno(), vfs.flags["chmod_f"]) set_fperms(f, vfs.flags)
sz, sha512, _ = hashcopy(p_data, f, None, 0, self.args.s_wr_slp) sz, sha512, _ = hashcopy(p_data, f, None, 0, self.args.s_wr_slp)
if lim: if lim:

View file

@ -320,7 +320,7 @@ class SMB(object):
self.hub.up2k.handle_mv(uname, "1.7.6.2", vp1, vp2) self.hub.up2k.handle_mv(uname, "1.7.6.2", vp1, vp2)
try: try:
bos.makedirs(ap2, vfs2.flags["chmod_d"]) bos.makedirs(ap2, vf=vfs2.flags)
except: except:
pass pass

View file

@ -45,6 +45,7 @@ from .util import (
exclude_dotfiles, exclude_dotfiles,
min_ex, min_ex,
runhook, runhook,
set_fperms,
undot, undot,
vjoin, vjoin,
vsplit, vsplit,
@ -388,8 +389,8 @@ class Tftpd(object):
a = (self.args.iobuf,) a = (self.args.iobuf,)
ret = open(ap, mode, *a, **ka) ret = open(ap, mode, *a, **ka)
if wr and "chmod_f" in vfs.flags: if wr and "fperms" in vfs.flags:
os.fchmod(ret.fileno(), vfs.flags["chmod_f"]) set_fperms(ret, vfs.flags)
return ret return ret
@ -398,7 +399,9 @@ class Tftpd(object):
if "*" not in vfs.axs.uwrite: if "*" not in vfs.axs.uwrite:
yeet("blocked mkdir; folder not world-writable: /%s" % (vpath,)) yeet("blocked mkdir; folder not world-writable: /%s" % (vpath,))
return bos.mkdir(ap, vfs.flags["chmod_d"]) bos.mkdir(ap, vfs.flags["chmod_d"])
if "chown" in vfs.flags:
bos.chown(ap, vfs.flags["uid"], vfs.flags["gid"])
def _unlink(self, vpath: str) -> None: def _unlink(self, vpath: str) -> None:
# return bos.unlink(self._v2a("stat", vpath, *a)[1]) # return bos.unlink(self._v2a("stat", vpath, *a)[1])

View file

@ -269,8 +269,8 @@ class ThumbSrv(object):
self.log("joined waiting room for %r" % (tpath,)) self.log("joined waiting room for %r" % (tpath,))
except: except:
thdir = os.path.dirname(tpath) thdir = os.path.dirname(tpath)
chmod = 0o700 if self.args.free_umask else 0o755 chmod = bos.MKD_700 if self.args.free_umask else bos.MKD_755
bos.makedirs(os.path.join(thdir, "w"), chmod) bos.makedirs(os.path.join(thdir, "w"), vf=chmod)
inf_path = os.path.join(thdir, "dir.txt") inf_path = os.path.join(thdir, "dir.txt")
if not bos.path.exists(inf_path): if not bos.path.exists(inf_path):

View file

@ -916,7 +916,7 @@ class Up2k(object):
for vol in vols: for vol in vols:
try: try:
# mkdir gonna happen at snap anyways; # mkdir gonna happen at snap anyways;
bos.makedirs(vol.realpath, vol.flags["chmod_d"]) bos.makedirs(vol.realpath, vf=vol.flags)
dir_is_empty(self.log_func, not self.args.no_scandir, vol.realpath) dir_is_empty(self.log_func, not self.args.no_scandir, vol.realpath)
except Exception as ex: except Exception as ex:
self.volstate[vol.vpath] = "OFFLINE (cannot access folder)" self.volstate[vol.vpath] = "OFFLINE (cannot access folder)"
@ -3309,7 +3309,7 @@ class Up2k(object):
reg, reg,
"up2k._get_volsize", "up2k._get_volsize",
) )
bos.makedirs(ap2, vfs.flags["chmod_d"]) bos.makedirs(ap2, vf=vfs.flags)
vfs.lim.nup(cj["addr"]) vfs.lim.nup(cj["addr"])
vfs.lim.bup(cj["addr"], cj["size"]) vfs.lim.bup(cj["addr"], cj["size"])
@ -3445,7 +3445,7 @@ class Up2k(object):
"wb", "wb",
fdir=fdir, fdir=fdir,
suffix="-%.6f-%s" % (ts, dip), suffix="-%.6f-%s" % (ts, dip),
chmod=vf.get("chmod_f", -1), vf=vf,
) )
f.close() f.close()
return ret return ret
@ -4304,7 +4304,7 @@ class Up2k(object):
self.log(t, 1) self.log(t, 1)
raise Pebkac(405, t) raise Pebkac(405, t)
bos.makedirs(os.path.dirname(dabs), dvn.flags["chmod_d"]) bos.makedirs(os.path.dirname(dabs), vf=dvn.flags)
c1, w, ftime_, fsize_, ip, at = self._find_from_vpath( c1, w, ftime_, fsize_, ip, at = self._find_from_vpath(
svn_dbv.realpath, srem_dbv svn_dbv.realpath, srem_dbv
@ -4480,7 +4480,10 @@ class Up2k(object):
vp = vjoin(dvp, rem) vp = vjoin(dvp, rem)
try: try:
dvn, drem = self.vfs.get(vp, uname, False, True) dvn, drem = self.vfs.get(vp, uname, False, True)
bos.mkdir(dvn.canonical(drem), dvn.flags["chmod_d"]) dap = dvn.canonical(drem)
bos.mkdir(dap, dvn.flags["chmod_d"])
if "chown" in dvn.flags:
bos.chown(dap, dvn.flags["uid"], dvn.flags["gid"])
except: except:
pass pass
@ -4550,7 +4553,7 @@ class Up2k(object):
is_xvol = svn.realpath != dvn.realpath is_xvol = svn.realpath != dvn.realpath
bos.makedirs(os.path.dirname(dabs), dvn.flags["chmod_d"]) bos.makedirs(os.path.dirname(dabs), vf=dvn.flags)
if is_dirlink: if is_dirlink:
dlabs = absreal(sabs) dlabs = absreal(sabs)
@ -5062,7 +5065,7 @@ class Up2k(object):
"wb", "wb",
fdir=pdir, fdir=pdir,
suffix="-%.6f-%s" % (job["t0"], dip), suffix="-%.6f-%s" % (job["t0"], dip),
chmod=vf.get("chmod_f", -1), vf=vf,
) )
try: try:
abspath = djoin(pdir, job["tnam"]) abspath = djoin(pdir, job["tnam"])

View file

@ -1587,7 +1587,8 @@ def ren_open(fname: str, *args: Any, **kwargs: Any) -> tuple[typing.IO[Any], str
fun = kwargs.pop("fun", open) fun = kwargs.pop("fun", open)
fdir = kwargs.pop("fdir", None) fdir = kwargs.pop("fdir", None)
suffix = kwargs.pop("suffix", None) suffix = kwargs.pop("suffix", None)
chmod = kwargs.pop("chmod", -1) vf = kwargs.pop("vf", None)
fperms = vf and "fperms" in vf
if fname == os.devnull: if fname == os.devnull:
return fun(fname, *args, **kwargs), fname return fun(fname, *args, **kwargs), fname
@ -1631,11 +1632,11 @@ def ren_open(fname: str, *args: Any, **kwargs: Any) -> tuple[typing.IO[Any], str
fp2 = os.path.join(fdir, fp2) fp2 = os.path.join(fdir, fp2)
with open(fsenc(fp2), "wb") as f2: with open(fsenc(fp2), "wb") as f2:
f2.write(orig_name.encode("utf-8")) f2.write(orig_name.encode("utf-8"))
if chmod >= 0: if fperms:
os.fchmod(f2.fileno(), chmod) set_fperms(f2, vf)
if chmod >= 0: if fperms:
os.fchmod(f.fileno(), chmod) set_fperms(f, vf)
return f, fname return f, fname
@ -2565,6 +2566,14 @@ def lsof(log: "NamedLogger", abspath: str) -> None:
log("lsof failed; " + min_ex(), 3) log("lsof failed; " + min_ex(), 3)
def set_fperms(f: Union[typing.BinaryIO, typing.IO[Any]], vf: dict[str, Any]) -> None:
fno = f.fileno()
if "chmod_f" in vf:
os.fchmod(fno, vf["chmod_f"])
if "chown" in vf:
os.fchown(fno, vf["uid"], vf["gid"])
def _fs_mvrm( def _fs_mvrm(
log: "NamedLogger", src: str, dst: str, atomic: bool, flags: dict[str, Any] log: "NamedLogger", src: str, dst: str, atomic: bool, flags: dict[str, Any]
) -> bool: ) -> bool:

View file

@ -152,6 +152,9 @@ class Cfg(Namespace):
ex = "ah_cli ah_gen css_browser dbpath hist ipu js_browser js_other mime mimes no_forget no_hash no_idx nonsus_urls og_tpl og_ua ua_nodoc ua_nozip" ex = "ah_cli ah_gen css_browser dbpath hist ipu js_browser js_other mime mimes no_forget no_hash no_idx nonsus_urls og_tpl og_ua ua_nodoc ua_nozip"
ka.update(**{k: None for k in ex.split()}) ka.update(**{k: None for k in ex.split()})
ex = "gid uid"
ka.update(**{k: -1 for k in ex.split()})
ex = "hash_mt hsortn qdel safe_dedup srch_time tail_fd tail_rate u2abort u2j u2sz" ex = "hash_mt hsortn qdel safe_dedup srch_time tail_fd tail_rate u2abort u2j u2sz"
ka.update(**{k: 1 for k in ex.split()}) ka.update(**{k: 1 for k in ex.split()})