• src/sbbs3/userdat.c

    From Rob Swindell (on Windows 11)@VERT to Git commit to main/sbbs/master on Sunday, August 09, 2026 00:19:25
    https://gitlab.synchro.net/main/sbbs/-/commit/6651ed85c10b9dd3e7ad52c4
    Modified Files:
    src/sbbs3/userdat.c
    Log Message:
    Fix: check_realname() don't check for multiple names when real names not req'd

    Bug discovered by Cyan (one name) trying to register a new user at bbsdev.net which does not prompt for a real name for new users (copies the user's alias, but required as space in it and rejected the new user ungracefully).

    ---
    þ 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 Monday, August 17, 2026 02:07:18
    https://gitlab.synchro.net/main/sbbs/-/commit/247ed952d22d98605c63cd51
    Modified Files:
    src/sbbs3/userdat.c
    Log Message:
    Don't lock every user record just to count the users

    readuserdat() is built for reading a single record safely: per call it
    does a filelength(), a seek, a shared byte-range lock, the read, and an
    unlock. total_users() called it once per user slot, so counting the
    users cost five file operations and two byte-range locks per slot.

    Byte-range locks are the worst possible unit of work when the data
    directory is network-mounted. They are synchronous round-trips to the
    lock manager and, unlike reads, can be neither cached nor batched by the
    client whatever its caching settings. Locking never made the total
    atomic either - only each record read within it - so the count was
    already assembled from records read at different moments.

    Read the user file sequentially in bulk and split the records in memory instead. The result is unchanged: records flagged DELETED or INACTIVE
    are still excluded. A record rewritten mid-scan can now be misparsed and
    shift the total by one, where before it would merely have been counted
    as of a slightly different moment; for a displayed total, neither is
    more meaningful than the other.

    For 1,457 user slots this replaces 7,286 file operations - 2,914 of them
    locks - with about 25 reads and no locks, taking the scan from 85ms to
    5ms with the data directory on a loopback SMB mount.

    This is the same fix as 8561162afd (fighter-20-fears, 2026-07-29),
    applied to the user census rather than the node list.

    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 Friday, October 02, 2026 19:25:26
    https://gitlab.synchro.net/main/sbbs/-/commit/d83fbe2a41580e37314ea671
    Modified Files:
    src/sbbs3/userdat.c
    Log Message:
    Reject future birthdates in birthdate_is_valid()

    The validator had a lower bound (1900) but no upper bound, so a birthdate
    such as 09/11/2890 was accepted at new-user registration and stored,
    and getage() then reported a negative age (-864).

    A birthdate later than today is now invalid. Every entry path that
    validates (new-user and profile prompts via system.check_birthdate())
    rejects it, and getage() returns 0 for any such record already stored.

    Fixes #1277

    Co-Authored-By: Claude Opus 5.5 <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 Monday, November 09, 2026 17:12:39
    https://gitlab.synchro.net/main/sbbs/-/commit/b643ac2bca589181742b3e75
    Modified Files:
    src/sbbs3/userdat.c
    Log Message:
    logoutuserdat(): unlock the user record before closing the file

    Since 48922a15c6 (2025-12-17), logoutuserdat() has read the user record
    with leave_locked=true, written it with fputuserdat(), and closed the file without unlocking the record, relying on close() to release the lock. Every other user record writer unlocks explicitly.

    On Windows, the release of a byte-range lock still held at close happens whenever the OS gets to it (Microsoft's LockFileEx documentation recommends
    an explicit unlock). The web server calls logoutuserdat() after nearly every authenticated request, so a webv4 user firing parallel requests could leave their record locked long enough for the other request threads to exhaust
    their lock retries and log "!Error -104 writing user data for user #N".
    On Vertrauen's Windows web server, every request from one such user stalled
    for about two minutes before failing this way.

    That commit also dropped the dirtyuserdat() call that the previous putuserstr()/adjustuserval() writes had performed; restore it.

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

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