bubble OS-filesystem errors to client

send a 500 or 404 if a folder is inaccessible or does not exist

previously it would return an empty directory listing instead
This commit is contained in:
ed 2024-11-02 17:38:17 +00:00
parent 71d9e010d9
commit 119e88d87b
9 changed files with 27 additions and 9 deletions

View file

@ -393,7 +393,8 @@ class Gateway(object):
if r.status != 200: if r.status != 200:
self.closeconn() self.closeconn()
info("http error %s reading dir %r", r.status, web_path) info("http error %s reading dir %r", r.status, web_path)
raise FuseOSError(errno.ENOENT) err = errno.ENOENT if r.status == 404 else errno.EIO
raise FuseOSError(err)
ctype = r.getheader("Content-Type", "") ctype = r.getheader("Content-Type", "")
if ctype == "application/json": if ctype == "application/json":

View file

@ -1142,7 +1142,10 @@ class Ctl(object):
if self.ar.drd: if self.ar.drd:
dp = os.path.join(top, rd) dp = os.path.join(top, rd)
lnodes = set(os.listdir(dp)) try:
lnodes = set(os.listdir(dp))
except:
lnodes = list(ls) # fs eio; don't delete
if ptn: if ptn:
zs = dp.replace(sep, b"/").rstrip(b"/") + b"/" zs = dp.replace(sep, b"/").rstrip(b"/") + b"/"
zls = [zs + x for x in lnodes] zls = [zs + x for x in lnodes]

View file

@ -591,10 +591,11 @@ class VFS(object):
scandir: bool, scandir: bool,
permsets: list[list[bool]], permsets: list[list[bool]],
lstat: bool = False, lstat: bool = False,
throw: bool = False,
) -> tuple[str, list[tuple[str, os.stat_result]], dict[str, "VFS"]]: ) -> tuple[str, list[tuple[str, os.stat_result]], dict[str, "VFS"]]:
"""replaces _ls for certain shares (single-file, or file selection)""" """replaces _ls for certain shares (single-file, or file selection)"""
vn, rem = self.shr_src # type: ignore vn, rem = self.shr_src # type: ignore
abspath, real, _ = vn.ls(rem, "\n", scandir, permsets, lstat) abspath, real, _ = vn.ls(rem, "\n", scandir, permsets, lstat, throw)
real = [x for x in real if os.path.basename(x[0]) in self.shr_files] real = [x for x in real if os.path.basename(x[0]) in self.shr_files]
return abspath, real, {} return abspath, real, {}
@ -605,11 +606,12 @@ class VFS(object):
scandir: bool, scandir: bool,
permsets: list[list[bool]], permsets: list[list[bool]],
lstat: bool = False, lstat: bool = False,
throw: bool = False,
) -> tuple[str, list[tuple[str, os.stat_result]], dict[str, "VFS"]]: ) -> tuple[str, list[tuple[str, os.stat_result]], dict[str, "VFS"]]:
"""return user-readable [fsdir,real,virt] items at vpath""" """return user-readable [fsdir,real,virt] items at vpath"""
virt_vis = {} # nodes readable by user virt_vis = {} # nodes readable by user
abspath = self.canonical(rem) abspath = self.canonical(rem)
real = list(statdir(self.log, scandir, lstat, abspath)) real = list(statdir(self.log, scandir, lstat, abspath, throw))
real.sort() real.sort()
if not rem: if not rem:
# no vfs nodes in the list of real inodes # no vfs nodes in the list of real inodes

View file

@ -296,6 +296,7 @@ class FtpFs(AbstractedFS):
self.uname, self.uname,
not self.args.no_scandir, not self.args.no_scandir,
[[True, False], [False, True]], [[True, False], [False, True]],
throw=True,
) )
vfs_ls = [x[0] for x in vfs_ls1] vfs_ls = [x[0] for x in vfs_ls1]
vfs_ls.extend(vfs_virt.keys()) vfs_ls.extend(vfs_virt.keys())

View file

