#include <arpa/inet.h>#include <ctype.h>#include <errno.h>#include <fcntl.h>#include <inttypes.h>#include <net/if.h>#include <netdb.h>#include <netinet/in.h>#include <netinet/tcp.h>#include <stdlib.h>#include <stdio.h>#include <string.h>#include <sys/param.h>#include <sys/socket.h>#include <sys/stat.h>#include <sys/time.h>#include <sys/uio.h>#include <sys/un.h>#include <time.h>#include <bstrlib.h>#include <sqlite3.h>#include <atalk/adouble.h>#include <atalk/cnid_bdb_private.h>#include <atalk/cnid_sqlite_private.h>#include <atalk/errchk.h>#include <atalk/globals.h>#include <atalk/logger.h>#include <atalk/unix.h>#include <atalk/util.h>#include <atalk/volume.h>Macros | |
| #define | CNID_SQLITE_BUSY_TIMEOUT 3000 |
| #define | CNID_SQLITE_ADD_ATTEMPTS 16 |
| #define | CNID_SQLITE_EMPTY_PROBE_TRIES 3 |
| #define | CNID_SQLITE_HINT_RESERVE (1u << 28) |
| #define | CNID_SQLITE_MAX_HINT (UINT32_MAX - CNID_SQLITE_HINT_RESERVE) |
Functions | |
| static void | cnid_sqlite_set_errno (int sqlite_return) |
| Map a sqlite3 result code onto the CNID error contract in errno. | |
| static int | init_prepared_stmt_one (CNID_sqlite_private *db, sqlite3_stmt **stmt, const char *tag, const char *sql_fmt) |
| Prepare one per-volume statement, replacing any previous handle. | |
| static int | init_prepared_stmt (CNID_sqlite_private *db) |
| (Re-)prepare every per-volume statement, at open and after a wipe | |
| static void | close_prepared_stmt (CNID_sqlite_private *db) |
| Finalize every prepared statement and clear the handles. | |
| static bool | cnid_sqlite_hint_usable (cnid_t hint) |
| Whether a CNID hint from AppleDouble/EA metadata is safe to bind. | |
| static bool | cnid_sqlite_rowid_ok (uint64_t rowid) |
| Whether a rowid read from the database still fits a CNID. | |
| static bool | cnid_sqlite_uuid_usable (const char *uuid) |
| Whether a volume UUID is safe to interpolate as a table name. | |
| static int | cnid_sqlite_execute (sqlite3 *con, const char *sql) |
| Run one SQL statement, classifying a failure into errno. | |
| static int | cnid_sqlite_delete_by_uuid (sqlite3 *con, const char *sql, const char *uuid) |
| Delete one row identified by a UUID, through a bound parameter. | |
| static int | cnid_sqlite_delete_volumes_row (sqlite3 *con, const char *uuid) |
| Remove a stale volume's row from the volumes table. | |
| static int | cnid_sqlite_delete_sequence_row (sqlite3 *con, const char *uuid) |
| Remove a stale volume's AUTOINCREMENT high-water mark. | |
| static void | cnid_sqlite_stmt_reset (sqlite3_stmt *stmt) |
| Reset a prepared statement without disturbing the classification. | |
| static bool | cnid_sqlite_stmt_ready (sqlite3_stmt *stmt, const char *caller) |
| Whether a prepared statement is available to bind and step. | |
| static int | cnid_sqlite_begin (sqlite3 *con, int *owned) |
| Open a transaction and mark it owned by the calling function. | |
| static int | cnid_sqlite_commit (sqlite3 *con, int *owned) |
| Commit the owned transaction, releasing ownership only on success. | |
| static void | cnid_sqlite_rollback (sqlite3 *con, int *owned) |
| Abandon an owned transaction without disturbing the classification. | |
| static int | cnid_sqlite_seed_sequence (CNID_sqlite_private *db) |
| Reseed the volume's AUTOINCREMENT sequence to the reserved floor. | |
| int | cnid_sqlite_delete (struct _cnid_db *cdb, const cnid_t id) |
| void | cnid_sqlite_close (struct _cnid_db *cdb) |
| int | cnid_sqlite_update (struct _cnid_db *cdb, cnid_t id, const struct stat *st, cnid_t did, const char *name, size_t len) |
| cnid_t | cnid_sqlite_lookup (struct _cnid_db *cdb, const struct stat *st, cnid_t did, const char *name, size_t len) |
| cnid_t | cnid_sqlite_add (struct _cnid_db *cdb, const struct stat *st, cnid_t did, const char *name, size_t len, cnid_t hint) |
| cnid_t | cnid_sqlite_get (struct _cnid_db *cdb, cnid_t did, const char *name, size_t len) |
| char * | cnid_sqlite_resolve (struct _cnid_db *cdb, cnid_t *id, void *buffer, size_t len) |
| int | cnid_sqlite_getstamp (struct _cnid_db *cdb, void *buffer, const size_t len) |
| int | cnid_sqlite_find (struct _cnid_db *cdb, const char *name, size_t namelen, cnid_t scope_did, void *buffer, size_t buflen, bool *more_available) |
| Backend implementation of cnid_find() for the sqlite backend. | |
| cnid_t | cnid_sqlite_rebuild_add (struct _cnid_db *cdb, const struct stat *st, cnid_t did, const char *name, size_t len, cnid_t hint) |
| int | cnid_sqlite_wipe (struct _cnid_db *cdb) |
| static struct _cnid_db * | cnid_sqlite_new (struct vol *vol) |
| struct _cnid_db * | cnid_sqlite_open (struct cnid_open_args *args) |
Variables | |
| struct _cnid_module | cnid_sqlite_module |
| #define CNID_SQLITE_ADD_ATTEMPTS 16 |
| #define CNID_SQLITE_BUSY_TIMEOUT 3000 |
| #define CNID_SQLITE_EMPTY_PROBE_TRIES 3 |
| #define CNID_SQLITE_HINT_RESERVE (1u << 28) |
| #define CNID_SQLITE_MAX_HINT (UINT32_MAX - CNID_SQLITE_HINT_RESERVE) |
|
static |
Finalize every prepared statement and clear the handles.
| cnid_t cnid_sqlite_add | ( | struct _cnid_db * | cdb, |
| const struct stat * | st, | ||
| cnid_t | did, | ||
| const char * | name, | ||
| size_t | len, | ||
| cnid_t | hint ) |
|
static |
Open a transaction and mark it owned by the calling function.
An exec'd COMMIT can fail and error-jump to cleanup, leaving the connection inside a transaction. Ownership is tracked so a cleanup handler can only abandon a transaction its own function started, never a caller's.
| void cnid_sqlite_close | ( | struct _cnid_db * | cdb | ) |
|
static |
Commit the owned transaction, releasing ownership only on success.
|
static |
Delete one row identified by a UUID, through a bound parameter.
The UUID is data here, not an identifier, so it never enters the SQL text: the stale entries this removes are read back out of the world-writable database file, where a value chosen to break out of a quoted string would otherwise run as SQL under the become_root() the cleanup holds.
|
static |
Remove a stale volume's AUTOINCREMENT high-water mark.
|
static |
Remove a stale volume's row from the volumes table.
|
static |
Run one SQL statement, classifying a failure into errno.
| int cnid_sqlite_find | ( | struct _cnid_db * | cdb, |
| const char * | name, | ||
| size_t | namelen, | ||
| cnid_t | scope_did, | ||
| void * | buffer, | ||
| size_t | buflen, | ||
| bool * | more_available ) |
Backend implementation of cnid_find() for the sqlite backend.
Parameters are pre-validated by the libatalk/cnid/cnid.c wrapper, so cdb, name, namelen and buflen are all sane on entry. Detects truncation by binding LIMIT max_results+1 and observing whether the extra row was produced; reports it via more_available.
namelen is unused: the SQLite backend builds the LIKE pattern via asprintf("%%%s%%", name), which already requires a NUL-terminated name. The parameter is kept to satisfy the cnid_db function-pointer signature shared with the dbd / mysql backends.
scope_did selects the statement: CNID_INVALID searches the whole volume, anything else only the subtree rooted at that directory.
| int cnid_sqlite_getstamp | ( | struct _cnid_db * | cdb, |
| void * | buffer, | ||
| const size_t | len ) |
Caller passes buffer where we will store the db stamp
|
static |
Whether a CNID hint from AppleDouble/EA metadata is safe to bind.
Rejects the reserved range below CNID_START and anything above CNID_SQLITE_MAX_HINT, which as an explicit rowid would raise the AUTOINCREMENT high-water mark towards the depletion reset.
| cnid_t cnid_sqlite_lookup | ( | struct _cnid_db * | cdb, |
| const struct stat * | st, | ||
| cnid_t | did, | ||
| const char * | name, | ||
| size_t | len ) |
| struct _cnid_db * cnid_sqlite_open | ( | struct cnid_open_args * | args | ) |
| cnid_t cnid_sqlite_rebuild_add | ( | struct _cnid_db * | cdb, |
| const struct stat * | st, | ||
| cnid_t | did, | ||
| const char * | name, | ||
| size_t | len, | ||
| cnid_t | hint ) |
|
static |
Abandon an owned transaction without disturbing the classification.
Runs from cleanup paths after errno has been classified; the ROLLBACK enters the VFS and could leave errno set by a speculative syscall.
|
static |
Whether a rowid read from the database still fits a CNID.
The Id column is a 64-bit sqlite integer while a CNID is 32 bits. Narrowing one that does not fit would produce the id of an unrelated live row, which the caller would then hand to a client, or delete.
|
static |
Reseed the volume's AUTOINCREMENT sequence to the reserved floor.
UPDATE first, then INSERT only where the UPDATE changed no row: sqlite_sequence has no UNIQUE constraint to upsert against. Runs inside the caller's transaction; on failure the caller owns the rollback.
|
static |
Map a sqlite3 result code onto the CNID error contract in errno.
CNID_ERR_BUSY covers what clears on its own: contention, disk full, I/O error, out of memory, and SQLITE_PROTOCOL, which is WAL's bounded locking handshake giving up. SQLITE_LOCKED is not in that set — it reports a same-connection or shared-cache conflict the busy handler never waits on, so the state survives a retry.
CNID_ERR_DB, which get_id() in etc/afpd/file.c answers by ending the session, means the backend itself is unreachable — for the dbd backend, cnid_metad being gone. A local database file has no such state: the connection outlives whatever one statement returns, so an unrecognised code fails the single operation as CNID_ERR_CORRUPT.
|
static |
Whether a prepared statement is available to bind and step.
The handles are NULL when cnid_sqlite_wipe()'s re-preparation stopped partway. sqlite3_bind_*() tolerates a NULL statement; sqlite3_step() on one is undefined, so every entry point checks the handles it uses.
|
static |
Reset a prepared statement without disturbing the classification.
sqlite3_reset() enters the VFS and can leave errno set by a speculative syscall. It runs in every cleanup path after errno has been classified, and that classification is the caller's only signal for what failed.
| int cnid_sqlite_update | ( | struct _cnid_db * | cdb, |
| cnid_t | id, | ||
| const struct stat * | st, | ||
| cnid_t | did, | ||
| const char * | name, | ||
| size_t | len ) |
|
static |
Whether a volume UUID is safe to interpolate as a table name.
A table name cannot be a bound parameter, so it is built into the SQL text. uuid_strip_dashes() yields exactly 32 hex digits; the UUIDs read back out of the volumes table are only as trustworthy as the world-writable database file, so anything else is refused rather than quoted and hoped for.
| int cnid_sqlite_wipe | ( | struct _cnid_db * | cdb | ) |
|
static |
(Re-)prepare every per-volume statement, at open and after a wipe
A failure leaves the handles past that point NULL; cnid_sqlite_stmt_ready() rejects those.
|
static |
Prepare one per-volume statement, replacing any previous handle.
| [in,out] | db | backend private data |
| [out] | stmt | statement handle to finalize and re-prepare |
| [in] | tag | statement name, for the debug log only |
| [in] | sql_fmt | SQL with one s for the volume's table name, or none |
| struct _cnid_module cnid_sqlite_module |