fs: use ftruncate on OpenBSD in AllocateFileRange #36189

pull HouseOfHufflepuff wants to merge 1 commits into bitcoin:master from HouseOfHufflepuff:fs-openbsd-ftruncate-allocatefilerange changing 1 files +4 −0
  1. HouseOfHufflepuff commented at 1:29 AM on September 8, 2026: contributor

    Summary

    OpenBSD provides neither fallocate nor posix_fallocate, so AllocateFileRange() always fell through to the slow fallback path there: writing the entire range one 64KiB buffer at a time instead of just advising the OS of the eventual file size.

    This adds an __OpenBSD__ branch that uses ftruncate, matching the guidance in #32643 and the same pattern already used for the macOS branch just above it in this function.

    Fixes #32643

    Test plan

    I don't have OpenBSD hardware to run this on directly, so it's verified by:

    • The "OpenBSD Cross" CI job, which compiles this exact branch for the target and passes.
    • Full local build (macOS, Clang) with no new warnings.
    • Full unit test suite (test_bitcoin, 825 cases) passes.
    • test/lint/lint-files.py, lint-includes.py, and lint-locale-dependence.py all pass.

    The approach mirrors a previous attempt at this issue (#32645, closed unmerged) which was hand-tested on real OpenBSD 7.7 via a signet IBD run, confirming the fallback path is no longer hit.

  2. fs: use ftruncate on OpenBSD in AllocateFileRange
    OpenBSD doesn't provide fallocate or posix_fallocate, so
    AllocateFileRange() was always taking the slow fallback path there,
    writing the whole range one 64KiB chunk at a time.
    
    ftruncate is available and, like on the other platforms handled here,
    is sufficient to advise the OS of the eventual file size in the
    non-Windows, non-macOS case.
    
    Fixes #32643
    cafd88ca4a
  3. DrahtBot commented at 1:29 AM on September 8, 2026: contributor

    <!--e57a25ab6845829454e8d69fc972939a-->

    The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

    <!--006a51241073e994b41acfe9ec718e94-->

    Code Coverage & Benchmarks

    For details see: https://corecheck.dev/bitcoin/bitcoin/pulls/36189.

    <!--021abf342d371248e50ceaed478a90ca-->

    Reviews

    See the guideline and AI policy for information on the review process.

    Type Reviewers
    Approach NACK winterrdog

    If your review is incorrectly listed, please copy-paste <code>&lt;!--meta-tag:bot-skip--&gt;</code> into the comment that the bot should ignore.

    <!--5faf32d7da4f0f540f40219e4f7537a3-->

  4. sedited commented at 11:10 AM on September 8, 2026: contributor

    Please don't post LLM PR descriptions. That "Test Plan" doesn't tell anything besides the bare minimum required to make CI pass.

  5. HouseOfHufflepuff commented at 5:11 PM on September 8, 2026: contributor

    Ready for review: @theStack @fanquake

  6. in src/util/fs_helpers.cpp:234 in cafd88ca4a
     227 | @@ -228,6 +228,10 @@ void AllocateFileRange(FILE* file, unsigned int offset, unsigned int length)
     228 |      // Version using posix_fallocate
     229 |      off_t nEndPos = (off_t)offset + length;
     230 |      if (0 == posix_fallocate(fileno(file), 0, nEndPos)) return;
     231 | +#elif defined(__OpenBSD__)
     232 | +    // OpenBSD doesn't have fallocate or posix_fallocate, use ftruncate instead
     233 | +    off_t nEndPos = (off_t)offset + length;
     234 | +    if (0 == ftruncate(fileno(file), nEndPos)) return;
    


    winterrdog commented at 9:35 PM on September 8, 2026:

    some thoughts && questions on this:

    1. on the shrink safety (data loss/clobbering):

    posix_fallocate() (plus other cases besides fallback) never shrinks the file: if offset + len is already within the current file size, it is effectively a no-op. that's what i'm trying to preserve in #35524 for the fallback (and Windows) case to fix #33164

    ftruncate() does not have that property. if nEndPos is smaller than the current file size, it will actually shrink the file and discard the data past nEndPos -- sth AllocateFileRange() shoud avoid doing based on how it's supposed to work

    so, can we guarantee that nEndPos is never smaller than the current file size when we hit the OpenBSD case ?

    2. on the sparse-file / preallocation-intent footgun:

    AllocateFileRange()'s intent is to preallocate (a disk optimisation technique), but on OpenBSD this case is just ftruncate(). that only extends the logical file size (see #17827 about how confusing this can be); it does not necessarily allocate the underlying disk blocks, so the new region can be a sparse hole. right now, that means on OpenBSD, a full disk won't be caught by ftruncate -- it gets deferred to whatever later write() actually touches that hole, which is not a great place to discover that you are out of space (partial writes, error handling scattered elsewhere, etc..). also, note that a sparse-then-filled-in region is more likely to get fragmented by the filesystem's allocator than a range that was reserved contiguously ahead of time (AOT), which can matter for IBD/reindex read performance (not a bug, just impacts performance in a "not-so-good" way)

    now, that is different from what posix_fallocate() guarantees: it reserves the space up front, so once it succeeds, subsequent writes into that range (physical size grows as a result) should never fail with ENOSPC

    so are we OK with the OpenBSD fallback weakening that guarantee ?


    i'd love to hear your thoughts

    a few refs:

  7. winterrdog commented at 12:36 PM on September 10, 2026: contributor

    from the looks of it, this PR is not going to fly anywhere with the current approach, since OpenBSD does not expose anything like fallocate/posix_fallocate/F_PREALLOCATE with equivalent disk-space reservation semantics

    the current ftruncate branch can actively defeat the generic fallback below it. ftruncate() only changes the logical file size (inode size get changed, not actual blocks), so it can succeed even when there is not enough disk space to back the newly extended range. because of that, if (0 == ftruncate(...)) return; will normally return successfully without actually allocating the blocks, so the code never reaches the fallback that writes zero-filled buffers across the range and forces the blocks to actually get allocated

    right now, i think the cleanest fix is to drop the __OpenBSD__ branch entirely and let it fall through to the existing fallback.

    also see: #32643 (comment)

    friendly ping: @fanquake @kallewoof -- any chance you got time to weigh in here ?


    for me, it's an approach NACK

  8. HouseOfHufflepuff commented at 4:42 PM on September 14, 2026: contributor

    You're right, thanks for digging into this — a shrink-guard wouldn't have been enough anyway since the real problem is ftruncate() succeeding without ever forcing block allocation, which skips the fallback that actually does. There's no OpenBSD syscall with real posix_fallocate() semantics, so I don't think this is fixable as an approach. Closing.

  9. HouseOfHufflepuff closed this on Sep 14, 2026


github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-09-15 18:51 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me