@ -1450,6 +1450,7 @@ class HttpCli(object):
not self.args.no_scandir, not self.args.no_scandir,
[[True, False]], [[True, False]],
lstat="davrt" not in vn.flags, lstat="davrt" not in vn.flags,
throw=True,
) )
if not self.can_read: if not self.can_read:
vfs_ls = [] vfs_ls = []
@ -5459,6 +5460,7 @@ class HttpCli(object):
not self.args.no_scandir, not self.args.no_scandir,
[[True, False], [False, True]], [[True, False], [False, True]],
lstat="lt" in self.uparam, lstat="lt" in self.uparam,
throw=True,
) )
stats = {k: v for k, v in vfs_ls} stats = {k: v for k, v in vfs_ls}
ls_names = [x[0] for x in vfs_ls] ls_names = [x[0] for x in vfs_ls]

View file

@ -269,6 +269,7 @@ class Tftpd(object):
"*", "*",
not self.args.no_scandir, not self.args.no_scandir,
[[True, False]], [[True, False]],
throw=True,
) )
dnames = set([x[0] for x in vfs_ls if stat.S_ISDIR(x[1].st_mode)]) dnames = set([x[0] for x in vfs_ls if stat.S_ISDIR(x[1].st_mode)])
dirs1 = [(v.st_mtime, v.st_size, k + "/") for k, v in vfs_ls if k in dnames] dirs1 = [(v.st_mtime, v.st_size, k + "/") for k, v in vfs_ls if k in dnames]

View file

@ -896,7 +896,9 @@ class ThumbSrv(object):
prev_b64 = None prev_b64 = None
prev_fp = "" prev_fp = ""
try: try:
t1 = statdir(self.log_func, not self.args.no_scandir, False, thumbpath) t1 = statdir(
self.log_func, not self.args.no_scandir, False, thumbpath, False
)
ents = sorted(list(t1)) ents = sorted(list(t1))
except: except:
return 0 return 0

View file

@ -1347,7 +1347,7 @@ class Up2k(object):
rds = rd + "/" if rd else "" rds = rd + "/" if rd else ""
cdirs = cdir + os.sep cdirs = cdir + os.sep
g = statdir(self.log_func, not self.args.no_scandir, True, cdir) g = statdir(self.log_func, not self.args.no_scandir, True, cdir, False)
gl = sorted(g) gl = sorted(g)
partials = set([x[0] for x in gl if "PARTIAL" in x[0]]) partials = set([x[0] for x in gl if "PARTIAL" in x[0]])
for iname, inf in gl: for iname, inf in gl:

View file

@ -2862,7 +2862,7 @@ def sendfile_kern(
def statdir( def statdir(
logger: Optional["RootLogger"], scandir: bool, lstat: bool, top: str logger: Optional["RootLogger"], scandir: bool, lstat: bool, top: str, throw: bool
) -> Generator[tuple[str, os.stat_result], None, None]: ) -> Generator[tuple[str, os.stat_result], None, None]:
if lstat and ANYWIN: if lstat and ANYWIN:
lstat = False lstat = False
@ -2898,6 +2898,12 @@ def statdir(
logger(src, "[s] {} @ {}".format(repr(ex), fsdec(abspath)), 6) logger(src, "[s] {} @ {}".format(repr(ex), fsdec(abspath)), 6)
except Exception as ex: except Exception as ex:
if throw:
zi = getattr(ex, "errno", 0)
if zi == errno.ENOENT:
raise Pebkac(404, str(ex))
raise
t = "{} @ {}".format(repr(ex), top) t = "{} @ {}".format(repr(ex), top)
if logger: if logger:
logger(src, t, 1) logger(src, t, 1)
@ -2906,7 +2912,7 @@ def statdir(
def dir_is_empty(logger: "RootLogger", scandir: bool, top: str): def dir_is_empty(logger: "RootLogger", scandir: bool, top: str):
for _ in statdir(logger, scandir, False, top): for _ in statdir(logger, scandir, False, top, False):
return False return False
return True return True
@ -2919,7 +2925,7 @@ def rmdirs(
top = os.path.dirname(top) top = os.path.dirname(top)
depth -= 1 depth -= 1
stats = statdir(logger, scandir, lstat, top) stats = statdir(logger, scandir, lstat, top, False)
dirs = [x[0] for x in stats if stat.S_ISDIR(x[1].st_mode)] dirs = [x[0] for x in stats if stat.S_ISDIR(x[1].st_mode)]
dirs = [os.path.join(top, x) for x in dirs] dirs = [os.path.join(top, x) for x in dirs]
ok = [] ok = []