Portable file primitives (open with delete sharing, unlink, mkdir,
mkdirp and atomic replace) live in file.c with a single UTF-16 path
conversion, leaving disk.c with the cdb write logic only.
sdb_lock_db and sdb_unlock_db are now the only way to take or release
a database lock, replacing the copies in sdb.c, main.c and tool.c.
MoveFileEx cannot replace a file that another process keeps open, so
syncing a database failed while any reader such as radare2 had it open.
Open databases with FILE_SHARE_DELETE and replace them with a POSIX
semantics rename, falling back to MoveFileEx where it is unsupported.
sdb -r and "sdb db =" opened the existing database before loading the
text, so keys removed from the source were merged back in. Build into
an empty database instead, holding the lock on the target path.
sdb -r also exited 0 when a .sdb or .c output could not be written,
and succeeded as long as any single file did.
sdb_new() recorded the lock mode even when no lock was taken for
unnamed databases, so sdb_fini() passed an uninitialized path to
sdb_unlock(). sdb_file() ignored lock path and lock failures, letting a
later unlock remove a lock owned by another process, and the sdb_new()
fail path leaked the lock it had just taken.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
createdb() ignored the result of sdb_sync() and always exited 0, even
when nothing could be written. sdb_ns_sync() now returns whether every
namespace was saved, and SIGHUP keeps the unsaved changes in memory
instead of reopening the database from disk when the sync fails.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Parallel "sdb db key=value" runs opened the database without a lock,
rewrote it through the same .tmp file and the last writer dropped the
other keys; on Windows the MoveFileEx replace also failed while another
process had the database open. Wait on the database lock for queries,
release it before exiting and before SIGHUP reopens the database.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
sdb_lock() returned false but left the newly created .lock file behind,
so sdb_lock_wait() kept retrying against its own lock forever.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Running several sdb-tool instances on the same .sdb.txt raced on the
shared .tmp file and on MoveFileEx, silently leaving stale or missing
databases. dothesdb now waits on the db lock and reports sync failures.
On Windows the lock is an exclusive FILE_FLAG_DELETE_ON_CLOSE handle,
so the OS releases it when the owner exits or crashes and waiters
never hang on a stale lock file.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
When sdb_ns_unset() released the last reference to the removed
namespace, sdb_free() went through sdb_ns_free(), which forces the
references of every descendant to 1 and frees them, even those still
owned by a sibling namespace or by the caller; a backreference could
free the parent. Reset the namespaces with the reference-aware
sdb_ns_reset() before releasing the last reference.
Fix and tests as suggested by trufae in review.