• Archive class / file-base listings: libarchive's mtree reader claims s

    From Rob Swindell@1:103/705 to GitLab issue in main/sbbs on Fri Oct 16 21:28:24 2026
    open https://gitlab.synchro.net/main/sbbs/-/work_items/1286

    ## Summary

    libarchive's mtree reader claims plain text files whose first lines are single tokens (number lists, word lists, one-name-per-line `.lst` files), and reading them then fails with:

    ```
    archive_read_next_header() returned -20: Missing type keyword in mtree specification
    ```

    mtree is a BSD manifest format (a text list of paths with `type=`/`mode=`/`size=` attributes). It contains no file data, so it is never a useful "archive" on a BBS. It is enabled because every read site calls `archive_read_support_format_all()`, and libarchive offers no way to exclude one format from that set.

    Found while testing the #1278 fix (b958104a19, landing-13-joined, 2026-10-16). Not caused by it: HEAD before that commit behaves the same.

    ## Reproduce

    ```
    $ seq 1 50 > words.txt
    $ jsexec -n -r 'print(new Archive("words.txt").list())'
    Error: archive_read_next_header() returned -20: Missing type keyword in mtree specification
    ```

    Without an `#mtree` signature line, libarchive guesses from the first lines, and a line holding a single token passes as "a path with no keywords". Text with spaces in its lines (logs, Markdown, source code) is not affected.

    ## Effects

    - **Uncompressed text file:** fails either way. Only the error text differs from the expected "Unrecognized archive format".
    - **Compressed text file** (e.g. a `.gz` of a word list): since #1278 this should list as a single-entry "raw" archive, but mtree claims the decompressed text first, so it still fails.
    - **A genuine mtree spec** (e.g. uploaded under a `.zip` name): lists as an "archive" whose entries are all 0 bytes; extracting creates empty files. Description extraction ignores an empty `FILE_ID.DIZ`.

    **Not a disclosure risk.** mtree entries can name local files (`contents=/path`, or a bare path), but libarchive only reads them when the mtree `checkfs` option is enabled, which is off by default and never set by Synchronet. Verified via `Archive.list()`/`read()`/`extract()` and directly against libarchive 3.7.4 with absolute, relative, and bare-path specs: every entry has size 0 and no data.

    ## Proposed fix

    In `check_archive_entry()` (`src/sbbs3/filedat.c`), reject `ARCHIVE_FORMAT_MTREE` the same way an uncompressed "raw" match is rejected, so the error is "Unrecognized archive format".

    Optionally, when mtree is rejected and a decompression filter was applied, reopen the file with only the raw format enabled so a compressed text file lists as a single entry. Enumerating every format except mtree explicitly would also work, but would go stale as libarchive adds formats.

    _Authored by Claude (Claude Code), on behalf of @rswindell_
    --- SBBSecho 3.38-Linux
    * Origin: Vertrauen - [vert/cvs/bbs].synchro.net (1:103/705)