• src/sbbs3/userdat.c userdat.h src/xpdev/filewrap.c filewrap.h

    From Rob Swindell (on Windows 11)@VERT to Git commit to main/sbbs/master on Wednesday, June 03, 2026 09:44:35
    https://gitlab.synchro.net/main/sbbs/-/commit/aa673afdf1543e90e582478b
    Modified Files:
    src/sbbs3/userdat.c userdat.h src/xpdev/filewrap.c filewrap.h
    Log Message:
    xpdev/filewrap: shared read locks (rdlock) so user.tab reads don't serialize on Windows/SMB

    xp_lockfile() on Windows used _locking(), which offers exclusive locks
    only, so every byte-range lock - even a read-only record read - was
    exclusive. The POSIX implementation already takes a *shared* lock
    (F_RDLCK) for descriptors opened O_RDONLY, so this was a silent
    cross-platform divergence dating to the original xpdev (f3f66b77d, lake-3-indigenous).

    readuserdat() locks every user.tab record it reads (for consistency
    against concurrent writers). On Windows that lock was exclusive, so two read-only getuserdat() calls on the same record serialized - and with
    the data directory on an SMB share, each lock/unlock is a network
    round-trip and the 200-attempt retry loop becomes a lock convoy. Under a web-scrape / login-probe flood, read-only lookups of hot records (e.g.
    user #1) serialize across every server sharing the mount, starving
    legitimate user.tab access. (GitLab #1153.)

    Add a shared-lock primitive:
    - xpdev: rdlock() - POSIX F_RDLCK; Windows LockFileEx without
    LOCKFILE_EXCLUSIVE_LOCK (the only Windows API offering shared
    byte-range locks). On Windows, lock()/xp_lockfile() and unlock() move
    to the LockFileEx/UnlockFileEx family so exclusive and shared locks
    pair with a single UnlockFileEx (a LockFileEx lock can't be released
    with _locking(LK_UNLCK)). Borland keeps the legacy _locking path.
    - sbbs3: rdlockuserdat(); readuserdat() takes a shared lock for
    read-only access (leave_locked == false) and an exclusive lock when
    the lock is held for a subsequent write.

    Concurrent readers of a user record now proceed in parallel; writers and
    the read-modify-write path stay exclusive. smblib, node.dab, the access
    log, and the JS/Baja file.lock() API are unchanged (all correctly remain exclusive).

    Build-verified under MSVC (Win32 Release: filewrap.c, userdat.c compile; sbbs.dll links). POSIX (GCC/Clang) and the Borland xpdev build not
    compiled here; not yet runtime-tested against the live SMB install.

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

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