Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package radare2 for openSUSE:Factory checked in at 2026-08-09 21:44:24 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/radare2 (Old) and /work/SRC/openSUSE:Factory/.radare2.new.16738 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "radare2" Sun Aug 9 21:44:24 2026 rev:16 rq:1370348 version:6.2.0 Changes: -------- --- /work/SRC/openSUSE:Factory/radare2/radare2.changes 2026-06-17 16:19:29.898023238 +0200 +++ /work/SRC/openSUSE:Factory/.radare2.new.16738/radare2.changes 2026-08-09 21:46:33.073213475 +0200 @@ -1,0 +2,58 @@ +Fri Aug 7 20:44:11 UTC 2026 - Eyad Issa <[email protected]> + +- Update to version 6.2.0: + * ABI: reduce struct padding and unify type member models + * Analysis: improve conventions, types, propagation, and ESIL + * Analysis: add stack argument APIs, reguse hints, PIC seeding + * API: add formatted SDB getters and setters + * Arch: add stateful sessions and improve PPC, MIPS, ARM, and x86 + * Bin: add generic resource extraction and demangler plugins + * Bin: harden PE, ELF, DEX, Java, Mach-O, NE, and QNX parsing + * Build/CI: add MiKNix, Android, JSLinux, and ReactOS support + * Console/Core: isolate tasks; improve grep, themes, and pdc + * Crash: fix OOB, UAF, overflow, injection, and OOM bugs + * Debug: improve cross-platform debugging, ptrace, regs, and dcut + * Disasm/ESIL: improve backward walks, call comments, and flags + * Graph/HTTP/IO: fix graph targets, auth, WebUI, and io.gprobe + * Magic/Port: improve matching and add browser NetBSD support + * Projects/Pseudo: improve persistence and structured pdc + * Search/Shell: overhaul contexts, parsing, substitution, APIs + * Sync/Tests: add Windows timeouts and eliminate test races + * Tools: improve Android toolchains, r2 flags, rax2, and rasm2 + * Types: expand signatures, pointers, structs, unions, and sizes + * Util: improve base64, math, locks, paths, tries, and strbufs + * Visual/Windows: fix scrolling, prompts, debugger, ANSI support + * Security: harden GDB, HTTP auth, shell, and loaders + +------------------------------------------------------------------- +Fri Jun 26 13:36:50 UTC 2026 - Eyad Issa <[email protected]> + +- Update to version 6.1.8: + * ABI: deprecate sdb_remove in favor of sdb_unset + * Analysis: improve trampolines, variables, DWARF, SDB, and tests + * Arch: harden PPC64 TOC tracking and improve endian handling + * Arch: update Capstone, CPU validation, and disassembly support + * ASM: apply the pseudo filter once and pad rasm2 output + * Bin: harden loaders; improve dyldcache, strings, relocations + * Bin: add FTAB extraction and C++, Rust, D, XL, ARM demanglers + * Build: add Nix support; improve SDB, Zydis, Meson, Capstone + * Console: fix task races, UAFs, marks, grep, and reset + * Core: fix numvar reference math state handling + * Crash: fix parser overflows, OOB reads, UAFs, null derefs + * Debug: fix profile leaks and GDB cache ownership checks + * Disasm: improve address bases, overlaps, comments, and pseudo + * ESIL: improve PPC, local state, stepping, traps, and step-back + * FS/IO: fix HFS+, NTFS, io.self leaks, and Windows block devices + * Print/Pseudo: improve JWT, JSON, markdown, pdc, and bounds + * Search/Shell: improve ESIL, gadgets, aliases, and parsing + * Socket: fix PTY dynapi initialization during redirection + * Tests/Tools: improve requirements, parsing, and rafs2 packaging + * Types/Util: improve lookups, unions, strings, and markdown + * Windows: port r2preload support +- CVE-2026-8696: radare2: version 6.1.5 contains a use-after-free + vulnerability in the gdbr_pids_list() function within the GDB + client core that allows remote attackers to cause a denial of + service or potentially execute arbitrary code +- Drop CVE-2026-8695.patch: upstreamed in 6.1.6 + +------------------------------------------------------------------- Old: ---- CVE-2026-8695.patch quickjs-3087a2ce5bcb66cc1fcd9f34d3e5ce3bd43a67d9.tar.gz radare2-6.1.4.tar.gz sdb-2.4.2.tar.gz New: ---- quickjs-9d15fb60b67c45fd0de413bb49e48f8dacebac16.tar.gz radare2-6.2.0.tar.gz sdb-2.5.0.tar.gz zydis-4.1.0.tar.gz ----------(Old B)---------- Old: service or potentially execute arbitrary code - Drop CVE-2026-8695.patch: upstreamed in 6.1.6 ----------(Old E)---------- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ radare2.spec ++++++ --- /var/tmp/diff_new_pack.XX2NIt/_old 2026-08-09 21:46:34.253253754 +0200 +++ /var/tmp/diff_new_pack.XX2NIt/_new 2026-08-09 21:46:34.253253754 +0200 @@ -16,15 +16,17 @@ # -%global sdb_rev 2.4.2 -%global sdb_soname 2_4_2 +%global sdb_rev 2.5.0 +%global sdb_soname 2_5_0 -%global qjs_rev 3087a2ce5bcb66cc1fcd9f34d3e5ce3bd43a67d9 +%global zydis_rev 4.1.0 + +%global qjs_rev 9d15fb60b67c45fd0de413bb49e48f8dacebac16 %global tests_rev d6a4529fd6c8439a309abbcf00bb7a9efc9e8271 Name: radare2 -Version: 6.1.4 +Version: 6.2.0 Release: 0 Summary: Reverse Engineering Framework License: GPL-3.0-only AND LGPL-3.0-only @@ -34,8 +36,8 @@ Source1: https://github.com/radareorg/sdb/archive/%{sdb_rev}/sdb-%{sdb_rev}.tar.gz Source2: https://github.com/quickjs-ng/quickjs/archive/%{qjs_rev}/quickjs-%{qjs_rev}.tar.gz Source3: https://github.com/radareorg/radare2-testbins/archive/%{tests_rev}/radare2-testbins-%{tests_rev}.tar.gz +Source4: https://github.com/zyantific/zydis/releases/download/v%{zydis_rev}/zydis-amalgamated.tar.gz#/zydis-%{zydis_rev}.tar.gz Patch0: pkgconfig.patch -Patch1: https://github.com/radareorg/radare2/commit/c213ad6894a1eb9086ac8bf5fae35757e9e1683c.patch#/CVE-2026-8695.patch BuildRequires: chrpath BuildRequires: dos2unix BuildRequires: fdupes @@ -107,6 +109,9 @@ mkdir -p subprojects/qjs tar -C subprojects/qjs --strip-components=1 -x -f %{SOURCE2} +mkdir -p subprojects/zydis +tar -C subprojects/zydis -x -f %{SOURCE4} + mkdir -p test/bins tar -C test/bins --strip-components=1 -x -f %{SOURCE3} ++++++ quickjs-3087a2ce5bcb66cc1fcd9f34d3e5ce3bd43a67d9.tar.gz -> quickjs-9d15fb60b67c45fd0de413bb49e48f8dacebac16.tar.gz ++++++ ++++ 21008 lines of diff (skipped) ++++++ radare2-6.1.4.tar.gz -> radare2-6.2.0.tar.gz ++++++ /work/SRC/openSUSE:Factory/radare2/radare2-6.1.4.tar.gz /work/SRC/openSUSE:Factory/.radare2.new.16738/radare2-6.2.0.tar.gz differ: char 13, line 1 ++++++ sdb-2.4.2.tar.gz -> sdb-2.5.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sdb-2.4.2/.github/workflows/ci.yml new/sdb-2.5.0/.github/workflows/ci.yml --- old/sdb-2.4.2/.github/workflows/ci.yml 2026-03-11 20:13:35.000000000 +0100 +++ new/sdb-2.5.0/.github/workflows/ci.yml 2026-08-07 16:51:53.000000000 +0200 @@ -10,7 +10,7 @@ build-windows: runs-on: windows-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - name: Windows Dependencies run: pip install meson ninja - name: Building Sdb @@ -23,7 +23,7 @@ build-cxx: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - name: Building Sdb run: make xxx - name: Testing gperf @@ -31,7 +31,7 @@ build-python: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - name: Building Sdb run: export CFLAGS=-O0 ; make - name: Building python bindings @@ -42,7 +42,7 @@ runs-on: ubuntu-latest continue-on-error: true steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - name: install gperf run: sudo apt install gperf - name: building sdb @@ -55,7 +55,7 @@ # runs-on: ubuntu-latest # continue-on-error: true # steps: -# - uses: actions/checkout@v6 +# - uses: actions/checkout@v7 # - name: install gperf # run: sudo apt install gperf # - name: building sdb @@ -67,7 +67,7 @@ build-linux: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - name: Meson Sdb run: | pip install meson ninja @@ -90,7 +90,7 @@ build-heap: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - name: Building Sdb run: make heapasan - name: Running tests @@ -100,7 +100,7 @@ build-macos: runs-on: macos-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - name: Building Sdb run: make -j4 && cd .. - name: Packaging @@ -133,7 +133,7 @@ sys: [MINGW64, UCRT64] runs-on: windows-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - name: Setup MSYS2 uses: msys2/setup-msys2@v2 with: @@ -174,7 +174,7 @@ runs-on: ubuntu-24.04 steps: - name: Checkout code - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: fetch-depth: 0 # Download all git history and tags - name: Check if is a release @@ -201,7 +201,7 @@ dist/artifacts/sdb_cydia/*.deb steps: - name: Checkout code - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: fetch-depth: 0 # Download all git history and tags - name: Extract sdb version @@ -214,7 +214,7 @@ path: dist/artifacts - name: Create GitHub release id: create_release - uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0 + uses: softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # v3.0.2 with: name: ${{ steps.version.outputs.branch }} tag_name: ${{ needs.check_release.outputs.tag_name }} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sdb-2.4.2/.github/workflows/codeql.yml new/sdb-2.5.0/.github/workflows/codeql.yml --- old/sdb-2.4.2/.github/workflows/codeql.yml 2026-03-11 20:13:35.000000000 +0100 +++ new/sdb-2.5.0/.github/workflows/codeql.yml 2026-08-07 16:51:53.000000000 +0200 @@ -24,7 +24,7 @@ steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Initialize CodeQL uses: github/codeql-action/init@v4 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sdb-2.4.2/config.mk new/sdb-2.5.0/config.mk --- old/sdb-2.4.2/config.mk 2026-03-11 20:13:35.000000000 +0100 +++ new/sdb-2.5.0/config.mk 2026-08-07 16:51:53.000000000 +0200 @@ -1,4 +1,4 @@ -SDBVER=2.4.2 +SDBVER=2.5.0 PREFIX?=/usr BINDIR=${PREFIX}/bin diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sdb-2.4.2/include/sdb/cdb_make.h new/sdb-2.5.0/include/sdb/cdb_make.h --- old/sdb-2.4.2/include/sdb/cdb_make.h 2026-03-11 20:13:35.000000000 +0100 +++ new/sdb-2.5.0/include/sdb/cdb_make.h 2026-08-07 16:51:53.000000000 +0200 @@ -35,6 +35,7 @@ int cdb_make_addbegin(struct cdb_make *, unsigned int, unsigned int); int cdb_make_addend(struct cdb_make *, unsigned int, unsigned int,ut32); int cdb_make_add(struct cdb_make *, const char *, unsigned int, const char *, unsigned int); +void cdb_make_cancel(struct cdb_make *); int cdb_make_finish(struct cdb_make *); #endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sdb-2.4.2/include/sdb/sdb.h new/sdb-2.5.0/include/sdb/sdb.h --- old/sdb-2.4.2/include/sdb/sdb.h 2026-03-11 20:13:35.000000000 +0100 +++ new/sdb-2.5.0/include/sdb/sdb.h 2026-08-07 16:51:53.000000000 +0200 @@ -178,9 +178,10 @@ SDB_API char *sdb_querysf(Sdb* s, char *buf, size_t buflen, const char *fmt, ...); SDB_API int sdb_query_file(Sdb *s, const char* file); SDB_API bool sdb_exists(Sdb*, const char *key); -SDB_API bool sdb_remove(Sdb*, const char *key, ut32 cas); SDB_API int sdb_unset(Sdb*, const char *key, ut32 cas); SDB_API int sdb_nunset(Sdb*, ut64 nkey, ut32 cas); +SDB_API bool sdb_rename(Sdb*, const char *oldkey, const char *newkey, ut32 cas); +SDB_API int sdb_rename_prefix(Sdb*, const char *oldprefix, const char *newprefix); SDB_API int sdb_unset_like(Sdb *s, const char *k); SDB_API char** sdb_like(Sdb *s, const char *k, const char *v, SdbForeachCallback cb); @@ -228,11 +229,13 @@ // Gets a const pointer to the value associated with `key` SDB_API const char *sdb_const_get(Sdb*, const char *key, ut32 *cas); +SDB_API const char *sdb_const_getf(Sdb*, ut32 *cas, const char *fmt, ...) SDB_PRINTF_CHECK(3, 4); // Gets a const pointer to the value associated with `key` and returns in // `vlen` the length of the value string. SDB_API const char *sdb_const_get_len(Sdb* s, const char *key, int *vlen, ut32 *cas); SDB_API int sdb_set(Sdb*, const char *key, const char *data, ut32 cas); +SDB_API int sdb_setf(Sdb*, const char *data, ut32 cas, const char *fmt, ...) SDB_PRINTF_CHECK(4, 5); SDB_API int sdb_nset(Sdb*, ut64 nkey, const char *data, ut32 cas); SDB_API ut64 sdb_num_nget(Sdb *s, ut64 nkey, ut32 *cas); SDB_API int sdb_num_nset(Sdb* s, ut64 nkey, ut64 nval, ut32 cas); @@ -248,7 +251,9 @@ SDB_API bool sdb_num_exists(Sdb*, const char *key); SDB_API int sdb_num_base(const char *s); SDB_API ut64 sdb_num_get(Sdb* s, const char *key, ut32 *cas); +SDB_API ut64 sdb_num_getf(Sdb* s, ut32 *cas, const char *fmt, ...) SDB_PRINTF_CHECK(3, 4); SDB_API int sdb_num_set(Sdb* s, const char *key, ut64 v, ut32 cas); +SDB_API int sdb_num_setf(Sdb* s, ut64 v, ut32 cas, const char *fmt, ...) SDB_PRINTF_CHECK(4, 5); SDB_API int sdb_num_add(Sdb *s, const char *key, ut64 v, ut32 cas); SDB_API ut64 sdb_num_inc(Sdb* s, const char *key, ut64 n, ut32 cas); SDB_API ut64 sdb_num_dec(Sdb* s, const char *key, ut64 n, ut32 cas); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sdb-2.4.2/include/sdb/version.h new/sdb-2.5.0/include/sdb/version.h --- old/sdb-2.4.2/include/sdb/version.h 2026-03-11 20:13:35.000000000 +0100 +++ new/sdb-2.5.0/include/sdb/version.h 2026-08-07 16:51:53.000000000 +0200 @@ -1 +1 @@ -#define SDB_VERSION "2.4.2" +#define SDB_VERSION "2.5.0" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sdb-2.4.2/memcache/Makefile new/sdb-2.5.0/memcache/Makefile --- old/sdb-2.4.2/memcache/Makefile 2026-03-11 20:13:35.000000000 +0100 +++ new/sdb-2.5.0/memcache/Makefile 2026-08-07 16:51:53.000000000 +0200 @@ -2,6 +2,7 @@ BIN=mcsdbd CBIN=mcsdbc +NETTEST=test_net OBJS=main.o memcache.o net.o protocol.o client.o COBJS=client.o net.o memcache.o LIBS=../src/libsdb.a @@ -30,7 +31,7 @@ endif ${CC} mainclient.o ${COBJS} ${LDFLAGS} -o ${CBIN} ${LIBS} -${OBJS} mainclient.o: cmds.h +${OBJS} mainclient.o test_net.o: cmds.h ${BIN}: ${OBJS} ifneq ($(SILENT),) @@ -38,18 +39,24 @@ endif ${CC} -o ${BIN} $(CFLAGS) ${OBJS} ${LDFLAGS} ${LIBS} -cmds.h: cmds.o - ${HOST_CC} -o cmds ../src/util.c cmds.c ${CPPFLAGS} $(CFLAGS) ${HOST_CFLAGS} ${LDFLAGS} +cmds.h: cmds.c + ${HOST_CC} -o cmds cmds.c ${CPPFLAGS} $(CFLAGS) ${HOST_CFLAGS} ${LDFLAGS} ./cmds > cmds.h +${NETTEST}: test_net.o net.o + ${CC} test_net.o net.o ${LDFLAGS} -o ${NETTEST} + +check: ${NETTEST} + ./${NETTEST} + clean: rm -rf *.dSYM - rm -f *.sdb ${LIBA} ${COBJS} ${CBIN} mainclient.o - rm -f ${OBJS} ${BIN}cmds.h cmds cmds.o + rm -f *.sdb ${LIBA} ${COBJS} ${CBIN} mainclient.o ${NETTEST} test_net.o + rm -f ${OBJS} ${BIN} cmds.h cmds cmds.o test: ./mcsdbd & ./mcsdbc < test killall mcsdbd -.PHONY: clean test all loc +.PHONY: clean test check all loc diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sdb-2.4.2/memcache/main.c new/sdb-2.5.0/memcache/main.c --- old/sdb-2.4.2/memcache/main.c 2026-03-11 20:13:35.000000000 +0100 +++ new/sdb-2.5.0/memcache/main.c 2026-08-07 16:51:53.000000000 +0200 @@ -114,6 +114,12 @@ c->idx = 0; r = 0; rlen = c->len-c->idx; + if (rlen > MCSDB_MAX_BUFFER - 1 - c->idx) { + *c->buf = 0; + c->idx = 0; + c->len = 0; + return 0; + } if (rlen>0 && c->len>0) { r = read (c->fd, c->buf+c->idx, rlen); if (r==-1) { @@ -133,7 +139,7 @@ }// else eprintf ("Invalid %d read wtf\n", r); } c->idx += r; - c->buf[c->idx+1] = 0; + c->buf[c->idx] = 0; if (c->idx == c->len) { // printf ("END OF MODE 1 ***/*/*///*/* ((%s))\n", c->buf); return 1; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sdb-2.4.2/memcache/net.c new/sdb-2.5.0/memcache/net.c --- old/sdb-2.4.2/memcache/net.c 2026-03-11 20:13:35.000000000 +0100 +++ new/sdb-2.5.0/memcache/net.c 2026-08-07 16:51:53.000000000 +0200 @@ -2,18 +2,43 @@ #include "mcsdb.h" #include <sdb/types.h> +#include <errno.h> #include <netinet/in.h> #include <fcntl.h> #include <poll.h> #include <netdb.h> #include <signal.h> #include <stdio.h> +#include <stdlib.h> +#include <string.h> #include <sys/socket.h> #include <stdarg.h> +#include <unistd.h> static char netbuf[MCSDB_MAX_BUFFER]; static size_t netbuflen = 0; +static int net_write_all(int fd, const char *buf, size_t len) { + size_t written = 0; + if (fd == -1) { + fd = STDOUT_FILENO; + } + while (written < len) { + ssize_t n = write (fd, buf + written, len - written); + if (n < 0) { + if (errno == EINTR) { + continue; + } + return -1; + } + if (!n) { + return -1; + } + written += n; + } + return 0; +} + char *net_readnl(int fd) { int i = -1; char buf[MCSDB_MAX_BUFFER]; @@ -33,33 +58,69 @@ //NetSocket *net_accept(NetSocket *s) { } int net_flush(int fd) { - int n; + size_t len; if (netbuflen < 1) { return 0; } - if (fd == -1) { - fd = 1; //stdout + len = netbuflen; + if (net_write_all (fd, netbuf, len) < 0) { + netbuflen = 0; + return -1; } - n = write (fd, netbuf, netbuflen); netbuflen = 0; - return n; + return (int)len; } int net_printf (int fd, const char *fmt, ...) { - int n; + char *out; + int n, formatted; + size_t len; char buf[MCSDB_MAX_BUFFER]; va_list ap; + if (!fmt) { + return -1; + } va_start (ap, fmt); - n = vsnprintf (buf, sizeof (buf)-1, fmt, ap); + n = vsnprintf (buf, sizeof (buf), fmt, ap); va_end (ap); if (n < 0) { - return 0; + return -1; + } + len = n; + out = buf; + if (len >= sizeof (buf)) { + out = malloc (len + 1); + if (!out) { + return -1; + } + va_start (ap, fmt); + formatted = vsnprintf (out, len + 1, fmt, ap); + va_end (ap); + if (formatted != n) { + free (out); + return -1; + } + } + if (len > sizeof (netbuf)) { + if (net_flush (fd) < 0 || net_write_all (fd, out, len) < 0) { + if (out != buf) { + free (out); + } + return -1; + } + } else { + if (len > sizeof (netbuf) - netbuflen && net_flush (fd) < 0) { + if (out != buf) { + free (out); + } + return -1; + } + memcpy (netbuf + netbuflen, out, len); + netbuflen += len; } - if (netbuflen+n > sizeof (netbuf)) { - net_flush (fd); + if (out != buf) { + free (out); } - strcpy (netbuf + netbuflen, buf); - netbuflen += n; return n; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sdb-2.4.2/meson.build new/sdb-2.5.0/meson.build --- old/sdb-2.4.2/meson.build 2026-03-11 20:13:35.000000000 +0100 +++ new/sdb-2.5.0/meson.build 2026-08-07 16:51:53.000000000 +0200 @@ -1,4 +1,4 @@ -project('sdb', 'c', meson_version : '>=0.60.0', version : '2.4.2') +project('sdb', 'c', meson_version : '>=0.60.0', version : '2.5.0') pkgconfig_mod = import('pkgconfig') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sdb-2.4.2/src/Makefile new/sdb-2.5.0/src/Makefile --- old/sdb-2.4.2/src/Makefile 2026-03-11 20:13:35.000000000 +0100 +++ new/sdb-2.5.0/src/Makefile 2026-08-07 16:51:53.000000000 +0200 @@ -71,7 +71,8 @@ ifneq ($(SILENT),) @echo BIN ${BIN} endif - $(CC) ${LDFLAGS} -o ${BIN} entry.o ${OBJ} +# CFLAGS may force a source language; reset it before passing object files. + $(CC) ${CFLAGS} -x none ${LDFLAGS} -o ${BIN} entry.o ${OBJ} mrproper clean: rm -rf ${OBJ} ${SOBJ} entry.o libsdb.a a.out ${BIN} sdb.dSYM diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sdb-2.4.2/src/cdb_make.c new/sdb-2.5.0/src/cdb_make.c --- old/sdb-2.4.2/src/cdb_make.c 2026-03-11 20:13:35.000000000 +0100 +++ new/sdb-2.5.0/src/cdb_make.c 2026-08-07 16:51:53.000000000 +0200 @@ -122,21 +122,36 @@ return cdb_make_addend (c, keylen, datalen, sdb_hash (key)); } +void cdb_make_cancel(struct cdb_make *c) { + struct cdb_hplist *x, *n; + if (!c) { + return; + } + for (x = c->head; x; x = n) { + n = x->next; + cdb_alloc_free (x); + } + cdb_alloc_free (c->split); + c->head = NULL; + c->split = NULL; + c->hash = NULL; +} + int cdb_make_finish(struct cdb_make *c) { - int i; + int i, result; char buf[8]; struct cdb_hp *hp; - struct cdb_hplist *x, *n; + struct cdb_hplist *x; ut32 len, u, memsize, count, where; memsize = c->memsize + c->numentries; if (memsize > (UT32_MAX / sizeof (struct cdb_hp))) { - return 0; + goto fail; } // Allocate memory with proper alignment c->split = (struct cdb_hp *) cdb_alloc (memsize * sizeof (struct cdb_hp)); if (!c->split) { - return 0; + goto fail; } // Ensure the pointer arithmetic is done with proper type safety // by first casting to char* for byte-wise arithmetic, then back to struct cdb_hp* @@ -175,26 +190,25 @@ ut32_pack (buf, c->hash[u].h); ut32_pack (buf + 4, c->hash[u].p); if (!buffer_putalign (&c->b, buf, 8)) { - return 0; + goto fail; } if (!incpos (c, 8)) { - return 0; + goto fail; } } } if (!buffer_flush (&c->b)) { - return 0; + goto fail; } if (!seek_set (c->fd, 0)) { - return 0; - } - // free childs - for (x = c->head; x;) { - n = x->next; - cdb_alloc_free (x); - x = n; + goto fail; } - cdb_alloc_free (c->split); - return buffer_putflush (&c->b, c->final, sizeof c->final); + result = buffer_putflush (&c->b, c->final, sizeof c->final); + cdb_make_cancel (c); + return result; + +fail: + cdb_make_cancel (c); + return 0; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sdb-2.4.2/src/disk.c new/sdb-2.5.0/src/disk.c --- old/sdb-2.4.2/src/disk.c 2026-03-11 20:13:35.000000000 +0100 +++ new/sdb-2.5.0/src/disk.c 2026-08-07 16:51:53.000000000 +0200 @@ -3,7 +3,7 @@ #include <fcntl.h> #include <errno.h> #include <sys/stat.h> -#include "sdb/sdb.h" +#include "sdb_private.h" #if __SDB_WINDOWS__ @@ -49,6 +49,20 @@ #define r_sys_mkdir_failed() (errno != EEXIST) #endif +static bool unlink_path(const char *path) { + if (!path) { + return false; + } +#if __SDB_WINDOWS__ && UNICODE + wchar_t *wpath = r_sys_conv_utf8_to_utf16 (path); + bool ret = wpath && _wunlink (wpath) != -1; + sdb_gh_free (wpath); + return ret; +#else + return unlink (path) != -1; +#endif +} + static inline bool mkdirp(char *dir) { const char slash = DIRSEP; char *path = dir; @@ -95,9 +109,6 @@ memcpy (str, dir, nlen + 1); mkdirp (str); memcpy (str + nlen, ".tmp", 5); - if (s->fdump != -1) { - close (s->fdump); - } #if __SDB_WINDOWS__ && UNICODE wchar_t *wstr = r_sys_conv_utf8_to_utf16 (str); if (wstr) { @@ -114,61 +125,99 @@ sdb_gh_free (str); return false; } - cdb_make_start (&s->m, s->fdump); s->ndump = str; + if (!cdb_make_start (&s->m, s->fdump)) { + sdb_disk_abort (s); + return false; + } return true; } SDB_API bool sdb_disk_insert(Sdb* s, const char *key, const char *val) { - struct cdb_make *c = &s->m; - if (!key || !val) { + if (!s || !key || !val) { return false; } //if (!*val) return 0; //undefine variable if no value - return cdb_make_add (c, key, strlen (key), val, strlen (val)); + return cdb_make_add (&s->m, key, strlen (key), val, strlen (val)); +} + +static void close_current_database(Sdb *s) { + cdb_free (&s->db); + s->db.fd = -1; + if (s->fd != -1) { + close (s->fd); + s->fd = -1; + } +} + +SDB_IPI void sdb_disk_abort(Sdb *s) { + if (!s) { + return; + } + cdb_make_cancel (&s->m); + if (s->fdump >= 0) { + close (s->fdump); + } + s->fdump = -1; + if (s->ndump) { + unlink_path (s->ndump); + R_FREE (s->ndump); + } } -#define IFRET(x) if (x) ret = 0 SDB_API bool sdb_disk_finish(Sdb* s) { - bool ret = true; - IFRET (!cdb_make_finish (&s->m)); + if (!s) { + return false; + } + if (s->fdump < 0 || !s->ndump || !s->dir) { + sdb_disk_abort (s); + return false; + } + bool ret = cdb_make_finish (&s->m); #if USE_MMAN - IFRET (fsync (s->fdump)); + if (ret && fsync (s->fdump) == -1) { + ret = false; + } #endif - IFRET (close (s->fdump)); + if (close (s->fdump) == -1) { + ret = false; + } s->fdump = -1; - // close current fd to avoid sharing violations - if (s->fd != -1) { - close (s->fd); - s->fd = -1; + if (!ret) { + sdb_disk_abort (s); + return false; } #if __SDB_WINDOWS__ LPTSTR ndump_ = r_sys_conv_utf8_to_utf16 (s->ndump); LPTSTR dir_ = r_sys_conv_utf8_to_utf16 (s->dir); - - if (MoveFileEx (ndump_, dir_, MOVEFILE_REPLACE_EXISTING)) { - //eprintf ("Error 0x%02x\n", GetLastError ()); + if (!ndump_ || !dir_) { + sdb_gh_free (ndump_); + sdb_gh_free (dir_); + sdb_disk_abort (s); + return false; } + // Windows cannot replace a file while the existing database is open. + close_current_database (s); + ret = MoveFileEx (ndump_, dir_, MOVEFILE_REPLACE_EXISTING) != 0; sdb_gh_free (ndump_); sdb_gh_free (dir_); + if (!ret) { + sdb_disk_abort (s); + (void)sdb_open (s, s->dir); + return false; + } #else - if (s->ndump && s->dir) { - IFRET (rename (s->ndump, s->dir)); + if (rename (s->ndump, s->dir) == -1) { + sdb_disk_abort (s); + return false; } + close_current_database (s); #endif sdb_gh_free (s->ndump); s->ndump = NULL; - // always reopen just in case - { - int rr = sdb_open (s, s->dir); - if (ret && rr < 0) { - ret = false; - } - cdb_init (&s->db, s->fd); - } - return ret; + return sdb_open (s, s->dir) >= 0; } SDB_API bool sdb_disk_unlink(Sdb *s) { - return (s->dir && *(s->dir) && unlink (s->dir) != -1); + return s->dir && *s->dir && unlink_path (s->dir); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sdb-2.4.2/src/ht.inc.c new/sdb-2.5.0/src/ht.inc.c --- old/sdb-2.4.2/src/ht.inc.c 2026-03-11 20:13:35.000000000 +0100 +++ new/sdb-2.5.0/src/ht.inc.c 2026-08-07 16:51:53.000000000 +0200 @@ -198,7 +198,7 @@ } if (bt->count + 1 >= bt->size) { - bt->size = (bt->count + 5) * 2; + bt->size = (bt->count + 1) * 2; HT_(Kv) *newkvarr = (HT_(Kv)*)sdb_gh_realloc (bt->arr, (bt->size) * ht->opt.elem_size); if (!SDB_UNLIKELY (newkvarr)) { return NULL; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sdb-2.4.2/src/ns.c new/sdb-2.5.0/src/ns.c --- old/sdb-2.4.2/src/ns.c 2026-03-11 20:13:35.000000000 +0100 +++ new/sdb-2.5.0/src/ns.c 2026-08-07 16:51:53.000000000 +0200 @@ -59,7 +59,7 @@ sdb_free (child); } } - free (ns->name); + sdb_gh_free (ns->name); ns->name = NULL; } if (!deleted) { @@ -76,10 +76,10 @@ } s->ns->free = NULL; ls_delete (s->ns, it); // free (it) - free (ns->name); + sdb_gh_free (ns->name); ns->name = NULL; } - free (ns); + sdb_gh_free (ns); it = &next; } ls_free (s->ns); @@ -118,8 +118,8 @@ sdb_free (ns->sdb); } } - free (ns->name); - free (ns); + sdb_gh_free (ns->name); + sdb_gh_free (ns); } ls_free (s->ns); s->ns = NULL; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sdb-2.4.2/src/num.c new/sdb-2.5.0/src/num.c --- old/sdb-2.4.2/src/num.c 2026-03-11 20:13:35.000000000 +0100 +++ new/sdb-2.5.0/src/num.c 2026-08-07 16:51:53.000000000 +0200 @@ -1,6 +1,6 @@ /* sdb - MIT - Copyright 2011-2022 - pancake */ -#include "sdb/sdb.h" +#include "sdb_private.h" // check if key exists and if it's a number.. rename? SDB_API bool sdb_num_exists (Sdb *s, const char *key) { @@ -8,11 +8,22 @@ return o ? (*o >= '0' && *o <= '9'): false; } -SDB_API ut64 sdb_num_get(Sdb *s, const char *key, ut32 *cas) { - const char *v = sdb_const_get (s, key, cas); +static ut64 sdb_num_parse(const char *v) { return (!v || *v == '-') ? 0LL : sdb_atoi (v); } +SDB_API ut64 sdb_num_get(Sdb *s, const char *key, ut32 *cas) { + return sdb_num_parse (sdb_const_get (s, key, cas)); +} + +SDB_API ut64 sdb_num_getf(Sdb *s, ut32 *cas, const char *fmt, ...) { + va_list ap; + va_start (ap, fmt); + ut64 value = sdb_num_parse (sdb_const_vgetf (s, cas, fmt, ap)); + va_end (ap); + return value; +} + SDB_API int sdb_num_add(Sdb *s, const char *key, ut64 v, ut32 cas) { char *val, b[SDB_NUM_BUFSZ]; int numbase = sdb_num_base (sdb_const_get (s, key, NULL)); @@ -27,6 +38,22 @@ return sdb_set (s, key, val, cas); } +SDB_API int sdb_num_setf(Sdb *s, ut64 v, ut32 cas, const char *fmt, ...) { + va_list ap; + char key[SDB_MAX_KEY]; + char val[SDB_NUM_BUFSZ]; + int len, numbase; + + va_start (ap, fmt); + len = vsnprintf (key, sizeof (key), fmt, ap); + va_end (ap); + if (len < 0 || len >= (int)sizeof (key)) { + return 0; + } + numbase = sdb_num_base (sdb_const_get (s, key, NULL)); + return sdb_set (s, key, sdb_itoa (v, numbase, val, sizeof (val)), cas); +} + SDB_API ut64 sdb_num_inc(Sdb *s, const char *key, ut64 n2, ut32 cas) { ut32 c; ut64 n = sdb_num_get (s, key, &c); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sdb-2.4.2/src/sdb.c new/sdb-2.5.0/src/sdb.c --- old/sdb-2.4.2/src/sdb.c 2026-03-11 20:13:35.000000000 +0100 +++ new/sdb-2.5.0/src/sdb.c 2026-08-07 16:51:53.000000000 +0200 @@ -3,7 +3,7 @@ #include <fcntl.h> #include <errno.h> #include <sys/stat.h> -#include "sdb/sdb.h" +#include "sdb_private.h" static inline SdbKv *next_kv(HtPP *ht, SdbKv *kv) { return (SdbKv *)((uintptr_t)kv + ht->opt.elem_size); @@ -182,11 +182,11 @@ } sdb_ns_free (s); s->refs = 0; + sdb_journal_close (s); sdb_gh_free (s->name); sdb_gh_free (s->path); ls_free (s->ns); sdb_ht_free (s->ht); - sdb_journal_close (s); if (s->fd != -1) { close (s->fd); s->fd = -1; @@ -284,7 +284,27 @@ return sdb_const_get_len (s, key, NULL, cas); } -// TODO: add sdb_getf? +SDB_IPI const char *sdb_const_vgetf(Sdb *s, ut32 *cas, const char *fmt, va_list ap) { + if (fmt) { + char key[SDB_MAX_KEY]; + int len = vsnprintf (key, SDB_MAX_KEY, fmt, ap); + if (len >= 0 && len < SDB_MAX_KEY) { + return sdb_const_get (s, key, cas); + } + } + if (cas) { + *cas = 0; + } + return NULL; +} + +SDB_API const char *sdb_const_getf(Sdb *s, ut32 *cas, const char *fmt, ...) { + va_list ap; + va_start (ap, fmt); + const char *value = sdb_const_vgetf (s, cas, fmt, ap); + va_end (ap); + return value; +} SDB_API char *sdb_get_len(Sdb* s, const char *key, int *vlen, ut32 *cas) { const char *value = sdb_const_get_len (s, key, vlen, cas); @@ -309,9 +329,89 @@ return sdb_nset (s, nkey, "", cas); } -/* remove from memory */ -SDB_API bool sdb_remove(Sdb *s, const char *key, ut32 cas) { - return sdb_ht_delete (s->ht, key); +SDB_API bool sdb_rename(Sdb *s, const char *oldkey, const char *newkey, ut32 cas) { + if (!s || !oldkey || !newkey) { + return false; + } + char *value = sdb_get (s, oldkey, NULL); + if (!value) { + return false; + } + if (!sdb_unset (s, oldkey, cas)) { + sdb_gh_free (value); + return false; + } + return sdb_set_owned (s, newkey, value, 0) != 0; +} + +typedef struct { + const char *prefix; + size_t len; + SdbList *keys; +} RenamePrefixData; + +static bool rename_prefix_cb(void *user, const char *k, const char *v) { + RenamePrefixData *rpd = (RenamePrefixData *)user; + (void)v; + if (strncmp (k, rpd->prefix, rpd->len)) { + return true; + } + char *key = sdb_strdup (k); + if (!key) { + return false; + } + if (!ls_append (rpd->keys, key)) { + sdb_gh_free (key); + return false; + } + return true; +} + +static char *rename_prefix_key(const char *newprefix, const char *suffix) { + size_t plen = strlen (newprefix); + size_t slen = strlen (suffix); + if (SZT_ADD_OVFCHK (plen, slen) || SZT_ADD_OVFCHK (plen + slen, 1)) { + return NULL; + } + char *key = (char *)sdb_gh_malloc (plen + slen + 1); + if (key) { + memcpy (key, newprefix, plen); + memcpy (key + plen, suffix, slen + 1); + } + return key; +} + +SDB_API int sdb_rename_prefix(Sdb *s, const char *oldprefix, const char *newprefix) { + if (!s || !oldprefix || !newprefix) { + return 0; + } + SdbList *keys = ls_newf ((SdbListFree)sdb_gh_free); + if (!keys) { + return 0; + } + RenamePrefixData rpd = { + .prefix = oldprefix, + .len = strlen (oldprefix), + .keys = keys + }; + if (!sdb_foreach (s, rename_prefix_cb, &rpd)) { + ls_free (keys); + return 0; + } + int count = 0; + SdbListIter *iter; + char *key; + ls_foreach_cast (keys, iter, char *, key) { + char *newkey = rename_prefix_key (newprefix, key + rpd.len); + if (newkey) { + if (sdb_rename (s, key, newkey, 0)) { + count++; + } + sdb_gh_free (newkey); + } + } + ls_free (keys); + return count; } // alias for '-key=str'.. '+key=str' concats @@ -606,6 +706,7 @@ cdb_findstart (&s->db); SdbKv *kv = sdb_ht_find_kvp (s->ht, key, &found); if (found && sdbkv_value (kv)) { + bool free_val = owned; if (cdb_findnext (&s->db, sdb_hash (key), key, klen)) { if (cas && kv->cas != cas) { if (owned) { @@ -625,6 +726,7 @@ kv->base.value_len = vlen; sdb_gh_free (kv->base.value); kv->base.value = val; // owned + free_val = false; } else { if ((ut32)vlen > kv->base.value_len) { sdb_gh_free (kv->base.value); @@ -634,9 +736,13 @@ kv->base.value_len = vlen; } } else { + cas = nextcas (kv); sdb_ht_delete (s->ht, key); } sdb_hook_call (s, key, val); + if (free_val) { + sdb_gh_free (val); + } return cas; } // empty values are also stored @@ -669,6 +775,16 @@ return sdb_set_internal (s, key, (char *)val, false, cas); } +SDB_API int sdb_setf(Sdb *s, const char *val, ut32 cas, const char *fmt, ...) { + va_list ap; + char key[SDB_MAX_KEY]; + int len; + va_start (ap, fmt); + len = vsnprintf (key, sizeof (key), fmt, ap); + va_end (ap); + return len >= 0 && len < (int)sizeof (key)? sdb_set (s, key, val, cas): 0; +} + SDB_API int sdb_nset(Sdb* s, ut64 nkey, const char *val, ut32 cas) { char buf[SDB_NUM_BUFSZ]; const char *key = sdb_itoa (nkey, 16, buf, sizeof (buf)); @@ -864,35 +980,57 @@ return sdb_foreach_end (s, true); } +typedef struct { + Sdb *s; + HtPP *persisted; +} SdbSync; + +static bool _sync_has_persisted(SdbSync *sync, const char *key) { + return ht_pp_find (sync->persisted, key, NULL) != NULL; +} + static bool _insert_into_disk(void *user, const char *key, const char *value) { - Sdb *s = (Sdb *)user; - if (s) { - sdb_disk_insert (s, key, value); - return true; + SdbSync *sync = (SdbSync *)user; + if (!sync || !sync->s) { + return false; } - return false; + // An updated key may occur more than once in an existing CDB. + return _sync_has_persisted (sync, key) + || sdb_disk_insert (sync->s, key, value); +} + +static bool _mark_persisted(void *user, const char *key, const char *value) { + SdbSync *sync = (SdbSync *)user; + (void)value; + return sync && (_sync_has_persisted (sync, key) + || ht_pp_insert (sync->persisted, key, (void *)1)); } -static bool _remove_afer_insert(void *user, const char *k, const char *v) { +static bool _remove_persisted(void *user, const void *key, const void *value) { Sdb *s = (Sdb *)user; - if (s) { - sdb_ht_delete (s->ht, k); - return true; - } - return false; + (void)value; + (void)sdb_ht_delete (s->ht, (const char *)key); + return true; } SDB_API bool sdb_sync(Sdb* s) { - bool result; ut32 i; - if (!s || !sdb_disk_create (s)) { + if (!s) { return false; } - result = sdb_foreach_cdb (s, _insert_into_disk, _remove_afer_insert, s); - if (!result) { + HtPP *persisted = ht_pp_new0 (); + if (!persisted) { return false; } + SdbSync sync = { s, persisted }; + if (!sdb_disk_create (s)) { + ht_pp_free (persisted); + return false; + } + if (!sdb_foreach_cdb (s, _insert_into_disk, _mark_persisted, &sync)) { + goto fail; + } /* append new keyvalues */ for (i = 0; i < s->ht->size; i++) { @@ -903,18 +1041,29 @@ BUCKET_FOREACH_SAFE (s->ht, bt, j, count, kv) { if (sdbkv_key (kv)) { const char *kvv = sdbkv_value (kv); - if (kvv && *kvv && !kv->expire) { - if (sdb_disk_insert (s, sdbkv_key (kv), sdbkv_value (kv))) { - sdb_remove (s, sdbkv_key (kv), 0); + if (kvv && *kvv && !kv->expire + && !_sync_has_persisted (&sync, sdbkv_key (kv))) { + if (!sdb_disk_insert (s, sdbkv_key (kv), kvv) + || !_mark_persisted (&sync, sdbkv_key (kv), kvv)) { + goto fail; } } } } } - sdb_disk_finish (s); - sdb_journal_clear (s); - // TODO: sdb_reset memory state? + if (!sdb_disk_finish (s)) { + goto fail; + } + // Dirty entries remain available for retry until the replacement succeeds. + ht_pp_foreach (persisted, _remove_persisted, s); + ht_pp_free (persisted); + (void)sdb_journal_clear (s); return true; + +fail: + sdb_disk_abort (s); + ht_pp_free (persisted); + return false; } SDB_API void sdb_dump_begin(Sdb* s) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sdb-2.4.2/src/sdb_private.h new/sdb-2.5.0/src/sdb_private.h --- old/sdb-2.4.2/src/sdb_private.h 1970-01-01 01:00:00.000000000 +0100 +++ new/sdb-2.5.0/src/sdb_private.h 2026-08-07 16:51:53.000000000 +0200 @@ -0,0 +1,10 @@ +#ifndef SDB_PRIVATE_H +#define SDB_PRIVATE_H + +#include <stdarg.h> +#include "sdb/sdb.h" + +SDB_IPI const char *sdb_const_vgetf(Sdb *s, ut32 *cas, const char *fmt, va_list ap); +SDB_IPI void sdb_disk_abort(Sdb *s); + +#endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sdb-2.4.2/src/tool.c new/sdb-2.5.0/src/tool.c --- old/sdb-2.4.2/src/tool.c 2026-03-11 20:13:35.000000000 +0100 +++ new/sdb-2.5.0/src/tool.c 2026-08-07 16:51:53.000000000 +0200 @@ -202,9 +202,9 @@ strbuf_appendf (sb, 0, "\t{\"%s\", \"%s\"},\n", ev, ek); } } - sdb_gh_free (ek); - sdb_gh_free (ev); } + sdb_gh_free (ek); + sdb_gh_free (ev); } ls_free (l); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sdb-2.4.2/test/unit/meson.build new/sdb-2.5.0/test/unit/meson.build --- old/sdb-2.4.2/test/unit/meson.build 2026-03-11 20:13:35.000000000 +0100 +++ new/sdb-2.5.0/test/unit/meson.build 2026-08-07 16:51:53.000000000 +0200 @@ -5,7 +5,8 @@ 'diff', 'hash', 'ls', - 'sdb' + 'sdb', + 'sync' ] foreach test : tests diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sdb-2.4.2/test/unit/test_getf.c new/sdb-2.5.0/test/unit/test_getf.c --- old/sdb-2.4.2/test/unit/test_getf.c 1970-01-01 01:00:00.000000000 +0100 +++ new/sdb-2.5.0/test/unit/test_getf.c 2026-08-07 16:51:53.000000000 +0200 @@ -0,0 +1,47 @@ +#include "minunit.h" +#include <sdb/sdb.h> + +static bool test_getf(void) { + Sdb *db = sdb_new0 (); + mu_assert_notnull (db, "database"); + mu_assert ("set", sdb_set (db, "user.alice.42", "admin", 0)); + mu_assert ("formatted set", sdb_setf (db, "staff", 0, "group.%s.%d", "alice", 42)); + mu_assert_streq (sdb_const_get (db, "group.alice.42", NULL), "staff", "formatted set value"); + mu_assert ("formatted number set", sdb_num_setf (db, 42, 0, "number.%s", "answer")); + mu_assert_eq (sdb_num_get (db, "number.answer", NULL), 42, "formatted number set value"); + + ut32 const_cas = 0; + const char *const_value = sdb_const_getf (db, &const_cas, + "user.%s.%d", "alice", 42); + mu_assert_streq (const_value, "admin", "formatted const get"); + mu_assert ("const get CAS", const_cas != 0); + + char maxkey[SDB_MAX_KEY]; + memset (maxkey, 'x', sizeof (maxkey) - 1); + maxkey[sizeof (maxkey) - 1] = 0; + mu_assert ("set maximum key", sdb_set (db, maxkey, "maximum", 0)); + mu_assert_streq (sdb_const_getf (db, NULL, "%s", maxkey), "maximum", + "maximum formatted key"); + + char oversized[SDB_MAX_KEY + 1]; + memset (oversized, 'x', sizeof (oversized) - 1); + oversized[sizeof (oversized) - 1] = 0; + ut32 cas = 123; + mu_assert_null (sdb_const_getf (db, &cas, "%s", oversized), + "oversized formatted key"); + mu_assert_eq (cas, 0, "oversized key CAS"); + mu_assert_null (sdb_const_getf (db, NULL, "missing.%d", 7), + "missing formatted key"); + + sdb_free (db); + mu_end; +} + +int all_tests(void) { + mu_run_test (test_getf); + return tests_passed != tests_run; +} + +int main(void) { + return all_tests (); +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sdb-2.4.2/test/unit/test_sdb.c new/sdb-2.5.0/test/unit/test_sdb.c --- old/sdb-2.4.2/test/unit/test_sdb.c 2026-03-11 20:13:35.000000000 +0100 +++ new/sdb-2.5.0/test/unit/test_sdb.c 2026-08-07 16:51:53.000000000 +0200 @@ -105,6 +105,34 @@ mu_end; } +bool test_sdb_unset_return(void) { + Sdb *db = sdb_new0 (); + sdb_set (db, "foo", "bar", 0); + int ret = sdb_unset (db, "foo", 0); + mu_assert ("Unset failed", ret != 0); + mu_assert ("Item not deleted", !sdb_exists (db, "foo")); + sdb_free (db); + mu_end; +} + +bool test_sdb_rename_prefix(void) { + Sdb *db = sdb_new0 (); + sdb_set (db, "zign|old|foo", "aaa", 0); + sdb_set (db, "zign|old|bar", "bbb", 0); + sdb_set (db, "zign|keep|foo", "ccc", 0); + + int ret = sdb_rename_prefix (db, "zign|old|", "zign|new|"); + mu_assert_eq (ret, 2, "Renamed keys"); + mu_assert ("Old foo still exists", !sdb_exists (db, "zign|old|foo")); + mu_assert ("Old bar still exists", !sdb_exists (db, "zign|old|bar")); + mu_assert ("New foo missing", !strcmp (sdb_const_get (db, "zign|new|foo", NULL), "aaa")); + mu_assert ("New bar missing", !strcmp (sdb_const_get (db, "zign|new|bar", NULL), "bbb")); + mu_assert ("Unmatched key changed", !strcmp (sdb_const_get (db, "zign|keep|foo", NULL), "ccc")); + + sdb_free (db); + mu_end; +} + bool test_sdb_milset(void) { int i = 0; const int MAX = 19999999; @@ -530,6 +558,8 @@ mu_run_test (test_sdb_list_delete); mu_run_test (test_sdb_delete_none); mu_run_test (test_sdb_delete_alot); + mu_run_test (test_sdb_unset_return); + mu_run_test (test_sdb_rename_prefix); mu_run_test (test_sdb_milset); mu_run_test (test_sdb_milset_random); mu_run_test (test_sdb_list_big); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sdb-2.4.2/test/unit/test_sync.c new/sdb-2.5.0/test/unit/test_sync.c --- old/sdb-2.4.2/test/unit/test_sync.c 1970-01-01 01:00:00.000000000 +0100 +++ new/sdb-2.5.0/test/unit/test_sync.c 2026-08-07 16:51:53.000000000 +0200 @@ -0,0 +1,120 @@ +#include "minunit.h" +#include <sdb/sdb.h> +#include <fcntl.h> +#include <sys/stat.h> + +#if !__SDB_WINDOWS__ +#include <signal.h> +#include <sys/resource.h> + +static void cleanup_sync_files(const char *path) { + char file[SDB_MAX_PATH]; + unlink (path); + snprintf (file, sizeof (file), "%s.tmp", path); + unlink (file); + snprintf (file, sizeof (file), "%s.journal", path); + unlink (file); +} + +static bool database_value_is(const char *path, const char *key, const char *expected) { + Sdb *db = sdb_new (NULL, path, 0); + if (!db) { + return false; + } + const char *value = sdb_const_get (db, key, NULL); + bool result = expected? value && !strcmp (value, expected): !value; + sdb_free (db); + return result; +} +#endif + +static bool test_sync_failure_preserves_database(void) { +#if __SDB_WINDOWS__ + mu_ignore; +#else + const char *path = ".test-sync-failure.sdb"; + char tmp[SDB_MAX_PATH]; + struct rlimit saved_limit, failing_limit; + struct sigaction saved_action, ignored_action; + struct stat journal_stat; + cleanup_sync_files (path); + + Sdb *db = sdb_new (NULL, path, 0); + bool initial_ok = db + && sdb_set (db, "stable", "old", 0) + && sdb_sync (db) + && sdb_journal_open (db); + if (!initial_ok) { + sdb_free (db); + cleanup_sync_files (path); + mu_fail ("initial database setup failed"); + } + bool updates_ok = sdb_set (db, "stable", "new", 0) + && sdb_set (db, "pending", "value", 0); + if (!updates_ok || getrlimit (RLIMIT_FSIZE, &saved_limit) == -1) { + sdb_free (db); + cleanup_sync_files (path); + mu_fail ("failed to prepare sync failure"); + } + + memset (&ignored_action, 0, sizeof (ignored_action)); + ignored_action.sa_handler = SIG_IGN; + sigemptyset (&ignored_action.sa_mask); + if (sigaction (SIGXFSZ, &ignored_action, &saved_action) == -1) { + sdb_free (db); + cleanup_sync_files (path); + mu_fail ("failed to ignore SIGXFSZ"); + } + failing_limit = saved_limit; + failing_limit.rlim_cur = 0; + if (setrlimit (RLIMIT_FSIZE, &failing_limit) == -1) { + sigaction (SIGXFSZ, &saved_action, NULL); + sdb_free (db); + cleanup_sync_files (path); + mu_fail ("failed to limit output size"); + } + + bool sync_result = sdb_sync (db); + bool limit_restored = setrlimit (RLIMIT_FSIZE, &saved_limit) != -1; + bool action_restored = sigaction (SIGXFSZ, &saved_action, NULL) != -1; + const char *stable = sdb_const_get (db, "stable", NULL); + const char *pending = sdb_const_get (db, "pending", NULL); + bool memory_retained = stable && pending + && !strcmp (stable, "new") && !strcmp (pending, "value"); + bool journal_retained = fstat (db->journal, &journal_stat) != -1 + && journal_stat.st_size > 0; + bool disk_preserved = database_value_is (path, "stable", "old") + && database_value_is (path, "pending", NULL); + snprintf (tmp, sizeof (tmp), "%s.tmp", path); + bool temp_removed = access (tmp, F_OK) == -1; + + bool retry_result = sdb_sync (db); + bool disk_updated = database_value_is (path, "stable", "new") + && database_value_is (path, "pending", "value"); + bool journal_cleared = fstat (db->journal, &journal_stat) != -1 + && journal_stat.st_size == 0; + + sdb_free (db); + cleanup_sync_files (path); + mu_assert_false (sync_result, "failed sync result"); + mu_assert_true (limit_restored, "restore file-size limit"); + mu_assert_true (action_restored, "restore SIGXFSZ handler"); + mu_assert_true (memory_retained, "pending changes retained in memory"); + mu_assert_true (journal_retained, "journal retained after failure"); + mu_assert_true (disk_preserved, "existing database preserved"); + mu_assert_true (temp_removed, "failed temporary database removed"); + mu_assert_true (retry_result, "retry succeeds"); + mu_assert_true (disk_updated, "retry persists pending changes"); + mu_assert_true (journal_cleared, "journal cleared after success"); + mu_end; +#endif +} + +static int all_tests(void) { + mu_run_test (test_sync_failure_preserves_database); + return tests_passed != tests_run; +} + +int main(int argc, char **argv) { + return all_tests (); +}
