0.37.1: fix folder-download hang/501 on special files + add download progress
Two issues with the 0.37.0 folder zip-download: 1. Hang / server error (reported as 501) on "some folders". archive_dir tried to zip every entry, including non-regular files. Opening a FIFO blocks forever (no writer); a unix socket / unreadable file raised an OSError that aborted the whole archive. Now only real regular files are zipped — FIFOs, sockets, devices and symlinks are skipped without ever open()-ing them, and a per-file read error skips just that file instead of failing the download. 2. No feedback while a large folder is being prepared. The zip is built server-side before any bytes flow, so the click felt dead. The Files page now shows an indeterminate "Preparing <name>…" bar from click, switching to a real percentage during the transfer (Content-Length is known for the finished zip). filesApi.download forwards onDownloadProgress. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
f1782eca0e
commit
0dc430bb2a
@@ -187,7 +187,13 @@ def archive_dir(path: str) -> tuple[str, str]:
|
||||
|
||||
Returns ``(tmp_zip_path, download_filename)``. The caller is responsible
|
||||
for deleting the temp file once it has been streamed to the client.
|
||||
Symlinks are skipped so the archive cannot escape the sandbox or loop.
|
||||
|
||||
Only regular files and real subdirectories are archived. Symlinks are
|
||||
skipped (no sandbox escape / loops); special files (FIFOs, sockets,
|
||||
devices) are skipped too — opening a FIFO would block forever and a
|
||||
socket can't be read at all. Files that can't be read (permissions, or
|
||||
that vanish mid-walk) are skipped individually rather than aborting the
|
||||
whole archive.
|
||||
"""
|
||||
real = _safe_real(path)
|
||||
if not os.path.isdir(real):
|
||||
@@ -207,10 +213,18 @@ def archive_dir(path: str) -> tuple[str, str]:
|
||||
zf.writestr(os.path.join(name, rel_root) + "/", "")
|
||||
for f in files:
|
||||
full = os.path.join(root, f)
|
||||
if os.path.islink(full):
|
||||
# os.path.isfile follows symlinks; combined with the islink
|
||||
# check it admits only real regular files (skips FIFOs,
|
||||
# sockets, devices and symlinks without ever open()-ing them).
|
||||
if os.path.islink(full) or not os.path.isfile(full):
|
||||
continue
|
||||
arc = (os.path.join(name, rel_root, f) if rel_root != "."
|
||||
else os.path.join(name, f))
|
||||
try:
|
||||
zf.write(full, arc)
|
||||
except OSError:
|
||||
# Unreadable or vanished mid-walk — skip just this file.
|
||||
continue
|
||||
zf.write(full, os.path.join(name, rel_root, f) if rel_root != "."
|
||||
else os.path.join(name, f))
|
||||
except OSError:
|
||||
if os.path.exists(tmp):
|
||||
os.unlink(tmp)
|
||||
|
||||
Reference in New Issue
Block a user