• src/sbbs3/nopen.c nopen.h

    From Rob Swindell (on Windows 11)@VERT to Git commit to main/sbbs/master on Fri Jul 24 23:17:57 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/254b876244369ee10695c7d3
    Modified Files:
    src/sbbs3/nopen.c nopen.h
    Log Message:
    backup: copy via a shared (DENYNONE) open rather than CopyFile()

    backup() with ren=false copied through the Win32 CopyFile() API, which
    opens its source GENERIC_READ / FILE_SHARE_READ. That is a deny-write
    share mode: for the duration of the copy no other opener -- on this host
    or, over SMB/CIFS, on any other host sharing the data directory -- can
    open that file for writing.

    Where a data/ directory is shared by several Synchronet hosts, that means
    every daily_maint() user-data backup blocks user-record writes system
    wide until the copy completes: putuserdat() gives up after nopen()'s
    ~10-second retry budget and returns USER_OPEN_ERROR (-102), and the
    record write is discarded rather than queued. A copy that never finishes wedges user-record writes indefinitely, with a log flood as the only
    visible symptom.

    Add fcopy(), opening both source and destination O_DENYNONE via nopen(),
    and call it from backup(). On Windows this restores the behavior that 2b892eae1f (peer-11-drive) replaced: that commit swapped fcopy() for
    CopyFile() to gain throughput (1GB to/from CIFS/SMB: 37s -> 5s), which is
    a real cost, but for a shared data directory a fast backup that locks out writers is the worse trade.

    Also move nopen.h's include guard off the reserved _NOPEN_H form.

    Co-Authored-By: Claude Opus 4.8 (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 Mon Jul 27 00:02:53 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/faf0950ba4089e13b3e610af
    Modified Files:
    src/sbbs3/nopen.c nopen.h
    Log Message:
    Rename fcopy() to nfcopy()

    The name said nothing about what distinguishes it from xpdev's
    CopyFile(), which the JavaScript file_copy() and most of the tree use.
    This one opens both files DENYNONE through nopen(), so a copy never
    blocks another node from writing to either file, and it retries a locked
    open rather than failing. The n prefix is how the rest of this file
    spells that already: nopen(), fnopen().

    The distinction is worth making obvious now that both routines preserve
    the source file's permissions (GitLab #1202) and that difference no
    longer tells them apart.

    nfcopy() is exported from libsbbs, so any out-of-tree caller has to
    follow; no compatibility alias is left behind. In-tree there is one
    caller, backup().

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

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