errorInternalThrow() called strncpy(stackTraceBuffer, stackTrace, n - 1) followed by messageBuffer[n - 1] = '\0' -- a copy-paste bug that wrote the NUL terminator into the wrong buffer.
Since both buffers are ERROR_MESSAGE_BUFFER_SIZE the errant write was in-bounds and messageBuffer was already terminated, so the bug was silent in practice. But when stackTrace's length >= sizeof(stackTraceBuffer) - 1, strncpy() does not null-terminate, leaving stackTraceBuffer non-terminated and exposing errorContext.error.stackTrace to over-read by any consumer.
Replace the verbose, hand-built HrnLibSsh2 script entries in the SFTP unit tests with a set of per-function HRN_LIBSSH2_* response macros (one per libssh2 shim function). Each macro names its function, bakes in the fixed parameters the production code always passes, and defaults the common result, so a test supplies only the values that vary as trailing designated initializers.
The harness now defaults omitted values rather than requiring every field to be spelled out: libssh2_session_hostkey() defaults length/type/value; libssh2_sftp_stat_ex() defaults an omitted .attr to a regular file with mode 0640, defaults .flags to the standard attribute set (adding a size for regular files), and defaults .uid/.gid to the test user/group. Add HRN_LIBSSH2_ATTR_EXISTENCE (path exists but reports no attributes) and HRN_LIBSSH2_OWNER_ROOT (report ownership by root) sentinels, plus HRN_LIBSSH2_DIR/FILE/LINK/FIFO() helpers that OR a file type with an octal mode.
Replace the NULL-terminated script array and hrnLibSsh2ScriptSet(array) with HRN_LIBSSH2_SCRIPT_SET(...), which computes the script length so no terminator entry is needed; hrnLibSsh2ScriptSet() now takes an explicit size. Rebuild HRNLIBSSH2_MACRO_STARTUP/SHUTDOWN() and HOSTKEY_HASH_ENTRY() on top of the new macros, and stat_ex now verifies the requested stat_type (via .follow) instead of scripting it as a parameter.
Reorganize the tests themselves: split bundled comment groups into TEST_TITLE sections, split scripts per section, and drop redundant connect/disconnect setup. Remove tests that duplicate Posix tests for common code. Remove duplicative SFTP tests. Rename HrnLibSsh2 fields for clarity (attrPerms -> attr/mode, symlinkExTarget -> target).
These changes cut sftpTest.c roughly in half.
Path remove (used by expire and friends) issued one HTTP DELETE per file. Use the Azure Blob Batch API to remove up to 256 objects per request, as the GCS driver already does, by sending the deletes as multipart sub-requests.
Azure's batch parser is stricter than the MIME/OData spec it is based on: it requires the body to begin with the boundary delimiter (no leading CRLF preamble) and splits header lines on ": ", so the multipart builder now emits the opening delimiter without a leading CRLF and writes the MIME part headers and embedded sub-request headers in the "Name: value" form. The multipart request code is shared with the GCS driver, which accepts either form.
Azure omits the blank line that terminates the headers of an empty-body sub-response, relying on the boundary's CRLF as the terminator, so multipart response header parsing now allows eof to end the header block.
Sub-requests that fail (not 2xx or 404) are retried individually. A failed part is mapped back to the original request by its position in the response rather than the echoed content-id header, since Azure omits content-id on some error responses.
Tolerate chunk extensions by stripping everything from the first ';' on the chunk-size line before parsing the hex size, and consume any chunk trailers (and the blank line that terminates them) following the terminating zero-size chunk so the connection is left aligned for reuse.
Treat the transfer-encoding value case-insensitively so "Chunked" is accepted as "chunked".
Reject a response that sets both transfer-encoding and content-length even when content-length is 0. The previous check keyed off contentSize > 0, so a zero content-length slipped through. Track whether the header was present with a dedicated flag and reject the ambiguous combination uniformly, as RFC 7230 permits.
Add PostgreSQL 19 as an unreleased version (release: false) and vendor its control/checkpoint structures, catalog and control versions, and XLOG_PAGE_MAGIC.
PostgreSQL 19 stores data_checksum_version in pg_control as a four-state ChecksumStateType enum (OFF, VERSION, INPROGRESS_OFF, INPROGRESS_ON) rather than the prior 0/1 value. Validate the field against the version-appropriate maximum and clear the in-progress states to 0, since page checksums cannot be relied on while checksums are being enabled or disabled. Consumers of pageChecksumVersion therefore continue to see only 0 or 1.
Add the PG19 test harness and the supporting Perl/CI plumbing (DbVersion, VmTest, container build) and adjust the integration test matrix.
The previous "still zero" wording leaned on the reader tracking that contentRemaining was zero on entry and remained zero after parsing the next chunk size. State the actual meaning instead: a zero-size chunk terminates the response.
musl 1.2.6 intentionally crashes when exit() is called recursively, which happened when a signal arrived while exit() was already in progress, e.g. when a server terminated a child that was already exiting. Set a flag when exit is in progress so exitOnSignal() ignores the signal and allows the in-flight exit() to complete. Reset the flag in exitInit() since a forked child may inherit it from a parent that was exiting.
Also call exitSafe() before notifying the parent in the server tests so a signal sent in response to the notification cannot arrive before the exit in progress flag is set.
Add Alpine 3.24 to CI to exercise the unit tests against musl 1.2.6, which is where this crash was found.
Drop the c-only restriction for the a321 CI job so the full unit and integration suites run on musl libc, exercising the integration tests (including SFTP) against Alpine in addition to glibc.
Apply the ssh-rsa HostKeyAlgorithms/PubkeyAcceptedAlgorithms workaround to a321 as well as u22, since Alpine 3.21 ships OpenSSH 9.x which no longer offers the SHA-1 ssh-rsa host-key algorithm by default and the libssh2 client requires it (otherwise the SFTP handshake fails key exchange with LIBSSH2_ERROR_KEX_FAILURE).
Suppress the libssh2_session_init_ex and libssh2_session_handshake "possibly lost" leaks reported by valgrind during SFTP integration. These are persistent allocations tied to the session lifetime and are flagged only on the Linux CI runner where valgrind wraps the integration test binary. The suppressions go in valgrind.suppress.none because integration tests always run with vm none.
Generalize hrnHostPgBinPath() to probe the Debian, RHEL, and Alpine PostgreSQL bin paths in turn rather than hardcoding two, and throw a clear assert if none match.
Add a321 to the default VM list, install PostgreSQL 15/16/17 on Alpine, and point VMDEF_PGSQL_BIN at the Alpine layout. Rebuild the a321 base image accordingly.
Debian 11 will be EOL just after the next release but it is also a blocker for some planned work due to old package versions. It seems fine to just expire it a bit early.
Also update the integration tests to run Debian 12 on Posix since Azure is not supported on i386.
Previously each read driver decided whether a missing file was an error, which duplicated the ignoreMissing logic across the Posix, SFTP, remote, S3, Azure, and GCS drivers. Now driver open() simply reports whether the file exists and StorageRead throws FileMissingError when missing files are not ignored.
Since the client now makes this decision, ignoreMissing no longer needs to be passed through the remote protocol and a missing file is reported locally rather than as an error raised from the remote.
When libbacktrace is enabled, throwing an error calls backtrace_full(), which unwinds the stack with libgcc's _Unwind_Backtrace. On aarch64 the unwinder (and glibc's _dl_find_object, which it calls to look up unwind tables) branches on values valgrind considers uninitialised. Since tests run under valgrind with --exit-on-first-error=yes, the false positive aborted any test that happened to trip it, e.g. storage/sftp.
Suppress Cond and Value8 errors that originate inside _Unwind_Backtrace when called from backtrace_full.
.github/ISSUE_TEMPLATE.md is no longer filling new issues even though it should still be working according to the documentation.
Rather than fight the system just move to the new format.
Add a repo-s3-service option that controls the SigV4 signing service name. Defaults to 's3' for standard S3 endpoints. Set to 's3-outposts' when using an S3 Outposts endpoint.
The signing service is used in the credential scope, HMAC signing key derivation, and authorization header. The option accepts free-form input to support future AWS service variants.
Previously, drivers constructed StorageRead/StorageWrite objects directly and stored metadata in a shared interface struct. Now, StorageRead/StorageWrite create the driver via storageInterfaceNewReadP()/NewWriteP() and mediate between IoRead/IoWrite and the driver. Drivers return opaque objects and own their metadata independently.
This loosens the tight coupling between drivers and the StorageRead/StorageWrite layer. The remote write driver replaces its back-pointer to StorageWrite with a filterGroup callback, eliminating the circular dependency. It makes retry in StorageRead much more readable.
Also move the logic for testing whether a file version could not be found out of the drivers and into StorageRead.
The syncPath value in StorageWrite is for informational purposes and does not determine if the path is actually synced or not.
Instead probe the Posix driver to make sure that syncPath is disabled so there is no error on CIFS.
On systems where uid/gid lookups are routed to a remote name service (sssd, systemd-userdbd, LDAP, etc.), every getpwuid()/getgrgid() call incurs a Unix socket round-trip. This dominates the manifest build phase for clusters with millions of files, even though the data files almost always share a single owner.
Add a small fixed-size (16-entry) per-process cache for userNameFromId() and groupNameFromId(). Linear scan is faster than a hash table at this size. Negative results (unknown ids) are also cached. Cache overflow falls through to uncached lookups.
This standard is over fifteen years old and the features we are interested in seem well supported on popular compilers.
The main advantage is that static_assert() will now display the specified message on error rather than the ever-cryptic `negative width
in bit-field '__error_if_negative'`. Now that we can depend on having
static_assert() we can replace our STATIC_ASSERT_STMT() macro.
Replace our ALIGN_OF() macro with alignof().
Replace our FN_NO_RETURN macro with noreturn. Include stdnoreturn.h in build.h to avoid needing to include it in many header files.
Use an anonymous union in common/type/json.c where it simplifies syntax.
Other uses of union seem better as they are.
In dark mode the black favicon was barely visible. Use a white favicon in dark mode instead.
Also, use the new SVG logo for the favicon and update logo.png to the new style.
When backups are running on multiple repositories simultaneously, the info command now reports per-repo progress in addition to the existing overall progress. A new repo array is included in JSON output for backup locks. This avoids confusing progress jumps when one repo finishes before another.
This job has never surfaced any useful data and now it is failing, so remove it.
It appears that CodeQL can now be automated directly within the Github interface, so that seems like a better route if we decide to reenable it.
Cirrus CI is shutting down on June 1 so migrate all tests. This could have been done before, probably, but it was not clear how to run FreeBSD on Github Actions. The cross-platforms-actions action solves that problem.
Fix a couple of minor test issues found on MacOS.
Also remove the dead make-cmd option. This has not been valid since the migration to meson.
Verify currently checks only backup directories present in the repository and does not validate consistency with backup.info. As a result, discrepancies between the repo contents and backup.info may go unnoticed.
Warn if a backup directory exists but is not described in backup.info. Warn if a backup is listed in backup.info but missing on disk. Add backups found only in backup.info (but not on disk) to the processing list so that verify command reports their status as manifest missing.
These are useful to denote elements that could be styled but currently work with defaults. However, CSS linters dislike empty rules so comment them out. This was already done with some rules but not followed consistently.
This allows logos to be displayed for sponsors in HTML on the homepage.
The markdown will continue to list sponsors in text but the list will be pulled from the new XML.