• src/sbbs3/ftpsrvr.cpp

    From Rob Swindell (on Windows 11)@1:103/705 to Git commit to main/sbbs/master on Wed May 20 16:55:04 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/3f0508da6bce06fb59f76358
    Modified Files:
    src/sbbs3/ftpsrvr.cpp
    Log Message:
    ftpsrvr: fix file-descriptor leaks in directory/index handlers

    Two distinct leaks of per-command temp-file descriptors, both surfacing
    as "ERROR 24 (Too many open files)" once the process exhausts its
    descriptor table -- after which every temp-file open (LIST/MLSD .lst,
    index .ndx, QWK packets, and even unrelated subsystems sharing the
    process) fails in a storm.

    1) MDTM <index-file> (getdate) opened a temp .ndx file via ftp_tmpfname()
    but never closed it: the lone fclose(fp) lives only in the !getdate
    (download) branch. Every MDTM on the dynamically-generated index file
    (startup->index_file_name, e.g. 00index) leaked one descriptor and
    orphaned an sbbstemp/SBBS_FTP.*.ndx file. FTP clients and crawlers
    MDTM each listed entry, so these accumulated steadily.
    Fix: don't open the temp file for a getdate/MDTM request at all (it is
    only needed to generate listing content); move the fopen into the
    !getdate branch.
    Introduced in 63e5e08f1c (2007-02-11), which made the index fopen
    unconditional and added early-outs for SIZE (continue) and MDTM
    (fall-through) that both bypassed the fclose. ff9ae78f44 (2007-11-28)
    fixed the SIZE half by moving its check above the fopen, but left the
    MDTM path leaking -- for ~18 years.

    2) The MLSD, LIST and index-generation handlers "continue" out of the
    command loop on smb_open_dir() failure without closing the already-
    opened temp fp (and, for LIST/MLSD, without servicing the already-
    announced data connection). Triggers whenever a directory's .shd is
    locked or contended.
    Fix: close fp on the failure path; for LIST/MLSD also run the normal
    filexfer() cleanup so the data connection is serviced and the temp
    file removed.
    Introduced in 925e3b0a2 (2021-04-04), which migrated FTP directory
    listings to smb_open_dir()/loadfiles().

    Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
    --- SBBSecho 3.37-Linux
    * Origin: Vertrauen - [vert/cvs/bbs].synchro.net (1:103/705)
  • From Rob Swindell (on Debian Linux)@1:103/705 to Git commit to main/sbbs/master on Wed May 20 17:10:12 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/19c8264107387b59dfe2dc70
    Modified Files:
    src/sbbs3/ftpsrvr.cpp
    Log Message:
    ftpsrvr: add errprintf() and route LOG_ERR/LOG_CRIT through it for de-dup

    The FTP server logged every error via lprintf(LOG_ERR/LOG_CRIT, ...),
    which writes each record to error.log with no repeat suppression. Under file-descriptor exhaustion (EMFILE) a single busy server flooded
    error.log with thousands of near-identical entries.

    Mirror the mailsrvr/websrvr/services pattern: add a static errprintf()
    that calls repeated_error(line, function) and demotes a consecutive
    repeat of the same site to LOG_WARNING (which falls out of error.log).
    Convert all 48 LOG_ERR/LOG_CRIT lprintf() call sites to
    errprintf(..., WHERE, ...).

    This is log de-duplication only; it does not address the underlying
    descriptor exhaustion. Note repeated_error() tracks only the single
    most-recent (line, function), so interleaved failures from many
    concurrent sessions are only partially collapsed.

    Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
    --- SBBSecho 3.37-Linux
    * Origin: Vertrauen - [vert/cvs/bbs].synchro.net (1:103/705)
  • From Rob Swindell (on Debian Linux)@1:103/705 to Git commit to main/sbbs/master on Thu May 21 20:23:05 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/308b735e39c6aaefec799fd0
    Modified Files:
    src/sbbs3/ftpsrvr.cpp
    Log Message:
    ftpsrvr: remove duplicate ndx temp-file fopen() from de-dup commit

    Commit 19c826410 ("add errprintf() and route LOG_ERR/LOG_CRIT through it
    for de-dup") was meant only to convert the index-file fopen() failure's lprintf(LOG_ERR,...) to errprintf(..., WHERE, ...). A rebase merge
    resolution instead inserted a second, redundant fopen() of the "ndx"
    temp file ahead of "success = true;" (giving the new copy the errprintf)
    while leaving the original lprintf() open untouched.

    That left two opens:
    - the getdate (MDTM-style) path opened a temp file it never uses --
    contradicting the "No temp file needed for a modification-time query"
    comment -- and never set tmpfile/delfile, leaking both the descriptor
    and the temp file (ironic in an EMFILE-motivated change);
    - the !getdate path opened the file twice, leaking the first descriptor.

    Drop the spurious block and keep the de-dup conversion on the surviving
    open inside the else branch, restoring the original single-open logic.

    Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
    --- SBBSecho 3.37-Linux
    * Origin: Vertrauen - [vert/cvs/bbs].synchro.net (1:103/705)