From a global code review of the borg2 codebase (2026-08).
Problem
borg now materializes an archive as a browsable tree in three independent places:
src/borg/fuse.py (~800 lines): low-level FUSE (llfuse / pyfuse3), with its own FuseBackend
src/borg/hlfuse.py (~740 lines): high-level FUSE (mfusepy), with a second FuseBackend
src/borg/webdav.py (~1060 lines): ArchiveVFS + WebDAV/HTTP server
All three re-implement the same core logic: walking archive items, building the directory tree, hardlink handling, the archives/versions views, uid/gid/mode/time mapping, chunk-stream file reads. Behavior fixes must be applied N times — e.g. the ACL/xattr exposure fix (#9954) had to touch the FUSE implementations separately, and subtle divergence between the mount variants is hard to notice in review.
Proposed direction
- Extract one shared archive-VFS core (the new
webdav.py ArchiveVFS is the cleanest starting point): tree construction, lookup, attribute mapping, and a chunk-stream file-read object.
- Make
fuse.py, hlfuse.py, and webdav thin protocol adapters over it.
- Then consider whether llfuse (fuse2 low-level) can be dropped once mfusepy (fuse2+3) is proven — that would remove one implementation and one CI leg (the matrix currently builds/tests three FUSE variants).
Likely saves ~1k lines and, more importantly, leaves exactly one place where "what does an archive look like as a filesystem" is defined.
🤖 Generated with Claude Code
From a global code review of the borg2 codebase (2026-08).
Problem
borg now materializes an archive as a browsable tree in three independent places:
src/borg/fuse.py(~800 lines): low-level FUSE (llfuse / pyfuse3), with its ownFuseBackendsrc/borg/hlfuse.py(~740 lines): high-level FUSE (mfusepy), with a secondFuseBackendsrc/borg/webdav.py(~1060 lines):ArchiveVFS+ WebDAV/HTTP serverAll three re-implement the same core logic: walking archive items, building the directory tree, hardlink handling, the archives/versions views, uid/gid/mode/time mapping, chunk-stream file reads. Behavior fixes must be applied N times — e.g. the ACL/xattr exposure fix (#9954) had to touch the FUSE implementations separately, and subtle divergence between the mount variants is hard to notice in review.
Proposed direction
webdav.pyArchiveVFSis the cleanest starting point): tree construction, lookup, attribute mapping, and a chunk-stream file-read object.fuse.py,hlfuse.py, and webdav thin protocol adapters over it.Likely saves ~1k lines and, more importantly, leaves exactly one place where "what does an archive look like as a filesystem" is defined.
🤖 Generated with Claude Code