• src/xpdev/filewrap.c

    From Rob Swindell (on Debian Linux)@VERT to Git commit to main/sbbs/master on Saturday, August 08, 2026 18:05:04
    https://gitlab.synchro.net/main/sbbs/-/commit/7384ba6569de8ffbf0720f2d
    Modified Files:
    src/xpdev/filewrap.c
    Log Message:
    Close descriptors with closefrom() rather than close_range()

    closefrom() exists on the BSDs, Solaris and glibc 2.34 and later, where close_range() is a Linux syscall this code was reaching for directly. On
    glibc, closefrom() is implemented in terms of close_range() anyway, so
    calling it gets the better mechanism where there is one and a working
    fallback where there isn't, without this file having to know which.

    Keeping one descriptor no longer needs a range API at all: close everything above it in a single call, and walk the few below it. That loop is bounded by the kept descriptor rather than by the process descriptor limit, which can be very large. Platforms with neither call (macOS) still consult sysconf(_SC_OPEN_MAX).

    Also spell the first descriptor to close as STDERR_FILENO + 1 rather than 3.

    Suggested by Deuce.

    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Rob Swindell (on Debian Linux)@VERT to Git commit to main/sbbs/master on Wednesday, November 11, 2026 21:04:16
    https://gitlab.synchro.net/main/sbbs/-/commit/36867a9bbfd65e97159fe358
    Modified Files:
    src/xpdev/filewrap.c
    Log Message:
    xpdev: release a CIFS server lock left behind by a failed OFD lock

    On Linux, lock() and rdlock() use OFD fcntl() locks. On a CIFS mount
    (e.g. a Synchronet tree shared over SMB, or a loopback mount of the local
    Samba share), the kernel client acquires the byte-range lock on the
    server first and only then records it in the local VFS lock table. When
    that local step fails with EAGAIN - a conflict with another open file description's lock that is still being released locally - the request
    fails but the server lock is not rolled back.

    The client also shares one SMB handle among concurrent opens of a file
    with identical flags, so the orphaned lock blocks every lock request on
    that range (EACCES) until the process closes its last descriptor for the
    file. Since every thread waiting for the lock holds the file open while it retries, the lock persists until all waiters time out. For user.tab that
    is the full lockuserdat()/rdlockuserdat() retry budget, about 106
    seconds, ending in USER_LOCK_ERROR (-104): e.g. "!ERROR -104 in
    logoutuserdat" when several SMTP sessions authenticate as the same user
    at once.

    When a non-blocking lock fails with EAGAIN on a CIFS/SMB2 filesystem,
    unlock the same range on the same descriptor, releasing the server lock.
    EACCES (a conflict reported by the server) grants nothing and is left
    alone, as are local filesystems, where unlocking after a failed request
    could trim an overlapping lock the descriptor already holds.

    Reproduced with two threads repeatedly taking a shared lock, then an
    exclusive lock, on the same 1000-byte record of a file on a CIFS mount
    (Linux 6.1, Samba 4.22): the first EAGAIN stalled every run, within a few thousand iterations. With this change, 18,200 iterations including 7
    EAGAIN failures completed without a stall. Process-associated F_SETLK
    locks never stalled, as they cannot conflict within one process locally.

    Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net