netatalk  4.6.0
Free and Open Source Apple Filing Protocol (AFP) Server
Loading...
Searching...
No Matches
cnid_sqlite.c File Reference
#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_dbcnid_sqlite_new (struct vol *vol)
struct _cnid_dbcnid_sqlite_open (struct cnid_open_args *args)

Variables

struct _cnid_module cnid_sqlite_module

Macro Definition Documentation

◆ CNID_SQLITE_ADD_ATTEMPTS

#define CNID_SQLITE_ADD_ATTEMPTS   16

◆ CNID_SQLITE_BUSY_TIMEOUT

#define CNID_SQLITE_BUSY_TIMEOUT   3000

◆ CNID_SQLITE_EMPTY_PROBE_TRIES

#define CNID_SQLITE_EMPTY_PROBE_TRIES   3

◆ CNID_SQLITE_HINT_RESERVE

#define CNID_SQLITE_HINT_RESERVE   (1u << 28)

◆ CNID_SQLITE_MAX_HINT

#define CNID_SQLITE_MAX_HINT   (UINT32_MAX - CNID_SQLITE_HINT_RESERVE)

Function Documentation

◆ close_prepared_stmt()

void close_prepared_stmt ( CNID_sqlite_private * db)
static

Finalize every prepared statement and clear the handles.

◆ cnid_sqlite_add()

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_sqlite_begin()

int cnid_sqlite_begin ( sqlite3 * con,
int * owned )
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.

◆ cnid_sqlite_close()

void cnid_sqlite_close ( struct _cnid_db * cdb)

◆ cnid_sqlite_commit()

int cnid_sqlite_commit ( sqlite3 * con,
int * owned )
static

Commit the owned transaction, releasing ownership only on success.

◆ cnid_sqlite_delete()

int cnid_sqlite_delete ( struct _cnid_db * cdb,
const cnid_t id )

◆ cnid_sqlite_delete_by_uuid()

int cnid_sqlite_delete_by_uuid ( sqlite3 * con,
const char * sql,
const char * uuid )
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.

Returns
0 on success, -1 with errno set by cnid_sqlite_set_errno()

◆ cnid_sqlite_delete_sequence_row()

int cnid_sqlite_delete_sequence_row ( sqlite3 * con,
const char * uuid )
static

Remove a stale volume's AUTOINCREMENT high-water mark.

◆ cnid_sqlite_delete_volumes_row()

int cnid_sqlite_delete_volumes_row ( sqlite3 * con,
const char * uuid )
static

Remove a stale volume's row from the volumes table.

◆ cnid_sqlite_execute()

int cnid_sqlite_execute ( sqlite3 * con,
const char * sql )
static

Run one SQL statement, classifying a failure into errno.

Returns
0 on success, -1 with errno set by cnid_sqlite_set_errno()

◆ cnid_sqlite_find()

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.

◆ cnid_sqlite_get()

cnid_t cnid_sqlite_get ( struct _cnid_db * cdb,
cnid_t did,
const char * name,
size_t len )

◆ cnid_sqlite_getstamp()

int cnid_sqlite_getstamp ( struct _cnid_db * cdb,
void * buffer,
const size_t len )

Caller passes buffer where we will store the db stamp

◆ cnid_sqlite_hint_usable()

bool cnid_sqlite_hint_usable ( cnid_t hint)
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_sqlite_lookup()

cnid_t cnid_sqlite_lookup ( struct _cnid_db * cdb,
const struct stat * st,
cnid_t did,
const char * name,
size_t len )

◆ cnid_sqlite_new()

struct _cnid_db * cnid_sqlite_new ( struct vol * vol)
static

◆ cnid_sqlite_open()

struct _cnid_db * cnid_sqlite_open ( struct cnid_open_args * args)

◆ cnid_sqlite_rebuild_add()

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 )

◆ cnid_sqlite_resolve()

char * cnid_sqlite_resolve ( struct _cnid_db * cdb,
cnid_t * id,
void * buffer,
size_t len )

◆ cnid_sqlite_rollback()

void cnid_sqlite_rollback ( sqlite3 * con,
int * owned )
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.

◆ cnid_sqlite_rowid_ok()

bool cnid_sqlite_rowid_ok ( uint64_t rowid)
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.

◆ cnid_sqlite_seed_sequence()

int cnid_sqlite_seed_sequence ( CNID_sqlite_private * db)
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.

◆ cnid_sqlite_set_errno()

void cnid_sqlite_set_errno ( int sqlite_return)
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.

◆ cnid_sqlite_stmt_ready()

bool cnid_sqlite_stmt_ready ( sqlite3_stmt * stmt,
const char * caller )
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.

◆ cnid_sqlite_stmt_reset()

void cnid_sqlite_stmt_reset ( sqlite3_stmt * stmt)
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.

◆ cnid_sqlite_update()

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_sqlite_uuid_usable()

bool cnid_sqlite_uuid_usable ( const char * uuid)
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.

◆ cnid_sqlite_wipe()

int cnid_sqlite_wipe ( struct _cnid_db * cdb)

◆ init_prepared_stmt()

int init_prepared_stmt ( CNID_sqlite_private * db)
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.

◆ init_prepared_stmt_one()

int init_prepared_stmt_one ( CNID_sqlite_private * db,
sqlite3_stmt ** stmt,
const char * tag,
const char * sql_fmt )
static

Prepare one per-volume statement, replacing any previous handle.

Parameters
[in,out]dbbackend private data
[out]stmtstatement handle to finalize and re-prepare
[in]tagstatement name, for the debug log only
[in]sql_fmtSQL with one s for the volume's table name, or none

Variable Documentation

◆ cnid_sqlite_module

struct _cnid_module cnid_sqlite_module
Initial value:
= {
"sqlite",
{ NULL, NULL },
0
}
struct _cnid_db * cnid_sqlite_open(struct cnid_open_args *args)
Definition cnid_sqlite.c:1820
#define NULL
Definition utf8util.c:47