2012-01-12 03:38:24 +00:00
/*
2012-05-19 04:39:50 +00:00
Serval Rhizome file sharing
2013-12-04 06:26:55 +00:00
Copyright ( C ) 2012 - 2013 Serval Project Inc .
2012-08-23 08:13:35 +00:00
2012-01-12 03:38:24 +00:00
This program is free software ; you can redistribute it and / or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation ; either version 2
of the License , or ( at your option ) any later version .
2012-08-23 08:13:35 +00:00
2012-01-12 03:38:24 +00:00
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
2012-08-23 08:13:35 +00:00
2012-01-12 03:38:24 +00:00
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 , USA .
*/
2012-08-24 05:56:25 +00:00
# define __RHIZOME_INLINE
2012-09-25 04:01:34 +00:00
# include <stdlib.h>
# include <time.h>
2013-10-02 08:49:20 +00:00
# include <ctype.h>
2013-10-03 13:46:45 +00:00
# include <assert.h>
2012-02-23 02:15:42 +00:00
# include "serval.h"
2012-12-04 03:42:28 +00:00
# include "conf.h"
2012-01-12 03:38:24 +00:00
# include "rhizome.h"
2012-05-20 03:32:41 +00:00
# include "strbuf.h"
2012-12-04 03:42:28 +00:00
# include "strbuf_helpers.h"
2012-09-25 04:01:34 +00:00
# include "str.h"
2013-10-16 03:00:00 +00:00
# include "keyring.h"
2012-01-12 03:38:24 +00:00
2012-12-04 03:42:28 +00:00
static char rhizome_thisdatastore_path [ 256 ] ;
2012-05-15 03:26:10 +00:00
2013-10-03 13:46:45 +00:00
static int rhizome_delete_manifest_retry ( sqlite_retry_state * retry , const rhizome_bid_t * bidp ) ;
2013-10-10 07:53:06 +00:00
static int rhizome_delete_file_retry ( sqlite_retry_state * retry , const rhizome_filehash_t * hashp ) ;
2013-10-03 13:46:45 +00:00
static int rhizome_delete_payload_retry ( sqlite_retry_state * retry , const rhizome_bid_t * bidp ) ;
2013-08-21 06:15:18 +00:00
2012-05-15 03:26:10 +00:00
const char * rhizome_datastore_path ( )
{
2012-12-04 03:42:28 +00:00
if ( ! rhizome_thisdatastore_path [ 0 ] )
2012-05-15 03:26:10 +00:00
rhizome_set_datastore_path ( NULL ) ;
return rhizome_thisdatastore_path ;
}
int rhizome_set_datastore_path ( const char * path )
{
2012-12-04 03:42:28 +00:00
strbuf b = strbuf_local ( rhizome_thisdatastore_path , sizeof rhizome_thisdatastore_path ) ;
strbuf_path_join ( b , serval_instancepath ( ) , config . rhizome . datastore_path , path , NULL ) ;
INFOF ( " Rhizome datastore path = %s " , alloca_str_toprint ( rhizome_thisdatastore_path ) ) ;
2012-05-15 03:26:10 +00:00
return 0 ;
}
2012-01-12 03:38:24 +00:00
2012-05-14 06:02:28 +00:00
int form_rhizome_datastore_path ( char * buf , size_t bufsiz , const char * fmt , . . . )
{
2012-05-23 08:41:34 +00:00
va_list ap ;
2012-05-20 03:32:41 +00:00
strbuf b = strbuf_local ( buf , bufsiz ) ;
2012-08-23 08:13:35 +00:00
strbuf_puts ( b , rhizome_datastore_path ( ) ) ;
2012-05-24 01:58:32 +00:00
if ( fmt ) {
va_start ( ap , fmt ) ;
if ( * strbuf_substr ( b , - 1 ) ! = ' / ' )
strbuf_putc ( b , ' / ' ) ;
strbuf_vsprintf ( b , fmt , ap ) ;
va_end ( ap ) ;
}
if ( strbuf_overrun ( b ) ) {
WHY ( " Path buffer overrun " ) ;
return 0 ;
}
return 1 ;
}
2012-05-14 06:02:28 +00:00
int create_rhizome_datastore_dir ( )
{
2012-12-11 05:29:46 +00:00
if ( config . debug . rhizome ) DEBUGF ( " mkdirs(%s, 0700) " , rhizome_datastore_path ( ) ) ;
2013-04-04 07:07:49 +00:00
return emkdirs ( rhizome_datastore_path ( ) , 0700 ) ;
2012-05-14 06:02:28 +00:00
}
2013-11-12 07:44:14 +00:00
sqlite3 * rhizome_db = NULL ;
2014-02-17 02:56:03 +00:00
serval_uuid_t rhizome_db_uuid ;
2012-01-12 03:38:24 +00:00
/* XXX Requires a messy join that might be slow. */
2013-10-03 19:20:37 +00:00
int rhizome_manifest_priority ( sqlite_retry_state * retry , const rhizome_bid_t * bidp )
2012-01-12 03:38:24 +00:00
{
2013-12-11 00:41:34 +00:00
uint64_t result = 0 ;
if ( sqlite_exec_uint64_retry ( retry , & result ,
2013-10-02 15:46:10 +00:00
" SELECT max(grouplist.priorty) FROM GROUPLIST,MANIFESTS,GROUPMEMBERSHIPS "
" WHERE MANIFESTS.id = ? "
" AND GROUPLIST.id = GROUPMEMBERSHIPS.groupid "
" AND GROUPMEMBERSHIPS.manifestid = MANIFESTS.id; " ,
2013-10-03 19:20:37 +00:00
RHIZOME_BID_T , bidp ,
END
2012-06-08 03:43:26 +00:00
) = = - 1
)
return - 1 ;
return ( int ) result ;
2012-01-12 03:38:24 +00:00
}
2012-12-11 05:29:46 +00:00
int is_debug_rhizome ( )
{
return config . debug . rhizome ;
}
int is_debug_rhizome_ads ( )
{
return config . debug . rhizome_ads ;
}
static int ( * sqlite_trace_func ) ( ) = is_debug_rhizome ;
2012-10-16 06:16:52 +00:00
const struct __sourceloc * sqlite_trace_whence = NULL ;
2013-11-13 02:15:32 +00:00
static int sqlite_trace_done ;
2012-10-10 02:52:30 +00:00
2013-11-13 02:15:32 +00:00
/* This is called by SQLite when executing a statement using sqlite3_step(). Unfortunately, it is
* not called on PRAGMA statements , and possibly others . Hence the use of the profile callback ( see
* below ) .
*
* @ author Andrew Bettison < andrew @ servalproject . com >
*/
2013-12-09 07:52:18 +00:00
static void sqlite_trace_callback ( void * UNUSED ( context ) , const char * rendered_sql )
2012-10-09 07:13:34 +00:00
{
2012-12-11 05:29:46 +00:00
if ( sqlite_trace_func ( ) )
2012-10-16 06:16:52 +00:00
logMessage ( LOG_LEVEL_DEBUG , sqlite_trace_whence ? * sqlite_trace_whence : __HERE__ , " %s " , rendered_sql ) ;
2013-11-13 02:15:32 +00:00
+ + sqlite_trace_done ;
}
/* This is called by SQLite when an executed statement finishes. We use it to log rendered SQL
* statements when the trace callback ( above ) has not been called , eg , for PRAGMA statements . This
* requires that the ' sqlite_trace_done ' static be reset to zero whenever a new prepared statement
* is about to be stepped .
*
* @ author Andrew Bettison < andrew @ servalproject . com >
*/
2013-12-09 07:52:18 +00:00
static void sqlite_profile_callback ( void * context , const char * rendered_sql , sqlite_uint64 UNUSED ( nanosec ) )
2013-11-13 02:15:32 +00:00
{
if ( ! sqlite_trace_done )
sqlite_trace_callback ( context , rendered_sql ) ;
2012-10-10 02:52:30 +00:00
}
/* This function allows code like:
*
* debugflags_t oldmask = sqlite_set_debugmask ( DEBUG_SOMETHING_ELSE ) ;
* . . .
* sqlite3_stmt * statement = sqlite_prepare ( & retry , " select blah blah blah " ) ;
* while ( sqlite_step_retry ( & retry , statement ) = = SQLITE_ROW ) {
* // do blah blah blah
* }
* . . .
* sqlite_set_debugmask ( oldmask ) ;
* return result ;
*
* so that code can choose which DEBUG_ flags control the logging of rendered SQL queries .
*
* @ author Andrew Bettison < andrew @ servalproject . com >
*/
2012-12-11 05:29:46 +00:00
int ( * sqlite_set_tracefunc ( int ( * newfunc ) ( ) ) ) ( )
2012-10-10 02:52:30 +00:00
{
2012-12-11 05:29:46 +00:00
int ( * oldfunc ) ( ) = sqlite_trace_func ;
sqlite_trace_func = newfunc ;
return oldfunc ;
2012-10-09 07:13:34 +00:00
}
2013-12-09 07:52:18 +00:00
void sqlite_log ( void * UNUSED ( ignored ) , int result , const char * msg )
2013-10-02 08:49:20 +00:00
{
2013-01-06 03:48:36 +00:00
WARNF ( " Sqlite: %d %s " , result , msg ) ;
}
2013-11-28 07:14:37 +00:00
void verify_bundles ( )
{
2013-01-16 00:26:09 +00:00
// assume that only the manifest itself can be trusted
// fetch all manifests and reinsert them.
sqlite_retry_state retry = SQLITE_RETRY_STATE_DEFAULT ;
// This cursor must be ordered descending as re-inserting the manifests will give them a new higher manifest id.
// If we didn't, we'd get stuck in an infinite loop.
sqlite3_stmt * statement = sqlite_prepare ( & retry , " SELECT ROWID, MANIFEST FROM MANIFESTS ORDER BY ROWID DESC; " ) ;
2013-11-28 07:14:37 +00:00
while ( sqlite_step_retry ( & retry , statement ) = = SQLITE_ROW ) {
2013-01-16 00:26:09 +00:00
sqlite3_int64 rowid = sqlite3_column_int64 ( statement , 0 ) ;
2013-12-19 08:37:14 +00:00
const void * blob = sqlite3_column_blob ( statement , 1 ) ;
size_t blob_length = sqlite3_column_bytes ( statement , 1 ) ;
rhizome_manifest * m = rhizome_new_manifest ( ) ;
if ( m ) {
memcpy ( m - > manifestdata , blob , blob_length ) ;
m - > manifest_all_bytes = blob_length ;
int ret = - 1 ;
if ( rhizome_manifest_parse ( m ) ! = - 1
& & rhizome_manifest_validate ( m )
& & rhizome_manifest_verify ( m )
) {
assert ( m - > finalised ) ;
// Store it again, to ensure that MANIFESTS columns are up to date.
2013-12-19 08:43:39 +00:00
ret = rhizome_store_manifest ( m ) ;
2013-12-19 08:37:14 +00:00
}
if ( ret ) {
if ( config . debug . rhizome )
DEBUGF ( " Removing invalid manifest entry @%lld " , rowid ) ;
sqlite_exec_void_retry ( & retry , " DELETE FROM MANIFESTS WHERE ROWID = ?; " , INT64 , rowid , END ) ;
}
rhizome_manifest_free ( m ) ;
2013-01-16 00:26:09 +00:00
}
}
sqlite3_finalize ( statement ) ;
}
2012-10-15 07:38:31 +00:00
/*
Refactor manifest: specific setter functions
Replace generic rhizome_manifest_set() and rhizome_manifest_set_ll()
with per-field setter functions, eg, rhizome_manifest_set_filesize().
Struct rhizome_manifest elements for all known fields, to replace the
use of rhizome_manifest_get() and rhizome_manifest_get_ll() everywhere:
sender, recipient, service, name, date, bundle_key.
Add boolean validity flags for binary blob types, to avoid having to compare
with many bytes of all-zero to detect presence, eg, has_sender, has_recipient,
has_author, has_bundle_key. These maintained by the setter functions.
Rename existing manifest struct elements to be the same as their field
names: fileLength -> filesize, journalTail -> tail.
More use of unsigned int, size_t and uint64_t for payload sizes, offsets, byte
counts, etc. especially in rhizome_store.c and meshms.c. More uniform use of
size_t to dimension memory buffers. Fix some printf(3) style format strings
for 64-bit correctness on 32-bit systems. Use new constant RHIZOME_SIZE_UNSET
instead of -1 to indicate unknown dimension, and explicitly assert its absence
before comparisons and arithmetic, for safety.
Replace some 'int' loop variables with 'unsigned' where appropriate.
Fix bugs discovered in MeshMS bundle private/public key generation and
bundle secret key handling for export/extract commands.
Instrument the first MeshMS test case to aid debugging.
New debug config flag: debug.manifest logs all modifications to all manifest
fields by setter functions.
Rename debug config flag: debug.rhizome_bind -> debug.rhizome_sql_bind.
2013-10-30 12:52:19 +00:00
* The MANIFESTS table ' author ' column records the cryptographically verified SID of the author that
* has write permission on the bundle , ie , possesses the Rhizome secret key that generated the BID ,
* and hence can derive the Bundle Secret from the bundle ' s BK field :
*
* - The MANIFESTS table ' author ' column is set to the author SID when a bundle is created locally
* by a non - secret identity , so no verification need be performed for one ' s own bundles while they
* remain in the local Rhizome store .
*
* - When a bundle is imported , the ' author ' column is set to NULL to indicate that no verification
* has passed yet . This includes one ' s own bundles that have been purged from the local Rhizome
* store then recovered from a remote Rhizome node .
*
* - When a manifest with NULL ' author ' is examined closely , ie extracted , not merely listed , the
* keyring is searched for an identity that is the author . If the identity is found and its
* Rhizome Secret unlocks the Bundle Key ( ie , reveals a Bundle Secret that yields the Bundle ' s ID
* as its public key ) , the MANIFESTS table ' author ' column is updated . This allows one to regain
* the ability to overwrite one ' s own bundles that have been lost but
* recovered from an exterior Rhizome node .
*
2012-10-15 07:38:31 +00:00
* - The above check automates the " own bundle recovery " mechanism at the expense of a CPU - heavy
* cryptographic check every time a foreign bundle is examined , but at least listing is fast .
* This will not scale as many identities are added to the keyring . It will eventually have to be
* replaced with a means to cache positive and negative verifications in the Rhizome db for local ,
* non - secret identities .
*
* - - Andrew Bettison < andrew @ servalproject . com > , October 2012
*/
2012-01-12 03:38:24 +00:00
int rhizome_opendb ( )
{
2013-11-12 07:44:14 +00:00
if ( rhizome_db ) {
assert ( uuid_is_valid ( & rhizome_db_uuid ) ) ;
return 0 ;
}
2012-01-12 03:38:24 +00:00
2012-06-27 07:24:42 +00:00
IN ( ) ;
if ( create_rhizome_datastore_dir ( ) = = - 1 ) {
RETURN ( WHY ( " No Directory " ) ) ;
}
2012-06-15 08:40:10 +00:00
char dbpath [ 1024 ] ;
2013-01-06 09:13:14 +00:00
if ( ! sqlite3_temp_directory ) {
if ( ! FORM_RHIZOME_DATASTORE_PATH ( dbpath , " " ) ) {
RETURN ( WHY ( " Invalid path " ) ) ;
}
sqlite3_temp_directory = sqlite3_mprintf ( " %s " , dbpath ) ;
}
2012-06-27 07:24:42 +00:00
if ( ! FORM_RHIZOME_DATASTORE_PATH ( dbpath , " rhizome.db " ) ) {
RETURN ( WHY ( " Invalid path " ) ) ;
}
2012-01-12 03:38:24 +00:00
2013-01-06 03:48:36 +00:00
sqlite3_config ( SQLITE_CONFIG_LOG , sqlite_log , NULL ) ;
2012-06-27 07:24:42 +00:00
if ( sqlite3_open ( dbpath , & rhizome_db ) ) {
RETURN ( WHYF ( " SQLite could not open database %s: %s " , dbpath , sqlite3_errmsg ( rhizome_db ) ) ) ;
}
2012-10-09 07:13:34 +00:00
sqlite3_trace ( rhizome_db , sqlite_trace_callback , NULL ) ;
2013-11-13 02:15:32 +00:00
sqlite3_profile ( rhizome_db , sqlite_profile_callback , NULL ) ;
2012-12-11 05:29:46 +00:00
int loglevel = ( config . debug . rhizome ) ? LOG_LEVEL_DEBUG : LOG_LEVEL_SILENT ;
2012-01-12 03:38:24 +00:00
2012-03-04 23:05:12 +00:00
/* Read Rhizome configuration */
2013-10-06 19:24:46 +00:00
if ( config . debug . rhizome )
DEBUGF ( " Rhizome will use % " PRIu64 " B of storage for its database. " , ( uint64_t ) config . rhizome . database_size ) ;
2013-01-06 02:34:49 +00:00
sqlite_retry_state retry = SQLITE_RETRY_STATE_DEFAULT ;
2012-10-03 15:54:39 +00:00
2013-12-11 00:41:34 +00:00
uint64_t version ;
if ( sqlite_exec_uint64_retry ( & retry , & version , " PRAGMA user_version; " , END ) = = - 1 )
2013-02-20 04:14:29 +00:00
RETURN ( - 1 ) ;
2013-01-06 02:34:49 +00:00
if ( version < 1 ) {
/* Create tables as required */
2013-10-02 15:46:10 +00:00
sqlite_exec_void_loglevel ( loglevel , " PRAGMA auto_vacuum=2; " , END ) ;
if ( sqlite_exec_void_retry ( & retry , " CREATE TABLE IF NOT EXISTS GROUPLIST(id text not null primary key, closed integer,ciphered integer,priority integer); " , END ) = = - 1
| | sqlite_exec_void_retry ( & retry , " CREATE TABLE IF NOT EXISTS MANIFESTS(id text not null primary key, version integer,inserttime integer, filesize integer, filehash text, author text, bar blob, manifest blob); " , END ) = = - 1
| | sqlite_exec_void_retry ( & retry , " CREATE TABLE IF NOT EXISTS FILES(id text not null primary key, length integer, highestpriority integer, datavalid integer, inserttime integer); " , END ) = = - 1
| | sqlite_exec_void_retry ( & retry , " CREATE TABLE IF NOT EXISTS FILEBLOBS(id text not null primary key, data blob); " , END ) = = - 1
| | sqlite_exec_void_retry ( & retry , " DROP TABLE IF EXISTS FILEMANIFESTS; " , END ) = = - 1
| | sqlite_exec_void_retry ( & retry , " CREATE TABLE IF NOT EXISTS GROUPMEMBERSHIPS(manifestid text not null, groupid text not null); " , END ) = = - 1
| | sqlite_exec_void_retry ( & retry , " CREATE TABLE IF NOT EXISTS VERIFICATIONS(sid text not null, did text, name text, starttime integer, endtime integer, signature blob); " , END ) = = - 1
2013-01-06 02:34:49 +00:00
) {
RETURN ( WHY ( " Failed to create schema " ) ) ;
}
/* Create indexes if they don't already exist */
2013-10-02 15:46:10 +00:00
sqlite_exec_void_loglevel ( LOG_LEVEL_WARN , " CREATE INDEX IF NOT EXISTS bundlesizeindex ON manifests (filesize); " , END ) ;
sqlite_exec_void_loglevel ( LOG_LEVEL_WARN , " CREATE INDEX IF NOT EXISTS IDX_MANIFESTS_HASH ON MANIFESTS(filehash); " , END ) ;
sqlite_exec_void_loglevel ( LOG_LEVEL_WARN , " PRAGMA user_version=1; " , END ) ;
2013-01-06 02:34:49 +00:00
}
2013-01-16 00:26:09 +00:00
if ( version < 2 ) {
2013-10-02 15:46:10 +00:00
sqlite_exec_void_loglevel ( LOG_LEVEL_WARN , " ALTER TABLE MANIFESTS ADD COLUMN service text; " , END ) ;
sqlite_exec_void_loglevel ( LOG_LEVEL_WARN , " ALTER TABLE MANIFESTS ADD COLUMN name text; " , END ) ;
sqlite_exec_void_loglevel ( LOG_LEVEL_WARN , " ALTER TABLE MANIFESTS ADD COLUMN sender text collate nocase; " , END ) ;
sqlite_exec_void_loglevel ( LOG_LEVEL_WARN , " ALTER TABLE MANIFESTS ADD COLUMN recipient text collate nocase; " , END ) ;
2013-01-16 00:26:09 +00:00
// if more bundle verification is required in later upgrades, move this to the end, don't run it more than once.
verify_bundles ( ) ;
2013-10-02 15:46:10 +00:00
sqlite_exec_void_loglevel ( LOG_LEVEL_WARN , " PRAGMA user_version=2; " , END ) ;
2013-01-16 00:26:09 +00:00
}
2013-02-15 06:38:51 +00:00
if ( version < 3 ) {
2013-10-02 15:46:10 +00:00
sqlite_exec_void_loglevel ( LOG_LEVEL_WARN , " CREATE INDEX IF NOT EXISTS IDX_MANIFESTS_ID_VERSION ON MANIFESTS(id, version); " , END ) ;
sqlite_exec_void_loglevel ( LOG_LEVEL_WARN , " PRAGMA user_version=3; " , END ) ;
2013-02-15 06:38:51 +00:00
}
2013-07-22 05:34:02 +00:00
if ( version < 4 ) {
2013-10-02 15:46:10 +00:00
sqlite_exec_void_loglevel ( LOG_LEVEL_WARN , " ALTER TABLE MANIFESTS ADD COLUMN tail integer; " , END ) ;
sqlite_exec_void_loglevel ( LOG_LEVEL_WARN , " PRAGMA user_version=4; " , END ) ;
2013-07-22 05:34:02 +00:00
}
2013-11-12 07:44:14 +00:00
if ( version < 5 ) {
sqlite_exec_void_loglevel ( LOG_LEVEL_WARN , " CREATE TABLE IF NOT EXISTS IDENTITY(uuid text not null); " , END ) ;
sqlite_exec_void_loglevel ( LOG_LEVEL_WARN , " PRAGMA user_version=5; " , END ) ;
}
char buf [ UUID_STRLEN + 1 ] ;
int r = sqlite_exec_strbuf_retry ( & retry , strbuf_local ( buf , sizeof buf ) , " SELECT uuid from IDENTITY LIMIT 1; " , END ) ;
if ( r = = - 1 )
RETURN ( - 1 ) ;
if ( r ) {
if ( ! str_to_uuid ( buf , & rhizome_db_uuid , NULL ) ) {
WHYF ( " IDENTITY table contains malformed UUID %s -- overwriting " , alloca_str_toprint ( buf ) ) ;
if ( uuid_generate_random ( & rhizome_db_uuid ) = = - 1 )
RETURN ( WHY ( " Cannot generate new UUID for Rhizome database " ) ) ;
2014-02-17 02:56:03 +00:00
if ( sqlite_exec_void_retry ( & retry , " UPDATE IDENTITY SET uuid = ? LIMIT 1; " , SERVAL_UUID_T , & rhizome_db_uuid , END ) = = - 1 )
2013-11-12 07:44:14 +00:00
RETURN ( WHY ( " Failed to update new UUID in Rhizome database " ) ) ;
if ( config . debug . rhizome )
DEBUGF ( " Updated Rhizome database UUID to %s " , alloca_uuid_str ( rhizome_db_uuid ) ) ;
}
} else if ( r = = 0 ) {
if ( uuid_generate_random ( & rhizome_db_uuid ) = = - 1 )
RETURN ( WHY ( " Cannot generate UUID for Rhizome database " ) ) ;
2014-02-17 02:56:03 +00:00
if ( sqlite_exec_void_retry ( & retry , " INSERT INTO IDENTITY (uuid) VALUES (?); " , SERVAL_UUID_T , & rhizome_db_uuid , END ) = = - 1 )
2013-11-12 07:44:14 +00:00
RETURN ( WHY ( " Failed to insert UUID into Rhizome database " ) ) ;
if ( config . debug . rhizome )
DEBUGF ( " Set Rhizome database UUID to %s " , alloca_uuid_str ( rhizome_db_uuid ) ) ;
}
2013-01-17 01:13:31 +00:00
// TODO recreate tables with collate nocase on hex columns
2013-11-12 07:44:14 +00:00
2013-01-06 02:34:49 +00:00
/* Future schema updates should be performed here.
The above schema can be assumed to exist .
All changes should attempt to preserve any existing data */
2013-11-12 07:44:14 +00:00
2012-12-28 02:47:04 +00:00
// We can't delete a file that is being transferred in another process at this very moment...
2013-02-20 04:13:20 +00:00
if ( config . rhizome . clean_on_open )
2013-02-20 04:14:29 +00:00
rhizome_cleanup ( NULL ) ;
2013-11-12 07:44:14 +00:00
INFOF ( " Opened Rhizome database %s, UUID=%s " , dbpath , alloca_uuid_str ( rhizome_db_uuid ) ) ;
2012-06-27 07:24:42 +00:00
RETURN ( 0 ) ;
2013-02-16 17:47:24 +00:00
OUT ( ) ;
2012-01-12 03:38:24 +00:00
}
2012-10-30 05:42:40 +00:00
int rhizome_close_db ( )
{
2013-01-03 03:46:33 +00:00
IN ( ) ;
2012-10-30 05:42:40 +00:00
if ( rhizome_db ) {
2013-08-16 05:27:28 +00:00
rhizome_cache_close ( ) ;
2013-01-06 09:13:14 +00:00
if ( ! sqlite3_get_autocommit ( rhizome_db ) ) {
WHY ( " Uncommitted transaction! " ) ;
2013-10-02 15:46:10 +00:00
sqlite_exec_void ( " ROLLBACK; " , END ) ;
2013-01-06 09:13:14 +00:00
}
2012-10-30 05:42:40 +00:00
sqlite3_stmt * stmt = NULL ;
while ( ( stmt = sqlite3_next_stmt ( rhizome_db , stmt ) ) ) {
const char * sql = sqlite3_sql ( stmt ) ;
WARNF ( " closing Rhizome db with unfinalised statement: %s " , sql ? sql : " BLOB " ) ;
}
2012-10-29 01:25:14 +00:00
int r = sqlite3_close ( rhizome_db ) ;
if ( r ! = SQLITE_OK )
2013-01-03 03:46:33 +00:00
RETURN ( WHYF ( " Failed to close sqlite database, %s " , sqlite3_errmsg ( rhizome_db ) ) ) ;
2012-10-29 01:25:14 +00:00
}
rhizome_db = NULL ;
2013-01-03 03:46:33 +00:00
RETURN ( 0 ) ;
2013-02-16 17:47:24 +00:00
OUT ( ) ;
2012-10-29 01:25:14 +00:00
}
2012-08-22 09:39:30 +00:00
/* SQL query retry logic.
The common retry - on - busy logic is factored into this function . This logic encapsulates the
maximum time ( timeout ) that the caller may wait for a lock to be released and the sleep interval
while waiting . The way to use it is this :
2012-08-23 08:13:35 +00:00
sqlite_retry_state retry = SQLITE_RETRY_STATE_DEFAULT ;
2012-08-22 09:39:30 +00:00
do ret = some_sqlite_operation ( . . . ) ;
2012-08-24 05:56:25 +00:00
while ( is_busy ( ret ) & & sqlite_retry ( & retry , " some_sqlite_operation " ) ) ;
2012-08-22 09:39:30 +00:00
if ( is_error ( ret ) | | is_busy ( ret ) )
return - 1 ; // an error has already been logged
2012-08-24 05:56:25 +00:00
sqlite_retry_done ( & retry , " some_sqlite_operation " ) ;
2012-08-22 09:39:30 +00:00
. . .
If the database is currently locked for updates , then some_sqlite_operation ( ) will return a code
indicating busy ( which is distinguishable from the codes for success or any other error ) .
sqlite_retry ( ) will then log a DEBUG or INFO message , sleep for a short period and return true if
the timeout has not been reached . It keeps this information in the ' retry ' variable , which must
be initialised as shown . As long as the timeout has not been reached , sqlite_retry ( ) will keep
sleeping and returning true . If the timeout is reached , then sqlite_retry ( ) will log an error
and return false . If the operation is successful , sqlite_retry_done ( ) must be called to log the
success as a DEBUG or INFO message to provide closure to the prior messages already logged by
sqlite_retry ( ) and to reset the ' retry ' variable for re - use .
The timeout and sleep interval depend on whether the caller is the servald server process or not .
2012-08-23 08:13:35 +00:00
See the definition of the SQLITE_RETRY_STATE_DEFAULT macro for the default settings .
2012-08-22 09:39:30 +00:00
A single ' retry ' variable may be initialised once then used for a succession of database
operations . If invoked by the server process , then the timeout timer will not be reset by
sqlite_retry ( ) or sqlite_retry_done ( ) , so that the timeout limit will apply to the cumulative
latency , not just to each individual query , which could potentially add up to much greater
latency than desired . However , in non - server processes , each query may be allowed its own
timeout , giving a greater chance of success at the expense of potentially greater latency .
*/
2012-08-24 05:56:25 +00:00
/* In the servald server process, by default we retry every 10 ms for up to 50 ms, so as to not
2012-08-23 08:13:35 +00:00
introduce too much latency into server responsiveness . In other processes ( eg , Batphone MeshMS
thread ) , by default we allow busy retries to go for over a second , waiting 100 ms between each
retry .
*/
sqlite_retry_state sqlite_retry_state_init ( int serverLimit , int serverSleep , int otherLimit , int otherSleep )
{
return ( sqlite_retry_state ) {
2013-01-06 09:13:14 +00:00
. limit = serverMode ? ( serverLimit < 0 ? 50 : serverLimit ) : ( otherLimit < 0 ? 5000 : otherLimit ) ,
2012-08-24 05:56:25 +00:00
. sleep = serverMode ? ( serverSleep < 0 ? 10 : serverSleep ) : ( otherSleep < 0 ? 100 : otherSleep ) ,
2012-08-23 08:13:35 +00:00
. elapsed = 0 ,
. start = - 1 ,
2012-08-24 05:56:25 +00:00
. busytries = 0
2012-08-23 08:13:35 +00:00
} ;
}
2012-10-16 06:16:52 +00:00
int _sqlite_retry ( struct __sourceloc __whence , sqlite_retry_state * retry , const char * action )
2012-08-22 09:39:30 +00:00
{
time_ms_t now = gettime_ms ( ) ;
2012-08-24 05:56:25 +00:00
+ + retry - > busytries ;
2012-08-22 09:39:30 +00:00
if ( retry - > start = = - 1 )
retry - > start = now ;
2012-12-27 04:45:23 +00:00
retry - > elapsed = now - retry - > start ;
INFOF ( " %s on try %u after %.3f seconds (limit %.3f): %s " ,
2012-08-24 05:56:25 +00:00
sqlite3_errmsg ( rhizome_db ) ,
retry - > busytries ,
2012-12-27 04:45:23 +00:00
( retry - > elapsed ) / 1e3 ,
( retry - > limit ) / 1e3 ,
2012-08-24 05:56:25 +00:00
action
) ;
2012-12-27 04:45:23 +00:00
2012-08-23 08:13:35 +00:00
if ( retry - > elapsed > = retry - > limit ) {
// reset ready for next query
2012-08-24 05:56:25 +00:00
retry - > busytries = 0 ;
2012-08-22 09:39:30 +00:00
if ( ! serverMode )
retry - > start = - 1 ;
return 0 ; // tell caller to stop trying
}
2012-12-27 04:45:23 +00:00
2012-08-23 08:13:35 +00:00
if ( retry - > sleep )
sleep_ms ( retry - > sleep ) ;
2012-08-22 09:39:30 +00:00
return 1 ; // tell caller to try again
}
2012-10-16 06:16:52 +00:00
void _sqlite_retry_done ( struct __sourceloc __whence , sqlite_retry_state * retry , const char * action )
2012-08-22 09:39:30 +00:00
{
2012-08-24 05:56:25 +00:00
if ( retry - > busytries ) {
2012-08-22 09:39:30 +00:00
time_ms_t now = gettime_ms ( ) ;
2012-12-27 04:45:23 +00:00
INFOF ( " succeeded on try %u after %.3f seconds (limit %.3f): %s " ,
2012-08-24 05:56:25 +00:00
retry - > busytries + 1 ,
2012-08-22 09:39:30 +00:00
( now - retry - > start ) / 1e3 ,
2012-12-27 04:45:23 +00:00
( retry - > limit ) / 1e3 ,
2012-08-22 09:39:30 +00:00
action
) ;
}
2012-08-23 08:13:35 +00:00
// reset ready for next query
2012-08-24 05:56:25 +00:00
retry - > busytries = 0 ;
2012-08-22 09:39:30 +00:00
if ( ! serverMode )
retry - > start = - 1 ;
}
2013-10-03 05:45:30 +00:00
/* Prepare an SQL command from a simple string. Returns NULL if an error occurs (logged as an
* error ) , otherwise returns a pointer to the prepared SQLite statement .
2013-10-02 08:49:20 +00:00
*
* IMPORTANT ! Do not form statement strings using sprintf ( 3 ) or strbuf_sprintf ( ) or similar
* methods , because those are susceptible to SQL injection attacks . Instead , use bound parameters
2013-10-03 05:45:30 +00:00
* and bind them using the _sqlite_bind ( ) function below .
2013-10-02 08:49:20 +00:00
*
2013-10-03 05:45:30 +00:00
* IMPORTANT ! Do not add sprintf ( 3 ) - like functionality to this method . It used to take
* sprintf ( 3 ) - style varargs and these were deliberately removed . It is vital to discourage bad
* practice , and adding sprintf ( 3 ) - style args to this function would be a step in the wrong
* direction .
2013-10-02 15:46:10 +00:00
*
* See GitHub issue # 69.
2013-10-02 08:49:20 +00:00
*
* @ author Andrew Bettison < andrew @ servalproject . com >
2012-05-25 10:12:45 +00:00
*/
2013-10-03 05:45:30 +00:00
sqlite3_stmt * _sqlite_prepare ( struct __sourceloc __whence , int log_level , sqlite_retry_state * retry , const char * sqltext )
2012-05-25 10:12:45 +00:00
{
2013-06-14 04:39:42 +00:00
IN ( ) ;
2012-08-20 09:04:35 +00:00
sqlite3_stmt * statement = NULL ;
2012-06-12 08:42:36 +00:00
if ( ! rhizome_db & & rhizome_opendb ( ) = = - 1 )
2013-06-14 04:39:42 +00:00
RETURN ( NULL ) ;
2012-10-04 05:00:20 +00:00
while ( 1 ) {
2013-10-02 08:49:20 +00:00
switch ( sqlite3_prepare_v2 ( rhizome_db , sqltext , - 1 , & statement , NULL ) ) {
2012-10-04 05:00:20 +00:00
case SQLITE_OK :
2013-11-18 05:54:03 +00:00
sqlite_trace_done = 0 ;
2013-06-14 04:39:42 +00:00
RETURN ( statement ) ;
2012-10-04 05:00:20 +00:00
case SQLITE_BUSY :
case SQLITE_LOCKED :
2013-10-02 08:49:20 +00:00
if ( retry & & _sqlite_retry ( __whence , retry , sqltext ) ) {
2012-10-04 05:00:20 +00:00
break ; // back to sqlite3_prepare_v2()
}
// fall through...
default :
2013-10-02 08:49:20 +00:00
LOGF ( log_level , " query invalid, %s: %s " , sqlite3_errmsg ( rhizome_db ) , sqltext ) ;
2012-10-04 05:00:20 +00:00
sqlite3_finalize ( statement ) ;
2013-06-14 04:39:42 +00:00
RETURN ( NULL ) ;
2012-10-04 05:00:20 +00:00
}
2012-05-25 10:12:45 +00:00
}
2012-08-20 09:04:35 +00:00
}
2013-10-03 05:45:30 +00:00
/* Bind some parameters to a prepared SQL statement. Returns -1 if an error occurs (logged as an
* error ) , otherwise zero with the prepared statement in * statement .
*
* Developed as part of GitHub issue # 69.
*
* @ author Andrew Bettison < andrew @ servalproject . com >
*/
2013-10-02 15:46:10 +00:00
int _sqlite_vbind ( struct __sourceloc __whence , int log_level , sqlite_retry_state * retry , sqlite3_stmt * statement , va_list ap )
2013-10-02 08:49:20 +00:00
{
2013-10-03 05:45:30 +00:00
const int index_limit = sqlite3_limit ( rhizome_db , SQLITE_LIMIT_VARIABLE_NUMBER , - 1 ) ;
2013-10-10 06:45:52 +00:00
unsigned argnum = 0 ;
2013-10-02 15:46:10 +00:00
int index_counter = 0 ;
2013-10-10 06:45:52 +00:00
enum sqlbind_type typ ;
while ( ( typ = va_arg ( ap , int ) ) ! = END ) {
+ + argnum ;
2013-10-02 08:49:20 +00:00
int index ;
2013-10-03 05:45:30 +00:00
const char * name = NULL ;
2013-10-10 06:45:52 +00:00
strbuf ext = NULL ;
2013-10-02 15:46:10 +00:00
if ( ( typ & 0xffff0000 ) = = INDEX ) {
typ & = 0xffff ;
index = va_arg ( ap , int ) ;
2013-10-10 06:45:52 +00:00
+ + argnum ;
2013-10-02 15:46:10 +00:00
if ( index < 1 | | index > index_limit ) {
2013-10-10 06:45:52 +00:00
LOGF ( log_level , " at bind arg %u, illegal index=%d: %s " , argnum , index , sqlite3_sql ( statement ) ) ;
2013-10-02 15:46:10 +00:00
return - 1 ;
}
2013-10-03 05:45:30 +00:00
if ( config . debug . rhizome )
2013-10-10 06:45:52 +00:00
strbuf_sprintf ( ( ext = strbuf_alloca ( 35 ) ) , " |INDEX(%d) " , index ) ;
2013-10-02 15:46:10 +00:00
} else if ( ( typ & 0xffff0000 ) = = NAMED ) {
typ & = 0xffff ;
2013-10-03 05:45:30 +00:00
name = va_arg ( ap , const char * ) ;
2013-10-10 06:45:52 +00:00
+ + argnum ;
2013-10-02 08:49:20 +00:00
index = sqlite3_bind_parameter_index ( statement , name ) ;
if ( index = = 0 ) {
2013-10-10 06:45:52 +00:00
LOGF ( log_level , " at bind arg %u, no parameter named %s in query: %s " , argnum , alloca_str_toprint ( name ) , sqlite3_sql ( statement ) ) ;
2013-10-02 15:46:10 +00:00
return - 1 ;
2013-10-02 08:49:20 +00:00
}
2013-10-03 05:45:30 +00:00
if ( config . debug . rhizome ) {
2013-10-10 06:45:52 +00:00
ext = strbuf_alloca ( 30 + toprint_str_len ( name , " \" \" " ) ) ;
strbuf_puts ( ext , " |NAMED( " ) ;
2013-10-03 05:45:30 +00:00
strbuf_toprint_quoted ( ext , " \" \" " , name ) ;
2013-10-10 06:45:52 +00:00
strbuf_puts ( ext , " ) " ) ;
2013-10-03 05:45:30 +00:00
}
2013-10-10 06:45:52 +00:00
} else if ( ( typ & 0xffff0000 ) = = 0 ) {
2013-10-02 15:46:10 +00:00
index = + + index_counter ;
2013-10-03 05:45:30 +00:00
if ( config . debug . rhizome )
2013-10-10 06:45:52 +00:00
ext = strbuf_alloca ( 10 ) ;
} else {
FATALF ( " at bind arg %u, unsupported bind code typ=0x%08x: %s " , argnum , typ , sqlite3_sql ( statement ) ) ;
return - 1 ;
2013-10-03 05:45:30 +00:00
}
# define BIND_DEBUG(TYP,FUNC,ARGFMT,...) \
Refactor manifest: specific setter functions
Replace generic rhizome_manifest_set() and rhizome_manifest_set_ll()
with per-field setter functions, eg, rhizome_manifest_set_filesize().
Struct rhizome_manifest elements for all known fields, to replace the
use of rhizome_manifest_get() and rhizome_manifest_get_ll() everywhere:
sender, recipient, service, name, date, bundle_key.
Add boolean validity flags for binary blob types, to avoid having to compare
with many bytes of all-zero to detect presence, eg, has_sender, has_recipient,
has_author, has_bundle_key. These maintained by the setter functions.
Rename existing manifest struct elements to be the same as their field
names: fileLength -> filesize, journalTail -> tail.
More use of unsigned int, size_t and uint64_t for payload sizes, offsets, byte
counts, etc. especially in rhizome_store.c and meshms.c. More uniform use of
size_t to dimension memory buffers. Fix some printf(3) style format strings
for 64-bit correctness on 32-bit systems. Use new constant RHIZOME_SIZE_UNSET
instead of -1 to indicate unknown dimension, and explicitly assert its absence
before comparisons and arithmetic, for safety.
Replace some 'int' loop variables with 'unsigned' where appropriate.
Fix bugs discovered in MeshMS bundle private/public key generation and
bundle secret key handling for export/extract commands.
Instrument the first MeshMS test case to aid debugging.
New debug config flag: debug.manifest logs all modifications to all manifest
fields by setter functions.
Rename debug config flag: debug.rhizome_bind -> debug.rhizome_sql_bind.
2013-10-30 12:52:19 +00:00
if ( config . debug . rhizome_sql_bind ) \
2013-10-10 06:45:52 +00:00
DEBUGF ( " %s%s %s(%d, " ARGFMT " ) %s " , # TYP , strbuf_str ( ext ) , # FUNC , index , # # __VA_ARGS__ , sqlite3_sql ( statement ) )
2013-10-02 15:46:10 +00:00
# define BIND_RETRY(FUNC, ...) \
do { \
switch ( FUNC ( statement , index , # # __VA_ARGS__ ) ) { \
case SQLITE_OK : \
break ; \
case SQLITE_BUSY : \
case SQLITE_LOCKED : \
if ( retry & & _sqlite_retry ( __whence , retry , # FUNC " () " ) ) \
continue ; \
default : \
LOGF ( log_level , # FUNC " (%d) failed, %s: %s " , index , sqlite3_errmsg ( rhizome_db ) , sqlite3_sql ( statement ) ) ; \
sqlite3_finalize ( statement ) ; \
return - 1 ; \
} \
break ; \
} while ( 1 )
2013-10-10 06:45:52 +00:00
# define BIND_NULL(TYP) \
if ( typ & NUL ) { \
BIND_DEBUG ( TYP , sqlite3_bind_null , " " ) ; \
BIND_RETRY ( sqlite3_bind_null ) ; \
} else { \
LOGF ( log_level , " at bind arg %u, %s%s parameter is NULL: %s " , argnum , # TYP , strbuf_str ( ext ) , sqlite3_sql ( statement ) ) ; \
sqlite3_finalize ( statement ) ; \
return - 1 ; \
}
2013-10-02 08:49:20 +00:00
switch ( typ ) {
case NUL :
2013-10-03 05:45:30 +00:00
BIND_DEBUG ( NUL , sqlite3_bind_null , " " ) ;
2013-10-02 15:46:10 +00:00
BIND_RETRY ( sqlite3_bind_null ) ;
2013-10-02 08:49:20 +00:00
break ;
2013-10-10 06:45:52 +00:00
default :
if ( ( typ & NUL ) & & config . debug . rhizome )
strbuf_puts ( ext , " |NUL " ) ;
switch ( typ & ~ NUL ) {
case INT : {
int value = va_arg ( ap , int ) ;
+ + argnum ;
BIND_DEBUG ( INT , sqlite3_bind_int , " %d " , value ) ;
BIND_RETRY ( sqlite3_bind_int , value ) ;
}
break ;
case INT_TOSTR : {
int value = va_arg ( ap , int ) ;
+ + argnum ;
char str [ 25 ] ;
sprintf ( str , " %d " , value ) ;
BIND_DEBUG ( INT_TOSTR , sqlite3_bind_text , " %s,-1,SQLITE_TRANSIENT " , alloca_str_toprint ( str ) ) ;
BIND_RETRY ( sqlite3_bind_text , str , - 1 , SQLITE_TRANSIENT ) ;
}
break ;
case UINT_TOSTR : {
unsigned value = va_arg ( ap , unsigned ) ;
+ + argnum ;
char str [ 25 ] ;
sprintf ( str , " %u " , value ) ;
BIND_DEBUG ( UINT_TOSTR , sqlite3_bind_text , " %s,-1,SQLITE_TRANSIENT " , alloca_str_toprint ( str ) ) ;
BIND_RETRY ( sqlite3_bind_text , str , - 1 , SQLITE_TRANSIENT ) ;
}
break ;
case INT64 : {
sqlite3_int64 value = va_arg ( ap , int64_t ) ;
BIND_DEBUG ( INT64 , sqlite3_bind_int64 , " % " PRId64 , ( int64_t ) value ) ;
BIND_RETRY ( sqlite3_bind_int64 , value ) ;
}
break ;
case INT64_TOSTR : {
int64_t value = va_arg ( ap , int64_t ) ;
+ + argnum ;
char str [ 35 ] ;
sprintf ( str , " % " PRId64 , value ) ;
BIND_DEBUG ( INT64_TOSTR , sqlite3_bind_text , " %s,-1,SQLITE_TRANSIENT " , alloca_str_toprint ( str ) ) ;
BIND_RETRY ( sqlite3_bind_text , str , - 1 , SQLITE_TRANSIENT ) ;
}
break ;
case UINT64_TOSTR : {
uint64_t value = va_arg ( ap , uint64_t ) ;
+ + argnum ;
char str [ 35 ] ;
sprintf ( str , " % " PRIu64 , value ) ;
BIND_DEBUG ( UINT64_TOSTR , sqlite3_bind_text , " %s,-1,SQLITE_TRANSIENT " , alloca_str_toprint ( str ) ) ;
BIND_RETRY ( sqlite3_bind_text , str , - 1 , SQLITE_TRANSIENT ) ;
}
break ;
case TEXT : {
const char * text = va_arg ( ap , const char * ) ;
+ + argnum ;
if ( text = = NULL ) {
BIND_NULL ( TEXT ) ;
} else {
BIND_DEBUG ( TEXT , sqlite3_bind_text , " %s,-1,SQLITE_TRANSIENT " , alloca_str_toprint ( text ) ) ;
BIND_RETRY ( sqlite3_bind_text , text , - 1 , SQLITE_TRANSIENT ) ;
}
}
break ;
case TEXT_LEN : {
const char * text = va_arg ( ap , const char * ) ;
int bytes = va_arg ( ap , int ) ;
argnum + = 2 ;
if ( text = = NULL ) {
BIND_NULL ( TEXT_LEN ) ;
} else {
BIND_DEBUG ( TEXT_LEN , sqlite3_bind_text , " %s,%d,SQLITE_TRANSIENT " , alloca_str_toprint ( text ) , bytes ) ;
BIND_RETRY ( sqlite3_bind_text , text , bytes , SQLITE_TRANSIENT ) ;
}
}
break ;
case STATIC_TEXT : {
const char * text = va_arg ( ap , const char * ) ;
+ + argnum ;
if ( text = = NULL ) {
BIND_NULL ( STATIC_TEXT ) ;
} else {
BIND_DEBUG ( STATIC_TEXT , sqlite3_bind_text , " %s,-1,SQLITE_STATIC " , alloca_str_toprint ( text ) ) ;
BIND_RETRY ( sqlite3_bind_text , text , - 1 , SQLITE_STATIC ) ;
}
}
break ;
case STATIC_TEXT_LEN : {
const char * text = va_arg ( ap , const char * ) ;
int bytes = va_arg ( ap , int ) ;
argnum + = 2 ;
if ( text = = NULL ) {
BIND_NULL ( STATIC_TEXT_LEN ) ;
} else {
BIND_DEBUG ( STATIC_TEXT_LEN , sqlite3_bind_text , " %s,%d,SQLITE_STATIC " , alloca_str_toprint ( text ) , bytes ) ;
BIND_RETRY ( sqlite3_bind_text , text , bytes , SQLITE_STATIC ) ;
}
}
break ;
case STATIC_BLOB : {
const void * blob = va_arg ( ap , const void * ) ;
int bytes = va_arg ( ap , int ) ;
argnum + = 2 ;
if ( blob = = NULL ) {
BIND_NULL ( STATIC_BLOB ) ;
} else {
BIND_DEBUG ( STATIC_BLOB , sqlite3_bind_blob , " %s,%d,SQLITE_STATIC " , alloca_toprint ( 20 , blob , bytes ) , bytes ) ;
BIND_RETRY ( sqlite3_bind_blob , blob , bytes , SQLITE_STATIC ) ;
}
} ;
break ;
case ZEROBLOB : {
int bytes = va_arg ( ap , int ) ;
+ + argnum ;
BIND_DEBUG ( ZEROBLOB , sqlite3_bind_zeroblob , " %d,SQLITE_STATIC " , bytes ) ;
BIND_RETRY ( sqlite3_bind_zeroblob , bytes ) ;
} ;
break ;
case SID_T : {
const sid_t * sidp = va_arg ( ap , const sid_t * ) ;
+ + argnum ;
if ( sidp = = NULL ) {
BIND_NULL ( SID_T ) ;
} else {
const char * sid_hex = alloca_tohex_sid_t ( * sidp ) ;
2013-10-15 00:45:31 +00:00
BIND_DEBUG ( SID_T , sqlite3_bind_text , " %s,%u,SQLITE_TRANSIENT " , sid_hex , SID_STRLEN ) ;
2013-10-10 06:45:52 +00:00
BIND_RETRY ( sqlite3_bind_text , sid_hex , SID_STRLEN , SQLITE_TRANSIENT ) ;
}
}
break ;
case RHIZOME_BID_T : {
const rhizome_bid_t * bidp = va_arg ( ap , const rhizome_bid_t * ) ;
+ + argnum ;
if ( bidp = = NULL ) {
BIND_NULL ( RHIZOME_BID_T ) ;
} else {
const char * bid_hex = alloca_tohex_rhizome_bid_t ( * bidp ) ;
2013-10-15 00:45:31 +00:00
BIND_DEBUG ( RHIZOME_BID_T , sqlite3_bind_text , " %s,%u,SQLITE_TRANSIENT " , bid_hex , RHIZOME_MANIFEST_ID_STRLEN ) ;
2013-10-10 06:45:52 +00:00
BIND_RETRY ( sqlite3_bind_text , bid_hex , RHIZOME_MANIFEST_ID_STRLEN , SQLITE_TRANSIENT ) ;
}
}
break ;
2013-10-10 07:53:06 +00:00
case RHIZOME_FILEHASH_T : {
const rhizome_filehash_t * hashp = va_arg ( ap , const rhizome_filehash_t * ) ;
2013-10-10 06:45:52 +00:00
+ + argnum ;
2013-10-10 07:53:06 +00:00
if ( hashp = = NULL ) {
BIND_NULL ( RHIZOME_FILEHASH_T ) ;
2013-10-10 06:45:52 +00:00
} else {
2013-11-15 06:40:33 +00:00
char hash_hex [ RHIZOME_FILEHASH_STRLEN + 1 ] ;
tohex ( hash_hex , RHIZOME_FILEHASH_STRLEN , hashp - > binary ) ;
2013-11-21 05:49:14 +00:00
BIND_DEBUG ( RHIZOME_FILEHASH_T , sqlite3_bind_text , " %s,%u,SQLITE_TRANSIENT " , hash_hex , RHIZOME_FILEHASH_STRLEN ) ;
2013-11-15 06:40:33 +00:00
BIND_RETRY ( sqlite3_bind_text , hash_hex , RHIZOME_FILEHASH_STRLEN , SQLITE_TRANSIENT ) ;
2013-10-10 06:45:52 +00:00
}
}
break ;
case TOHEX : {
const unsigned char * binary = va_arg ( ap , const unsigned char * ) ;
unsigned bytes = va_arg ( ap , unsigned ) ;
argnum + = 2 ;
if ( binary = = NULL ) {
BIND_NULL ( TOHEX ) ;
} else {
const char * hex = alloca_tohex ( binary , bytes ) ;
2013-10-15 00:45:31 +00:00
BIND_DEBUG ( TOHEX , sqlite3_bind_text , " %s,%u,SQLITE_TRANSIENT " , hex , bytes * 2 ) ;
2013-10-10 06:45:52 +00:00
BIND_RETRY ( sqlite3_bind_text , hex , bytes * 2 , SQLITE_TRANSIENT ) ;
}
}
break ;
case TEXT_TOUPPER : {
const char * text = va_arg ( ap , const char * ) ;
+ + argnum ;
if ( text = = NULL ) {
BIND_NULL ( TEXT_TOUPPER ) ;
} else {
unsigned bytes = strlen ( text ) ;
char upper [ bytes + 1 ] ;
str_toupper_inplace ( strcpy ( upper , text ) ) ;
2013-10-15 00:45:31 +00:00
BIND_DEBUG ( TEXT_TOUPPER , sqlite3_bind_text , " %s,%u,SQLITE_TRANSIENT " , alloca_toprint ( - 1 , upper , bytes ) , bytes ) ;
2013-10-10 06:45:52 +00:00
BIND_RETRY ( sqlite3_bind_text , upper , bytes , SQLITE_TRANSIENT ) ;
}
}
break ;
case TEXT_LEN_TOUPPER : {
const char * text = va_arg ( ap , const char * ) ;
unsigned bytes = va_arg ( ap , unsigned ) ;
argnum + = 2 ;
if ( text = = NULL ) {
BIND_NULL ( TEXT ) ;
} else {
char upper [ bytes ] ;
unsigned i ;
for ( i = 0 ; i ! = bytes ; + + i )
upper [ i ] = toupper ( text [ i ] ) ;
2013-10-15 00:45:31 +00:00
BIND_DEBUG ( TEXT_LEN_TOUPPER , sqlite3_bind_text , " %s,%u,SQLITE_TRANSIENT " , alloca_toprint ( - 1 , upper , bytes ) , bytes ) ;
2013-10-10 06:45:52 +00:00
BIND_RETRY ( sqlite3_bind_text , upper , bytes , SQLITE_TRANSIENT ) ;
}
}
break ;
2014-02-17 02:56:03 +00:00
case SERVAL_UUID_T : {
const serval_uuid_t * uuidp = va_arg ( ap , const serval_uuid_t * ) ;
2013-11-12 07:44:14 +00:00
+ + argnum ;
if ( uuidp = = NULL ) {
2014-02-17 02:56:03 +00:00
BIND_NULL ( SERVAL_UUID_T ) ;
2013-11-12 07:44:14 +00:00
} else {
char uuid_str [ UUID_STRLEN + 1 ] ;
uuid_to_str ( uuidp , uuid_str ) ;
2014-02-17 02:56:03 +00:00
BIND_DEBUG ( SERVAL_UUID_T , sqlite3_bind_text , " %s,%u,SQLITE_TRANSIENT " , uuid_str , UUID_STRLEN ) ;
2013-11-12 07:44:14 +00:00
BIND_RETRY ( sqlite3_bind_text , uuid_str , UUID_STRLEN , SQLITE_TRANSIENT ) ;
}
}
break ;
2013-10-10 06:45:52 +00:00
# undef BIND_RETRY
default :
FATALF ( " at bind arg %u, unsupported bind code typ=0x%08x: %s " , argnum , typ , sqlite3_sql ( statement ) ) ;
2013-10-02 08:49:20 +00:00
}
break ;
}
2013-10-10 06:45:52 +00:00
}
2013-10-02 15:46:10 +00:00
return 0 ;
}
int _sqlite_bind ( struct __sourceloc __whence , int log_level , sqlite_retry_state * retry , sqlite3_stmt * statement , . . . )
{
va_list ap ;
va_start ( ap , statement ) ;
int ret = _sqlite_vbind ( __whence , log_level , retry , statement , ap ) ;
2013-10-02 08:49:20 +00:00
va_end ( ap ) ;
return ret ;
}
2013-10-03 05:45:30 +00:00
/* Prepare an SQL statement and bind some parameters. Returns a pointer to the SQLite statement if
* successful or NULL if an error occurs ( which is logged at the given log level ) .
*
* @ author Andrew Bettison < andrew @ servalproject . com >
*/
sqlite3_stmt * _sqlite_prepare_bind ( struct __sourceloc __whence , int log_level , sqlite_retry_state * retry , const char * sqltext , . . . )
{
sqlite3_stmt * statement = _sqlite_prepare ( __whence , log_level , retry , sqltext ) ;
if ( statement ! = NULL ) {
va_list ap ;
va_start ( ap , sqltext ) ;
int ret = _sqlite_vbind ( __whence , log_level , retry , statement , ap ) ;
va_end ( ap ) ;
if ( ret = = - 1 ) {
sqlite3_finalize ( statement ) ;
statement = NULL ;
}
}
return statement ;
}
int _sqlite_step ( struct __sourceloc __whence , int log_level , sqlite_retry_state * retry , sqlite3_stmt * statement )
2012-08-20 09:04:35 +00:00
{
2013-06-14 04:39:42 +00:00
IN ( ) ;
2012-10-10 02:52:30 +00:00
int ret = - 1 ;
2012-10-16 06:16:52 +00:00
sqlite_trace_whence = & __whence ;
2012-10-10 02:52:30 +00:00
while ( statement ) {
2012-08-23 08:13:35 +00:00
int stepcode = sqlite3_step ( statement ) ;
2012-08-22 09:39:30 +00:00
switch ( stepcode ) {
case SQLITE_OK :
case SQLITE_DONE :
case SQLITE_ROW :
if ( retry )
2012-10-16 06:16:52 +00:00
_sqlite_retry_done ( __whence , retry , sqlite3_sql ( statement ) ) ;
2012-10-10 02:52:30 +00:00
ret = stepcode ;
statement = NULL ;
break ;
2012-08-22 09:39:30 +00:00
case SQLITE_BUSY :
case SQLITE_LOCKED :
2012-10-16 06:16:52 +00:00
if ( retry & & _sqlite_retry ( __whence , retry , sqlite3_sql ( statement ) ) ) {
2012-08-24 05:56:25 +00:00
sqlite3_reset ( statement ) ;
2012-08-23 08:13:35 +00:00
break ; // back to sqlite3_step()
2012-08-24 05:56:25 +00:00
}
2012-08-22 09:39:30 +00:00
// fall through...
default :
2013-01-06 02:34:49 +00:00
LOGF ( log_level , " query failed (%d), %s: %s " , stepcode , sqlite3_errmsg ( rhizome_db ) , sqlite3_sql ( statement ) ) ;
2012-10-10 02:52:30 +00:00
ret = - 1 ;
statement = NULL ;
break ;
2012-08-22 09:39:30 +00:00
}
}
2012-10-16 06:16:52 +00:00
sqlite_trace_whence = NULL ;
2013-06-14 04:39:42 +00:00
OUT ( ) ;
2012-10-10 02:52:30 +00:00
return ret ;
2012-08-23 08:13:35 +00:00
}
/*
2013-02-20 04:14:29 +00:00
* Convenience wrapper for executing a prepared SQL statement where the row outputs are not wanted .
* Always finalises the statement before returning .
*
* If an error occurs then logs it at the given level and returns - 1.
*
* If ' retry ' is non - NULL and the BUSY error occurs ( indicating the database is locked , ie ,
* currently in use by another process ) , then resets the statement and retries while sqlite_retry ( )
* returns true . If sqlite_retry ( ) returns false then returns - 1.
*
* Otherwise returns the number of rows ( SQLITE_ROW ) results , which will be zero if the first result
* was SQLITE_OK or SQLITE_DONE .
*
* @ author Andrew Bettison < andrew @ servalproject . com >
2012-08-23 08:13:35 +00:00
*/
2013-10-03 05:45:30 +00:00
static int _sqlite_exec ( struct __sourceloc __whence , int log_level , sqlite_retry_state * retry , sqlite3_stmt * statement )
2012-08-23 08:13:35 +00:00
{
if ( ! statement )
return - 1 ;
int rowcount = 0 ;
int stepcode ;
2013-10-03 05:45:30 +00:00
while ( ( stepcode = _sqlite_step ( __whence , log_level , retry , statement ) ) = = SQLITE_ROW )
2012-08-23 08:13:35 +00:00
+ + rowcount ;
2012-08-20 09:04:35 +00:00
sqlite3_finalize ( statement ) ;
2013-02-20 04:14:29 +00:00
if ( sqlite_trace_func ( ) )
DEBUGF ( " rowcount=%d changes=%d " , rowcount , sqlite3_changes ( rhizome_db ) ) ;
return sqlite_code_ok ( stepcode ) ? rowcount : - 1 ;
2012-05-25 10:12:45 +00:00
}
2013-09-30 06:45:01 +00:00
/* Execute an SQL command that returns no value. If an error occurs then logs it at ERROR level and
* returns - 1. Otherwise returns the number of rows changed by the command .
*
* @ author Andrew Bettison < andrew @ servalproject . com >
*/
2013-10-02 15:46:10 +00:00
static int _sqlite_vexec_void ( struct __sourceloc __whence , int log_level , sqlite_retry_state * retry , const char * sqltext , va_list ap )
2012-08-22 09:39:30 +00:00
{
2013-10-03 05:45:30 +00:00
sqlite3_stmt * statement = _sqlite_prepare ( __whence , log_level , retry , sqltext ) ;
2013-10-02 15:46:10 +00:00
if ( ! statement )
return - 1 ;
if ( _sqlite_vbind ( __whence , log_level , retry , statement , ap ) = = - 1 )
return - 1 ;
2013-10-03 05:45:30 +00:00
int rowcount = _sqlite_exec ( __whence , log_level , retry , statement ) ;
2013-02-20 04:14:29 +00:00
if ( rowcount = = - 1 )
return - 1 ;
if ( rowcount )
WARNF ( " void query unexpectedly returned %d row%s " , rowcount , rowcount = = 1 ? " " : " s " ) ;
return sqlite3_changes ( rhizome_db ) ;
2012-08-22 09:39:30 +00:00
}
2013-09-30 06:45:01 +00:00
/* Convenience wrapper for executing an SQL command that returns no value. If an error occurs then
* logs it at ERROR level and returns - 1. Otherwise returns the number of rows changed by the
* command .
2013-02-20 04:14:29 +00:00
*
* @ author Andrew Bettison < andrew @ servalproject . com >
2012-06-12 08:42:36 +00:00
*/
2013-10-03 05:45:30 +00:00
int _sqlite_exec_void ( struct __sourceloc __whence , int log_level , const char * sqltext , . . . )
2012-06-12 08:42:36 +00:00
{
2012-08-22 09:39:30 +00:00
va_list ap ;
2013-10-02 15:46:10 +00:00
va_start ( ap , sqltext ) ;
2012-08-23 08:13:35 +00:00
sqlite_retry_state retry = SQLITE_RETRY_STATE_DEFAULT ;
2013-10-02 15:46:10 +00:00
int ret = _sqlite_vexec_void ( __whence , log_level , & retry , sqltext , ap ) ;
2012-08-22 09:39:30 +00:00
va_end ( ap ) ;
return ret ;
2012-06-12 08:42:36 +00:00
}
2012-08-22 09:39:30 +00:00
/* Same as sqlite_exec_void() but if the statement cannot be executed because the database is
2013-02-20 04:14:29 +00:00
* currently locked for updates , then will call sqlite_retry ( ) on the supplied retry state variable
* instead of its own , internal one . If ' retry ' is passed as NULL , then will not sleep and retry at
* all in the event of a busy condition , but will log it as an error and return immediately .
*
* @ author Andrew Bettison < andrew @ servalproject . com >
2012-06-12 08:42:36 +00:00
*/
2013-10-03 05:45:30 +00:00
int _sqlite_exec_void_retry ( struct __sourceloc __whence , int log_level , sqlite_retry_state * retry , const char * sqltext , . . . )
2013-02-20 04:14:29 +00:00
{
va_list ap ;
2013-10-02 15:46:10 +00:00
va_start ( ap , sqltext ) ;
int ret = _sqlite_vexec_void ( __whence , log_level , retry , sqltext , ap ) ;
2013-02-20 04:14:29 +00:00
va_end ( ap ) ;
return ret ;
}
2013-12-11 00:41:34 +00:00
static int _sqlite_vexec_uint64 ( struct __sourceloc __whence , sqlite_retry_state * retry , uint64_t * result , const char * sqltext , va_list ap )
2012-01-12 03:38:24 +00:00
{
2013-10-03 05:45:30 +00:00
sqlite3_stmt * statement = _sqlite_prepare ( __whence , LOG_LEVEL_ERROR , retry , sqltext ) ;
2012-08-23 08:13:35 +00:00
if ( ! statement )
return - 1 ;
2013-10-02 15:46:10 +00:00
if ( _sqlite_vbind ( __whence , LOG_LEVEL_ERROR , retry , statement , ap ) = = - 1 )
return - 1 ;
2012-08-23 08:13:35 +00:00
int ret = 0 ;
int rowcount = 0 ;
int stepcode ;
2013-10-03 05:45:30 +00:00
while ( ( stepcode = _sqlite_step ( __whence , LOG_LEVEL_ERROR , retry , statement ) ) = = SQLITE_ROW ) {
2012-08-23 08:13:35 +00:00
int columncount = sqlite3_column_count ( statement ) ;
2012-10-16 06:16:52 +00:00
if ( columncount ! = 1 )
ret = WHYF ( " incorrect column count %d (should be 1): %s " , columncount , sqlite3_sql ( statement ) ) ;
2012-08-23 08:13:35 +00:00
else if ( + + rowcount = = 1 )
* result = sqlite3_column_int64 ( statement , 0 ) ;
2012-05-25 04:59:55 +00:00
}
2012-08-23 08:13:35 +00:00
if ( rowcount > 1 )
2012-10-16 06:16:52 +00:00
WARNF ( " query unexpectedly returned %d rows, ignored all but first " , rowcount ) ;
2012-08-23 08:13:35 +00:00
sqlite3_finalize ( statement ) ;
2013-02-20 04:14:29 +00:00
if ( ! sqlite_code_ok ( stepcode ) | | ret = = - 1 )
return - 1 ;
if ( sqlite_trace_func ( ) )
2013-12-11 00:41:34 +00:00
DEBUGF ( " rowcount=%d changes=%d result=% " PRIu64 , rowcount , sqlite3_changes ( rhizome_db ) , * result ) ;
2013-02-20 04:14:29 +00:00
return rowcount ;
2012-05-25 04:59:55 +00:00
}
2012-08-22 09:39:30 +00:00
/*
2013-02-20 04:14:29 +00:00
* Convenience wrapper for executing an SQL command that returns a single int64 value .
* Logs an error and returns - 1 if an error occurs .
* If no row is found , then returns 0 and does not alter * result .
* If exactly one row is found , the assigns its value to * result and returns 1.
* If more than one row is found , then logs a warning , assigns the value of the first row to * result
* and returns the number of rows .
*
* @ author Andrew Bettison < andrew @ servalproject . com >
2012-08-22 09:39:30 +00:00
*/
2013-12-11 00:41:34 +00:00
int _sqlite_exec_uint64 ( struct __sourceloc __whence , uint64_t * result , const char * sqlformat , . . . )
2012-08-22 09:39:30 +00:00
{
va_list ap ;
va_start ( ap , sqlformat ) ;
2012-08-23 08:13:35 +00:00
sqlite_retry_state retry = SQLITE_RETRY_STATE_DEFAULT ;
2013-12-11 00:41:34 +00:00
int ret = _sqlite_vexec_uint64 ( __whence , & retry , result , sqlformat , ap ) ;
2012-08-22 09:39:30 +00:00
va_end ( ap ) ;
return ret ;
}
2013-12-11 00:41:34 +00:00
/* Same as sqlite_exec_uint64() but if the statement cannot be executed because the database is
2013-02-20 04:14:29 +00:00
* currently locked for updates , then will call sqlite_retry ( ) on the supplied retry state variable
* instead of its own , internal one . If ' retry ' is passed as NULL , then will not sleep and retry at
* all in the event of a busy condition , but will log it as an error and return immediately .
*
* @ author Andrew Bettison < andrew @ servalproject . com >
2012-08-22 09:39:30 +00:00
*/
2013-12-11 00:41:34 +00:00
int _sqlite_exec_uint64_retry ( struct __sourceloc __whence , sqlite_retry_state * retry , uint64_t * result , const char * sqlformat , . . . )
2012-08-22 09:39:30 +00:00
{
va_list ap ;
va_start ( ap , sqlformat ) ;
2013-12-11 00:41:34 +00:00
int ret = _sqlite_vexec_uint64 ( __whence , retry , result , sqlformat , ap ) ;
2012-08-22 09:39:30 +00:00
va_end ( ap ) ;
return ret ;
}
2013-02-20 04:14:29 +00:00
/* Convenience wrapper for executing an SQL command that returns a single text value.
* Logs an error and returns - 1 if an error occurs , otherwise the number of rows that were found :
* 0 means no rows , nothing is appended to the strbuf
* 1 means exactly one row , appends its column to the strbuf
* 2 more than one row , logs a warning and appends the first row ' s column to the strbuf
*
* @ author Andrew Bettison < andrew @ servalproject . com >
2012-05-25 04:59:55 +00:00
*/
2012-10-16 06:16:52 +00:00
int _sqlite_exec_strbuf ( struct __sourceloc __whence , strbuf sb , const char * sqlformat , . . . )
2012-05-25 04:59:55 +00:00
{
2012-10-04 05:00:20 +00:00
sqlite_retry_state retry = SQLITE_RETRY_STATE_DEFAULT ;
2013-02-20 04:14:29 +00:00
va_list ap ;
va_start ( ap , sqlformat ) ;
int ret = _sqlite_vexec_strbuf_retry ( __whence , & retry , sb , sqlformat , ap ) ;
va_end ( ap ) ;
return ret ;
}
int _sqlite_exec_strbuf_retry ( struct __sourceloc __whence , sqlite_retry_state * retry , strbuf sb , const char * sqlformat , . . . )
{
va_list ap ;
va_start ( ap , sqlformat ) ;
int ret = _sqlite_vexec_strbuf_retry ( __whence , retry , sb , sqlformat , ap ) ;
va_end ( ap ) ;
return ret ;
}
2013-10-02 15:46:10 +00:00
int _sqlite_vexec_strbuf_retry ( struct __sourceloc __whence , sqlite_retry_state * retry , strbuf sb , const char * sqltext , va_list ap )
2013-02-20 04:14:29 +00:00
{
2013-10-03 05:45:30 +00:00
sqlite3_stmt * statement = _sqlite_prepare ( __whence , LOG_LEVEL_ERROR , retry , sqltext ) ;
2012-08-23 08:13:35 +00:00
if ( ! statement )
return - 1 ;
2013-10-02 15:46:10 +00:00
if ( _sqlite_vbind ( __whence , LOG_LEVEL_ERROR , retry , statement , ap ) = = - 1 )
return - 1 ;
2012-08-23 08:13:35 +00:00
int ret = 0 ;
int rowcount = 0 ;
int stepcode ;
2013-10-03 05:45:30 +00:00
while ( ( stepcode = _sqlite_step ( __whence , LOG_LEVEL_ERROR , retry , statement ) ) = = SQLITE_ROW ) {
2012-08-23 08:13:35 +00:00
int columncount = sqlite3_column_count ( statement ) ;
2012-10-16 06:16:52 +00:00
if ( columncount ! = 1 )
ret - WHYF ( " incorrect column count %d (should be 1): %s " , columncount , sqlite3_sql ( statement ) ) ;
2012-08-23 08:13:35 +00:00
else if ( + + rowcount = = 1 )
strbuf_puts ( sb , ( const char * ) sqlite3_column_text ( statement , 0 ) ) ;
2012-05-25 04:59:55 +00:00
}
2012-08-23 08:13:35 +00:00
if ( rowcount > 1 )
2012-10-16 06:16:52 +00:00
WARNF ( " query unexpectedly returned %d rows, ignored all but first " , rowcount ) ;
2012-08-23 08:13:35 +00:00
sqlite3_finalize ( statement ) ;
return sqlite_code_ok ( stepcode ) & & ret ! = - 1 ? rowcount : - 1 ;
2012-01-12 03:38:24 +00:00
}
2013-12-30 04:26:08 +00:00
int _sqlite_blob_open_retry (
struct __sourceloc __whence ,
int log_level ,
sqlite_retry_state * retry ,
const char * dbname ,
const char * tablename ,
const char * colname ,
sqlite3_int64 rowid ,
int flags ,
sqlite3_blob * * blobp
)
{
IN ( ) ;
while ( 1 ) {
int code = sqlite3_blob_open ( rhizome_db , dbname , tablename , colname , rowid , flags , blobp ) ;
switch ( code ) {
case SQLITE_OK :
if ( retry )
_sqlite_retry_done ( __whence , retry , " sqlite3_blob_open() " ) ;
RETURN ( code ) ;
case SQLITE_DONE :
case SQLITE_ROW :
LOGF ( log_level , " sqlite3_blob_open() returned unexpected code (%d) " , code ) ;
RETURN ( - 1 ) ;
case SQLITE_BUSY :
case SQLITE_LOCKED :
if ( retry & & _sqlite_retry ( __whence , retry , " sqlite3_blob_open() " ) )
break ; // back to sqlite3_blob_open()
// fall through...
default :
LOGF ( log_level , " sqlite3_blob_open() failed (%d), %s " , code , sqlite3_errmsg ( rhizome_db ) ) ;
RETURN ( - 1 ) ;
}
}
FATAL ( " not reached " ) ;
OUT ( ) ;
}
int _sqlite_blob_write_retry (
struct __sourceloc __whence ,
int log_level ,
sqlite_retry_state * retry ,
sqlite3_blob * blob ,
const void * buf ,
int len ,
int offset
)
{
IN ( ) ;
while ( 1 ) {
int code = sqlite3_blob_write ( blob , buf , len , offset ) ;
switch ( code ) {
case SQLITE_OK :
if ( retry )
_sqlite_retry_done ( __whence , retry , " sqlite3_blob_write() " ) ;
RETURN ( code ) ;
case SQLITE_DONE :
case SQLITE_ROW :
LOGF ( log_level , " sqlite3_blob_write() returned unexpected code (%d) " , code ) ;
RETURN ( - 1 ) ;
case SQLITE_BUSY :
case SQLITE_LOCKED :
if ( retry & & _sqlite_retry ( __whence , retry , " sqlite3_blob_write() " ) )
break ; // back to sqlite3_blob_open()
// fall through...
default :
LOGF ( log_level , " sqlite3_blob_write() failed (%d), %s " , code , sqlite3_errmsg ( rhizome_db ) ) ;
RETURN ( - 1 ) ;
}
}
FATAL ( " not reached " ) ;
OUT ( ) ;
}
int _sqlite_blob_close ( struct __sourceloc __whence , int log_level , sqlite3_blob * blob )
{
int code = sqlite3_blob_close ( blob ) ;
if ( code ! = SQLITE_OK )
LOGF ( log_level , " sqlite3_blob_close() failed: %s " , sqlite3_errmsg ( rhizome_db ) ) ;
return 0 ;
}
2013-12-11 00:41:34 +00:00
static uint64_t rhizome_database_used_bytes ( )
2012-01-12 03:38:24 +00:00
{
2013-12-11 00:41:34 +00:00
uint64_t db_page_size ;
uint64_t db_page_count ;
uint64_t db_free_page_count ;
if ( sqlite_exec_uint64 ( & db_page_size , " PRAGMA page_size; " , END ) = = - 1LL
| | sqlite_exec_uint64 ( & db_page_count , " PRAGMA page_count; " , END ) = = - 1LL
| | sqlite_exec_uint64 ( & db_free_page_count , " PRAGMA free_count; " , END ) = = - 1LL
) {
WHY ( " Cannot measure database used bytes " ) ;
return UINT64_MAX ;
}
2012-06-12 08:42:36 +00:00
return db_page_size * ( db_page_count - db_free_page_count ) ;
2012-01-12 03:38:24 +00:00
}
2013-10-10 07:53:06 +00:00
int rhizome_database_filehash_from_id ( const rhizome_bid_t * bidp , uint64_t version , rhizome_filehash_t * hashp )
2013-02-16 09:55:26 +00:00
{
IN ( ) ;
2013-10-10 07:53:06 +00:00
strbuf hash_sb = strbuf_alloca ( RHIZOME_FILEHASH_STRLEN + 1 ) ;
if ( sqlite_exec_strbuf ( hash_sb , " SELECT filehash FROM MANIFESTS WHERE version = ? AND id = ?; " ,
INT64 , version , RHIZOME_BID_T , bidp , END ) = = - 1 )
RETURN ( - 1 ) ;
if ( strbuf_overrun ( hash_sb ) | | str_to_rhizome_filehash_t ( hashp , strbuf_str ( hash_sb ) ) = = - 1 )
2013-12-11 00:41:34 +00:00
RETURN ( WHYF ( " malformed file hash for bid=%s version=% " PRIu64 , alloca_tohex_rhizome_bid_t ( * bidp ) , version ) ) ;
2013-10-10 07:53:06 +00:00
RETURN ( 0 ) ;
2013-02-16 17:47:24 +00:00
OUT ( ) ;
2013-02-16 09:55:26 +00:00
}
2013-10-10 07:53:06 +00:00
static int rhizome_delete_external ( const rhizome_filehash_t * hashp )
2013-08-21 06:15:18 +00:00
{
// attempt to remove any external blob
char blob_path [ 1024 ] ;
2013-10-10 07:53:06 +00:00
if ( ! FORM_RHIZOME_DATASTORE_PATH ( blob_path , alloca_tohex_rhizome_filehash_t ( * hashp ) ) )
2013-08-21 06:15:18 +00:00
return - 1 ;
2013-12-18 07:10:24 +00:00
if ( unlink ( blob_path ) = = - 1 ) {
if ( errno ! = ENOENT )
return WHYF_perror ( " unlink(%s) " , alloca_str_toprint(blob_path)) ;
return 1 ;
}
if ( config . debug . externalblobs )
DEBUGF ( " Deleted blob file %s " , blob_path ) ;
return 0 ;
2013-08-21 06:15:18 +00:00
}
2013-09-30 06:50:50 +00:00
static int rhizome_delete_orphan_fileblobs_retry ( sqlite_retry_state * retry )
{
2013-10-02 15:46:10 +00:00
return sqlite_exec_void_retry_loglevel ( LOG_LEVEL_WARN , retry ,
" DELETE FROM FILEBLOBS WHERE NOT EXISTS( SELECT 1 FROM FILES WHERE FILES.id = FILEBLOBS.id ); " ,
END ) ;
2013-09-30 06:50:50 +00:00
}
2013-10-10 07:53:06 +00:00
int rhizome_remove_file_datainvalid ( sqlite_retry_state * retry , const rhizome_filehash_t * hashp )
2013-09-30 07:02:08 +00:00
{
int ret = 0 ;
2013-10-02 15:46:10 +00:00
if ( sqlite_exec_void_retry_loglevel ( LOG_LEVEL_WARN , retry ,
" DELETE FROM FILES WHERE id = ? and datavalid = 0; " ,
2013-10-10 07:53:06 +00:00
RHIZOME_FILEHASH_T , hashp , END
2013-10-02 15:46:10 +00:00
) = = - 1
)
2013-09-30 07:02:08 +00:00
ret = - 1 ;
2013-10-02 15:46:10 +00:00
if ( sqlite_exec_void_retry_loglevel ( LOG_LEVEL_WARN , retry ,
" DELETE FROM FILEBLOBS WHERE id = ? AND NOT EXISTS( SELECT 1 FROM FILES WHERE FILES.id = FILEBLOBS.id ); " ,
2013-10-10 07:53:06 +00:00
RHIZOME_FILEHASH_T , hashp , END
2013-10-02 15:46:10 +00:00
) = = - 1
)
2013-09-30 07:02:08 +00:00
ret = - 1 ;
return ret ;
}
2013-02-20 04:14:29 +00:00
int rhizome_cleanup ( struct rhizome_cleanup_report * report )
2012-12-31 04:39:12 +00:00
{
2013-01-03 03:46:33 +00:00
IN ( ) ;
2013-10-06 17:12:17 +00:00
if ( config . debug . rhizome & & report = = NULL )
report = alloca ( sizeof * report ) ;
if ( report )
bzero ( report , sizeof * report ) ;
2013-02-20 06:52:53 +00:00
sqlite_retry_state retry = SQLITE_RETRY_STATE_DEFAULT ;
2013-10-06 17:12:17 +00:00
/* For testing, it helps to speed up the cleanup process. */
const char * orphan_payload_persist_ms = getenv ( " SERVALD_ORPHAN_PAYLOAD_PERSIST_MS " ) ;
const char * invalid_payload_persist_ms = getenv ( " SERVALD_INVALID_PAYLOAD_PERSIST_MS " ) ;
2013-09-30 06:50:50 +00:00
time_ms_t now = gettime_ms ( ) ;
2013-10-06 17:12:17 +00:00
time_ms_t insert_horizon_no_manifest = now - ( orphan_payload_persist_ms ? atoi ( orphan_payload_persist_ms ) : 1000 ) ; // 1 second ago
time_ms_t insert_horizon_not_valid = now - ( invalid_payload_persist_ms ? atoi ( invalid_payload_persist_ms ) : 300000 ) ; // 5 minutes ago
2013-09-30 06:50:50 +00:00
2013-02-20 06:52:53 +00:00
// cleanup external blobs for unreferenced files
2013-10-06 17:12:17 +00:00
unsigned candidates = 0 ;
2013-10-02 15:46:10 +00:00
sqlite3_stmt * statement = sqlite_prepare_bind ( & retry ,
" SELECT id FROM FILES WHERE inserttime < ? AND datavalid = 0; " ,
2013-10-06 17:12:17 +00:00
INT64 , insert_horizon_not_valid , END ) ;
2013-08-27 04:12:56 +00:00
while ( sqlite_step_retry ( & retry , statement ) = = SQLITE_ROW ) {
candidates + + ;
const char * id = ( const char * ) sqlite3_column_text ( statement , 0 ) ;
2013-10-10 07:53:06 +00:00
rhizome_filehash_t hash ;
if ( str_to_rhizome_filehash_t ( & hash , id ) = = - 1 )
WARNF ( " invalid field FILES.id=%s -- ignored " , alloca_str_toprint ( id ) ) ;
else if ( rhizome_delete_external ( & hash ) = = 0 & & report )
+ + report - > deleted_stale_incoming_files ;
2013-08-27 04:12:56 +00:00
}
2013-02-20 06:52:53 +00:00
sqlite3_finalize ( statement ) ;
2013-10-10 07:53:06 +00:00
2013-10-02 15:46:10 +00:00
statement = sqlite_prepare_bind ( & retry ,
" SELECT id FROM FILES WHERE inserttime < ? AND datavalid = 1 AND NOT EXISTS( SELECT 1 FROM MANIFESTS WHERE MANIFESTS.filehash = FILES.id); " ,
INT64 , insert_horizon_no_manifest , END ) ;
2013-08-27 04:12:56 +00:00
while ( sqlite_step_retry ( & retry , statement ) = = SQLITE_ROW ) {
candidates + + ;
const char * id = ( const char * ) sqlite3_column_text ( statement , 0 ) ;
2013-10-10 07:53:06 +00:00
rhizome_filehash_t hash ;
if ( str_to_rhizome_filehash_t ( & hash , id ) = = - 1 )
WARNF ( " invalid field FILES.id=%s -- ignored " , alloca_str_toprint ( id ) ) ;
else if ( rhizome_delete_external ( & hash ) = = 0 & & report )
2013-10-06 17:12:17 +00:00
+ + report - > deleted_orphan_files ;
2013-08-27 04:12:56 +00:00
}
2013-02-20 06:52:53 +00:00
sqlite3_finalize ( statement ) ;
2013-02-20 04:14:29 +00:00
int ret ;
2013-09-30 06:50:50 +00:00
if ( candidates ) {
2013-08-27 04:12:56 +00:00
// clean out unreferenced files
2013-10-02 15:46:10 +00:00
ret = sqlite_exec_void_retry_loglevel ( LOG_LEVEL_WARN , & retry ,
" DELETE FROM FILES WHERE inserttime < ? AND datavalid = 0; " ,
2013-10-06 17:12:17 +00:00
INT64 , insert_horizon_not_valid , END ) ;
if ( report & & ret > 0 )
report - > deleted_stale_incoming_files + = ret ;
2013-10-02 15:46:10 +00:00
ret = sqlite_exec_void_retry_loglevel ( LOG_LEVEL_WARN , & retry ,
" DELETE FROM FILES WHERE inserttime < ? AND datavalid=1 AND NOT EXISTS( SELECT 1 FROM MANIFESTS WHERE MANIFESTS.filehash = FILES.id); " ,
INT64 , insert_horizon_no_manifest , END ) ;
2013-10-06 17:12:17 +00:00
if ( report & & ret > 0 )
report - > deleted_orphan_files + = ret ;
2013-08-27 04:12:56 +00:00
}
2013-02-20 06:52:53 +00:00
2013-09-30 06:50:50 +00:00
if ( ( ret = rhizome_delete_orphan_fileblobs_retry ( & retry ) ) > 0 & & report )
report - > deleted_orphan_fileblobs + = ret ;
2013-02-20 06:52:53 +00:00
2013-10-06 17:12:17 +00:00
if ( config . debug . rhizome & & report )
DEBUGF ( " report deleted_stale_incoming_files=%u deleted_orphan_files=%u deleted_orphan_fileblobs=%u " ,
report - > deleted_stale_incoming_files ,
report - > deleted_orphan_files ,
report - > deleted_orphan_fileblobs
) ;
2013-02-20 04:14:29 +00:00
RETURN ( 0 ) ;
2013-01-03 03:46:33 +00:00
OUT ( ) ;
2012-12-31 04:39:12 +00:00
}
2013-10-06 19:24:46 +00:00
int rhizome_make_space ( int group_priority , uint64_t bytes )
2012-01-12 03:38:24 +00:00
{
/* Asked for impossibly large amount */
2013-12-10 06:54:14 +00:00
const size_t margin = 65536 ;
const uint64_t limit = config . rhizome . database_size > margin ? config . rhizome . database_size - margin : 1 ;
if ( bytes > = limit )
2013-10-06 19:24:46 +00:00
return WHYF ( " bytes=% " PRIu64 " is too large " , bytes ) ;
2012-01-12 03:38:24 +00:00
2013-12-11 00:41:34 +00:00
uint64_t db_used = rhizome_database_used_bytes ( ) ;
if ( db_used = = UINT64_MAX )
2012-08-24 05:56:25 +00:00
return - 1 ;
2012-01-12 03:38:24 +00:00
2013-02-20 04:14:29 +00:00
rhizome_cleanup ( NULL ) ;
2012-12-31 04:39:12 +00:00
2012-01-12 03:38:24 +00:00
/* If there is already enough space now, then do nothing more */
2013-12-10 06:54:14 +00:00
if ( db_used + bytes < = limit )
2012-08-24 05:56:25 +00:00
return 0 ;
2012-01-12 03:38:24 +00:00
/* Okay, not enough space, so free up some. */
2012-10-04 05:00:20 +00:00
sqlite_retry_state retry = SQLITE_RETRY_STATE_DEFAULT ;
2013-10-02 15:46:10 +00:00
sqlite3_stmt * statement = sqlite_prepare_bind ( & retry ,
" SELECT id,length FROM FILES WHERE highestpriority < ? ORDER BY DESCENDING LENGTH " ,
INT , group_priority , END ) ;
2012-08-24 05:56:25 +00:00
if ( ! statement )
return - 1 ;
2013-12-11 00:41:34 +00:00
while ( db_used + bytes > limit & & sqlite_step_retry ( & retry , statement ) = = SQLITE_ROW ) {
2012-08-24 05:56:25 +00:00
/* Make sure we can drop this blob, and if so drop it, and recalculate number of bytes required */
2013-10-10 07:53:06 +00:00
const char * id ;
2012-08-24 05:56:25 +00:00
/* Get values */
if ( sqlite3_column_type ( statement , 0 ) = = SQLITE_TEXT )
2013-10-10 07:53:06 +00:00
id = ( const char * ) sqlite3_column_text ( statement , 0 ) ;
2012-08-24 05:56:25 +00:00
else {
WHY ( " Incorrect type in id column of files table " ) ;
break ;
}
if ( sqlite3_column_type ( statement , 1 ) = = SQLITE_INTEGER )
; //length=sqlite3_column_int(statement, 1);
else {
WHY ( " Incorrect type in length column of files table " ) ;
break ;
2012-01-12 03:38:24 +00:00
}
2013-10-10 07:53:06 +00:00
rhizome_filehash_t hash ;
if ( str_to_rhizome_filehash_t ( & hash , id ) = = - 1 )
WHYF ( " invalid field FILES.id=%s -- ignored " , alloca_str_toprint ( id ) ) ;
else {
/* Try to drop this file from storage, discarding any references that do not trump the
* priority of this request . The query done earlier should ensure this , but it doesn ' t hurt
* to be paranoid , and it also protects against inconsistency in the database .
*/
rhizome_drop_stored_file ( & hash , group_priority + 1 ) ;
2013-12-11 00:41:34 +00:00
if ( ( db_used = rhizome_database_used_bytes ( ) ) = = UINT64_MAX )
break ;
2013-10-10 07:53:06 +00:00
}
2012-08-24 05:56:25 +00:00
}
2012-01-12 03:38:24 +00:00
sqlite3_finalize ( statement ) ;
2013-10-06 19:24:46 +00:00
//int64_t equal_priority_larger_file_space_used = sqlite_exec_int64("SELECT COUNT(length) FROM
//FILES WHERE highestpriority = ? and length > ?", INT, group_priority, INT64, bytes, END);
2012-01-12 03:38:24 +00:00
/* XXX Get rid of any equal priority files that are larger than this one */
/* XXX Get rid of any higher priority files that are not relevant in this time or location */
/* Couldn't make space */
return WHY ( " Incomplete " ) ;
}
2013-10-03 19:20:37 +00:00
/* Drop the specified file from storage, and any manifests that reference it, provided that none of
* those manifests are being retained at a higher priority than the maximum specified here .
*/
2013-10-10 07:53:06 +00:00
int rhizome_drop_stored_file ( const rhizome_filehash_t * hashp , int maximum_priority )
2012-01-12 03:38:24 +00:00
{
2012-10-04 05:00:20 +00:00
sqlite_retry_state retry = SQLITE_RETRY_STATE_DEFAULT ;
2013-10-10 07:53:06 +00:00
sqlite3_stmt * statement = sqlite_prepare_bind ( & retry , " SELECT id FROM MANIFESTS WHERE filehash = ? " , RHIZOME_FILEHASH_T , hashp , END ) ;
2012-08-23 08:13:35 +00:00
if ( ! statement )
2013-10-10 07:53:06 +00:00
return WHYF ( " Could not drop stored file id=%s " , alloca_tohex_rhizome_filehash_t ( * hashp ) ) ;
2012-08-23 08:13:35 +00:00
int can_drop = 1 ;
2012-08-24 05:56:25 +00:00
while ( sqlite_step_retry ( & retry , statement ) = = SQLITE_ROW ) {
2012-08-23 08:13:35 +00:00
/* Find manifests for this file */
if ( sqlite3_column_type ( statement , 0 ) ! = SQLITE_TEXT ) {
WHYF ( " Incorrect type in id column of manifests table " ) ;
break ;
2012-01-12 03:38:24 +00:00
}
2013-10-03 19:20:37 +00:00
const char * q_id = ( char * ) sqlite3_column_text ( statement , 0 ) ;
rhizome_bid_t bid ;
if ( str_to_rhizome_bid_t ( & bid , q_id ) = = - 1 ) {
WARNF ( " malformed column value MANIFESTS.id = %s -- skipped " , alloca_str_toprint ( q_id ) ) ;
continue ;
}
2012-08-23 08:13:35 +00:00
/* Check that manifest is not part of a higher priority group.
If so , we cannot drop the manifest or the file .
However , we will keep iterating , as we can still drop any other manifests pointing to this file
that are lower priority , and thus free up a little space . */
2013-10-03 19:20:37 +00:00
int priority = rhizome_manifest_priority ( & retry , & bid ) ;
2012-08-23 08:13:35 +00:00
if ( priority = = - 1 )
2013-10-10 07:53:06 +00:00
WHYF ( " Cannot drop fileid=%s due to error, bid=%s " , alloca_tohex_rhizome_filehash_t ( * hashp ) , alloca_tohex_rhizome_bid_t ( bid ) ) ;
2012-08-23 08:13:35 +00:00
else if ( priority > maximum_priority ) {
2013-10-10 07:53:06 +00:00
WHYF ( " Cannot drop fileid=%s due to manifest priority, bid=%s " , alloca_tohex_rhizome_filehash_t ( * hashp ) , alloca_tohex_rhizome_bid_t ( bid ) ) ;
2012-08-23 08:13:35 +00:00
can_drop = 0 ;
} else {
2012-12-11 05:29:46 +00:00
if ( config . debug . rhizome )
2012-08-23 08:13:35 +00:00
DEBUGF ( " removing stale manifests, groupmemberships " ) ;
2013-10-03 19:20:37 +00:00
sqlite_exec_void_retry ( & retry , " DELETE FROM MANIFESTS WHERE id = ?; " , RHIZOME_BID_T , & bid , END ) ;
sqlite_exec_void_retry ( & retry , " DELETE FROM KEYPAIRS WHERE public = ?; " , RHIZOME_BID_T , & bid , END ) ;
sqlite_exec_void_retry ( & retry , " DELETE FROM GROUPMEMBERSHIPS WHERE manifestid = ?; " , RHIZOME_BID_T , & bid , END ) ;
2012-01-12 03:38:24 +00:00
}
}
2012-08-23 08:13:35 +00:00
sqlite3_finalize ( statement ) ;
2013-08-21 06:15:18 +00:00
if ( can_drop )
2013-10-10 07:53:06 +00:00
rhizome_delete_file_retry ( & retry , hashp ) ;
2012-01-12 03:38:24 +00:00
return 0 ;
}
/*
Store the specified manifest into the sqlite database .
We assume that sufficient space has been made for us .
The manifest should be finalised , and so we don ' t need to
look at the underlying manifest file , but can just write m - > manifest_data
as a blob .
associated_filename needs to be read in and stored as a blob . Hopefully that
can be done in pieces so that we don ' t have memory exhaustion issues on small
architectures . However , we do know it ' s hash apriori from m , and so we can
skip loading the file in if it is already stored . mmap ( ) apparently works on
Linux FAT file systems , and is probably the best choice since it doesn ' t need
all pages to be in RAM at the same time .
SQLite does allow modifying of blobs once stored in the database .
The trick is to insert the blob as all zeroes using a special function , and then
substitute bytes in the blog progressively .
We need to also need to create the appropriate row ( s ) in the MANIFESTS , FILES ,
2012-05-28 09:54:02 +00:00
and GROUPMEMBERSHIPS tables , and possibly GROUPLIST as well .
2012-01-12 03:38:24 +00:00
*/
2013-12-19 08:43:39 +00:00
int rhizome_store_manifest ( rhizome_manifest * m )
2012-01-12 03:38:24 +00:00
{
2013-12-30 07:39:56 +00:00
assert ( m - > finalised ) ;
2012-01-12 03:38:24 +00:00
2013-11-05 07:28:03 +00:00
// If we don't have the secret for this manifest, only store it if its self-signature is valid
if ( ! m - > haveSecret & & ! m - > selfSigned )
return WHY ( " Manifest is not signed, and I don't have the key. Manifest might be forged or corrupt. " ) ;
2012-08-23 08:13:35 +00:00
2012-01-12 03:38:24 +00:00
/* Bind BAR to data field */
unsigned char bar [ RHIZOME_BAR_BYTES ] ;
rhizome_manifest_to_bar ( m , bar ) ;
2012-10-02 21:36:06 +00:00
/* Store the file (but not if it is already in the database) */
Refactor manifest: specific setter functions
Replace generic rhizome_manifest_set() and rhizome_manifest_set_ll()
with per-field setter functions, eg, rhizome_manifest_set_filesize().
Struct rhizome_manifest elements for all known fields, to replace the
use of rhizome_manifest_get() and rhizome_manifest_get_ll() everywhere:
sender, recipient, service, name, date, bundle_key.
Add boolean validity flags for binary blob types, to avoid having to compare
with many bytes of all-zero to detect presence, eg, has_sender, has_recipient,
has_author, has_bundle_key. These maintained by the setter functions.
Rename existing manifest struct elements to be the same as their field
names: fileLength -> filesize, journalTail -> tail.
More use of unsigned int, size_t and uint64_t for payload sizes, offsets, byte
counts, etc. especially in rhizome_store.c and meshms.c. More uniform use of
size_t to dimension memory buffers. Fix some printf(3) style format strings
for 64-bit correctness on 32-bit systems. Use new constant RHIZOME_SIZE_UNSET
instead of -1 to indicate unknown dimension, and explicitly assert its absence
before comparisons and arithmetic, for safety.
Replace some 'int' loop variables with 'unsigned' where appropriate.
Fix bugs discovered in MeshMS bundle private/public key generation and
bundle secret key handling for export/extract commands.
Instrument the first MeshMS test case to aid debugging.
New debug config flag: debug.manifest logs all modifications to all manifest
fields by setter functions.
Rename debug config flag: debug.rhizome_bind -> debug.rhizome_sql_bind.
2013-10-30 12:52:19 +00:00
assert ( m - > filesize ! = RHIZOME_SIZE_UNSET ) ;
if ( m - > filesize > 0 & & ! rhizome_exists ( & m - > filehash ) )
2013-10-10 07:53:06 +00:00
return WHY ( " File should already be stored by now " ) ;
2012-08-23 08:13:35 +00:00
sqlite_retry_state retry = SQLITE_RETRY_STATE_DEFAULT ;
2013-10-02 15:46:10 +00:00
if ( sqlite_exec_void_retry ( & retry , " BEGIN TRANSACTION; " , END ) = = - 1 )
2012-12-28 02:16:07 +00:00
return WHY ( " Failed to begin transaction " ) ;
2013-10-10 06:45:52 +00:00
2013-11-05 07:28:03 +00:00
time_ms_t now = gettime_ms ( ) ;
2013-11-07 06:52:06 +00:00
// The INSERT OR REPLACE statement will delete a row with the same ID (primary key) if it exists,
// so a new autoincremented ROWID will be allocated whether or not the manifest with this ID is
// already in the table. Other code depends on this property: that ROWID is monotonically
// increasing with time and unique.
2012-08-23 08:13:35 +00:00
sqlite3_stmt * stmt ;
2013-10-10 06:45:52 +00:00
if ( ( stmt = sqlite_prepare_bind ( & retry ,
2013-07-22 05:34:02 +00:00
" INSERT OR REPLACE INTO MANIFESTS( "
" id, "
" manifest, "
" version, "
" inserttime, "
" bar, "
" filesize, "
" filehash, "
" author, "
" service, "
" name, "
" sender, "
" recipient, "
" tail "
" ) VALUES( "
" ?,?,?,?,?,?,?,?,?,?,?,?,? "
2013-10-10 06:45:52 +00:00
" ); " ,
RHIZOME_BID_T , & m - > cryptoSignPublic ,
2013-11-28 07:14:37 +00:00
STATIC_BLOB , m - > manifestdata , m - > manifest_all_bytes ,
2013-10-10 06:45:52 +00:00
INT64 , m - > version ,
2013-11-05 07:28:03 +00:00
INT64 , ( int64_t ) now ,
2013-10-10 06:45:52 +00:00
STATIC_BLOB , bar , RHIZOME_BAR_BYTES ,
Refactor manifest: specific setter functions
Replace generic rhizome_manifest_set() and rhizome_manifest_set_ll()
with per-field setter functions, eg, rhizome_manifest_set_filesize().
Struct rhizome_manifest elements for all known fields, to replace the
use of rhizome_manifest_get() and rhizome_manifest_get_ll() everywhere:
sender, recipient, service, name, date, bundle_key.
Add boolean validity flags for binary blob types, to avoid having to compare
with many bytes of all-zero to detect presence, eg, has_sender, has_recipient,
has_author, has_bundle_key. These maintained by the setter functions.
Rename existing manifest struct elements to be the same as their field
names: fileLength -> filesize, journalTail -> tail.
More use of unsigned int, size_t and uint64_t for payload sizes, offsets, byte
counts, etc. especially in rhizome_store.c and meshms.c. More uniform use of
size_t to dimension memory buffers. Fix some printf(3) style format strings
for 64-bit correctness on 32-bit systems. Use new constant RHIZOME_SIZE_UNSET
instead of -1 to indicate unknown dimension, and explicitly assert its absence
before comparisons and arithmetic, for safety.
Replace some 'int' loop variables with 'unsigned' where appropriate.
Fix bugs discovered in MeshMS bundle private/public key generation and
bundle secret key handling for export/extract commands.
Instrument the first MeshMS test case to aid debugging.
New debug config flag: debug.manifest logs all modifications to all manifest
fields by setter functions.
Rename debug config flag: debug.rhizome_bind -> debug.rhizome_sql_bind.
2013-10-30 12:52:19 +00:00
INT64 , m - > filesize ,
RHIZOME_FILEHASH_T | NUL , m - > filesize > 0 ? & m - > filehash : NULL ,
2013-11-05 07:28:03 +00:00
// Only store the author if it is known to be authentic.
SID_T | NUL , m - > authorship = = AUTHOR_AUTHENTIC ? & m - > author : NULL ,
Refactor manifest: specific setter functions
Replace generic rhizome_manifest_set() and rhizome_manifest_set_ll()
with per-field setter functions, eg, rhizome_manifest_set_filesize().
Struct rhizome_manifest elements for all known fields, to replace the
use of rhizome_manifest_get() and rhizome_manifest_get_ll() everywhere:
sender, recipient, service, name, date, bundle_key.
Add boolean validity flags for binary blob types, to avoid having to compare
with many bytes of all-zero to detect presence, eg, has_sender, has_recipient,
has_author, has_bundle_key. These maintained by the setter functions.
Rename existing manifest struct elements to be the same as their field
names: fileLength -> filesize, journalTail -> tail.
More use of unsigned int, size_t and uint64_t for payload sizes, offsets, byte
counts, etc. especially in rhizome_store.c and meshms.c. More uniform use of
size_t to dimension memory buffers. Fix some printf(3) style format strings
for 64-bit correctness on 32-bit systems. Use new constant RHIZOME_SIZE_UNSET
instead of -1 to indicate unknown dimension, and explicitly assert its absence
before comparisons and arithmetic, for safety.
Replace some 'int' loop variables with 'unsigned' where appropriate.
Fix bugs discovered in MeshMS bundle private/public key generation and
bundle secret key handling for export/extract commands.
Instrument the first MeshMS test case to aid debugging.
New debug config flag: debug.manifest logs all modifications to all manifest
fields by setter functions.
Rename debug config flag: debug.rhizome_bind -> debug.rhizome_sql_bind.
2013-10-30 12:52:19 +00:00
STATIC_TEXT , m - > service ,
STATIC_TEXT | NUL , m - > name ,
SID_T | NUL , m - > has_sender ? & m - > sender : NULL ,
SID_T | NUL , m - > has_recipient ? & m - > recipient : NULL ,
INT64 , m - > tail ,
2013-10-10 06:45:52 +00:00
END
)
) = = NULL )
2012-08-23 08:13:35 +00:00
goto rollback ;
2012-08-24 05:56:25 +00:00
if ( sqlite_step_retry ( & retry , stmt ) = = - 1 )
2012-08-23 08:13:35 +00:00
goto rollback ;
sqlite3_finalize ( stmt ) ;
stmt = NULL ;
2013-11-11 07:43:38 +00:00
rhizome_manifest_set_rowid ( m , sqlite3_last_insert_rowid ( rhizome_db ) ) ;
2013-11-05 07:28:03 +00:00
rhizome_manifest_set_inserttime ( m , now ) ;
2012-08-23 08:13:35 +00:00
2013-06-18 03:57:26 +00:00
// if (serverMode)
// rhizome_sync_bundle_inserted(bar);
2012-12-31 04:39:12 +00:00
// TODO remove old payload?
2012-12-27 04:45:23 +00:00
Refactor manifest: specific setter functions
Replace generic rhizome_manifest_set() and rhizome_manifest_set_ll()
with per-field setter functions, eg, rhizome_manifest_set_filesize().
Struct rhizome_manifest elements for all known fields, to replace the
use of rhizome_manifest_get() and rhizome_manifest_get_ll() everywhere:
sender, recipient, service, name, date, bundle_key.
Add boolean validity flags for binary blob types, to avoid having to compare
with many bytes of all-zero to detect presence, eg, has_sender, has_recipient,
has_author, has_bundle_key. These maintained by the setter functions.
Rename existing manifest struct elements to be the same as their field
names: fileLength -> filesize, journalTail -> tail.
More use of unsigned int, size_t and uint64_t for payload sizes, offsets, byte
counts, etc. especially in rhizome_store.c and meshms.c. More uniform use of
size_t to dimension memory buffers. Fix some printf(3) style format strings
for 64-bit correctness on 32-bit systems. Use new constant RHIZOME_SIZE_UNSET
instead of -1 to indicate unknown dimension, and explicitly assert its absence
before comparisons and arithmetic, for safety.
Replace some 'int' loop variables with 'unsigned' where appropriate.
Fix bugs discovered in MeshMS bundle private/public key generation and
bundle secret key handling for export/extract commands.
Instrument the first MeshMS test case to aid debugging.
New debug config flag: debug.manifest logs all modifications to all manifest
fields by setter functions.
Rename debug config flag: debug.rhizome_bind -> debug.rhizome_sql_bind.
2013-10-30 12:52:19 +00:00
#if 0
2012-01-12 03:38:24 +00:00
if ( rhizome_manifest_get ( m , " isagroup " , NULL , 0 ) ! = NULL ) {
int closed = rhizome_manifest_get_ll ( m , " closedgroup " ) ;
if ( closed < 1 ) closed = 0 ;
int ciphered = rhizome_manifest_get_ll ( m , " cipheredgroup " ) ;
if ( ciphered < 1 ) ciphered = 0 ;
2013-10-10 06:45:52 +00:00
if ( ( stmt = sqlite_prepare_bind ( & retry ,
" INSERT OR REPLACE INTO GROUPLIST(id,closed,ciphered,priority) VALUES (?,?,?,?); " ,
RHIZOME_BID_T , & m - > cryptoSignPublic ,
INT , closed ,
INT , ciphered ,
INT , RHIZOME_PRIORITY_DEFAULT ,
END
)
) = = NULL
)
2012-08-23 08:13:35 +00:00
goto rollback ;
2012-08-24 05:56:25 +00:00
if ( sqlite_step_retry ( & retry , stmt ) = = - 1 )
2012-08-23 08:13:35 +00:00
goto rollback ;
sqlite3_finalize ( stmt ) ;
stmt = NULL ;
2012-01-12 03:38:24 +00:00
}
Refactor manifest: specific setter functions
Replace generic rhizome_manifest_set() and rhizome_manifest_set_ll()
with per-field setter functions, eg, rhizome_manifest_set_filesize().
Struct rhizome_manifest elements for all known fields, to replace the
use of rhizome_manifest_get() and rhizome_manifest_get_ll() everywhere:
sender, recipient, service, name, date, bundle_key.
Add boolean validity flags for binary blob types, to avoid having to compare
with many bytes of all-zero to detect presence, eg, has_sender, has_recipient,
has_author, has_bundle_key. These maintained by the setter functions.
Rename existing manifest struct elements to be the same as their field
names: fileLength -> filesize, journalTail -> tail.
More use of unsigned int, size_t and uint64_t for payload sizes, offsets, byte
counts, etc. especially in rhizome_store.c and meshms.c. More uniform use of
size_t to dimension memory buffers. Fix some printf(3) style format strings
for 64-bit correctness on 32-bit systems. Use new constant RHIZOME_SIZE_UNSET
instead of -1 to indicate unknown dimension, and explicitly assert its absence
before comparisons and arithmetic, for safety.
Replace some 'int' loop variables with 'unsigned' where appropriate.
Fix bugs discovered in MeshMS bundle private/public key generation and
bundle secret key handling for export/extract commands.
Instrument the first MeshMS test case to aid debugging.
New debug config flag: debug.manifest logs all modifications to all manifest
fields by setter functions.
Rename debug config flag: debug.rhizome_bind -> debug.rhizome_sql_bind.
2013-10-30 12:52:19 +00:00
# endif
2012-01-12 03:38:24 +00:00
Refactor manifest: specific setter functions
Replace generic rhizome_manifest_set() and rhizome_manifest_set_ll()
with per-field setter functions, eg, rhizome_manifest_set_filesize().
Struct rhizome_manifest elements for all known fields, to replace the
use of rhizome_manifest_get() and rhizome_manifest_get_ll() everywhere:
sender, recipient, service, name, date, bundle_key.
Add boolean validity flags for binary blob types, to avoid having to compare
with many bytes of all-zero to detect presence, eg, has_sender, has_recipient,
has_author, has_bundle_key. These maintained by the setter functions.
Rename existing manifest struct elements to be the same as their field
names: fileLength -> filesize, journalTail -> tail.
More use of unsigned int, size_t and uint64_t for payload sizes, offsets, byte
counts, etc. especially in rhizome_store.c and meshms.c. More uniform use of
size_t to dimension memory buffers. Fix some printf(3) style format strings
for 64-bit correctness on 32-bit systems. Use new constant RHIZOME_SIZE_UNSET
instead of -1 to indicate unknown dimension, and explicitly assert its absence
before comparisons and arithmetic, for safety.
Replace some 'int' loop variables with 'unsigned' where appropriate.
Fix bugs discovered in MeshMS bundle private/public key generation and
bundle secret key handling for export/extract commands.
Instrument the first MeshMS test case to aid debugging.
New debug config flag: debug.manifest logs all modifications to all manifest
fields by setter functions.
Rename debug config flag: debug.rhizome_bind -> debug.rhizome_sql_bind.
2013-10-30 12:52:19 +00:00
#if 0
2012-08-23 08:13:35 +00:00
if ( m - > group_count > 0 ) {
2013-10-10 06:45:52 +00:00
if ( ( stmt = sqlite_prepare ( & retry , " INSERT OR REPLACE INTO GROUPMEMBERSHIPS (manifestid, groupid) VALUES (?, ?); " ) ) = = NULL )
2012-08-23 08:13:35 +00:00
goto rollback ;
2013-10-30 03:15:51 +00:00
unsigned i ;
2012-05-28 02:29:35 +00:00
for ( i = 0 ; i < m - > group_count ; i + + ) {
2013-10-10 06:45:52 +00:00
if ( sqlite_bind ( & retry , stmt , RHIZOME_BID_T , & m - > cryptoSignPublic , TEXT , m - > groups [ i ] ) = = - 1 )
2012-08-23 08:13:35 +00:00
goto rollback ;
2012-08-24 05:56:25 +00:00
if ( sqlite_step_retry ( & retry , stmt ) = = - 1 )
2012-08-23 08:13:35 +00:00
goto rollback ;
2012-08-24 05:56:25 +00:00
sqlite3_reset ( stmt ) ;
2012-05-28 02:29:35 +00:00
}
2012-08-23 08:13:35 +00:00
sqlite3_finalize ( stmt ) ;
stmt = NULL ;
2012-05-28 02:29:35 +00:00
}
Refactor manifest: specific setter functions
Replace generic rhizome_manifest_set() and rhizome_manifest_set_ll()
with per-field setter functions, eg, rhizome_manifest_set_filesize().
Struct rhizome_manifest elements for all known fields, to replace the
use of rhizome_manifest_get() and rhizome_manifest_get_ll() everywhere:
sender, recipient, service, name, date, bundle_key.
Add boolean validity flags for binary blob types, to avoid having to compare
with many bytes of all-zero to detect presence, eg, has_sender, has_recipient,
has_author, has_bundle_key. These maintained by the setter functions.
Rename existing manifest struct elements to be the same as their field
names: fileLength -> filesize, journalTail -> tail.
More use of unsigned int, size_t and uint64_t for payload sizes, offsets, byte
counts, etc. especially in rhizome_store.c and meshms.c. More uniform use of
size_t to dimension memory buffers. Fix some printf(3) style format strings
for 64-bit correctness on 32-bit systems. Use new constant RHIZOME_SIZE_UNSET
instead of -1 to indicate unknown dimension, and explicitly assert its absence
before comparisons and arithmetic, for safety.
Replace some 'int' loop variables with 'unsigned' where appropriate.
Fix bugs discovered in MeshMS bundle private/public key generation and
bundle secret key handling for export/extract commands.
Instrument the first MeshMS test case to aid debugging.
New debug config flag: debug.manifest logs all modifications to all manifest
fields by setter functions.
Rename debug config flag: debug.rhizome_bind -> debug.rhizome_sql_bind.
2013-10-30 12:52:19 +00:00
# endif
2013-10-02 15:46:10 +00:00
if ( sqlite_exec_void_retry ( & retry , " COMMIT; " , END ) ! = - 1 ) {
2013-01-15 00:02:48 +00:00
// This message used in tests; do not modify or remove.
2013-12-11 00:41:34 +00:00
INFOF ( " RHIZOME ADD MANIFEST service=%s bid=%s version=% " PRIu64 ,
Refactor manifest: specific setter functions
Replace generic rhizome_manifest_set() and rhizome_manifest_set_ll()
with per-field setter functions, eg, rhizome_manifest_set_filesize().
Struct rhizome_manifest elements for all known fields, to replace the
use of rhizome_manifest_get() and rhizome_manifest_get_ll() everywhere:
sender, recipient, service, name, date, bundle_key.
Add boolean validity flags for binary blob types, to avoid having to compare
with many bytes of all-zero to detect presence, eg, has_sender, has_recipient,
has_author, has_bundle_key. These maintained by the setter functions.
Rename existing manifest struct elements to be the same as their field
names: fileLength -> filesize, journalTail -> tail.
More use of unsigned int, size_t and uint64_t for payload sizes, offsets, byte
counts, etc. especially in rhizome_store.c and meshms.c. More uniform use of
size_t to dimension memory buffers. Fix some printf(3) style format strings
for 64-bit correctness on 32-bit systems. Use new constant RHIZOME_SIZE_UNSET
instead of -1 to indicate unknown dimension, and explicitly assert its absence
before comparisons and arithmetic, for safety.
Replace some 'int' loop variables with 'unsigned' where appropriate.
Fix bugs discovered in MeshMS bundle private/public key generation and
bundle secret key handling for export/extract commands.
Instrument the first MeshMS test case to aid debugging.
New debug config flag: debug.manifest logs all modifications to all manifest
fields by setter functions.
Rename debug config flag: debug.rhizome_bind -> debug.rhizome_sql_bind.
2013-10-30 12:52:19 +00:00
m - > service ? m - > service : " NULL " ,
2013-10-03 13:46:45 +00:00
alloca_tohex_rhizome_bid_t ( m - > cryptoSignPublic ) ,
2013-01-15 00:02:48 +00:00
m - > version
Refactor manifest: specific setter functions
Replace generic rhizome_manifest_set() and rhizome_manifest_set_ll()
with per-field setter functions, eg, rhizome_manifest_set_filesize().
Struct rhizome_manifest elements for all known fields, to replace the
use of rhizome_manifest_get() and rhizome_manifest_get_ll() everywhere:
sender, recipient, service, name, date, bundle_key.
Add boolean validity flags for binary blob types, to avoid having to compare
with many bytes of all-zero to detect presence, eg, has_sender, has_recipient,
has_author, has_bundle_key. These maintained by the setter functions.
Rename existing manifest struct elements to be the same as their field
names: fileLength -> filesize, journalTail -> tail.
More use of unsigned int, size_t and uint64_t for payload sizes, offsets, byte
counts, etc. especially in rhizome_store.c and meshms.c. More uniform use of
size_t to dimension memory buffers. Fix some printf(3) style format strings
for 64-bit correctness on 32-bit systems. Use new constant RHIZOME_SIZE_UNSET
instead of -1 to indicate unknown dimension, and explicitly assert its absence
before comparisons and arithmetic, for safety.
Replace some 'int' loop variables with 'unsigned' where appropriate.
Fix bugs discovered in MeshMS bundle private/public key generation and
bundle secret key handling for export/extract commands.
Instrument the first MeshMS test case to aid debugging.
New debug config flag: debug.manifest logs all modifications to all manifest
fields by setter functions.
Rename debug config flag: debug.rhizome_bind -> debug.rhizome_sql_bind.
2013-10-30 12:52:19 +00:00
) ;
2013-01-15 00:02:48 +00:00
monitor_announce_bundle ( m ) ;
2013-09-17 04:45:12 +00:00
if ( serverMode )
rhizome_sync_announce ( ) ;
2012-05-28 02:29:35 +00:00
return 0 ;
2013-01-15 00:02:48 +00:00
}
2012-08-23 08:13:35 +00:00
rollback :
if ( stmt )
sqlite3_finalize ( stmt ) ;
2013-10-10 06:45:52 +00:00
WHYF ( " Failed to store bundle bid=%s " , alloca_tohex_rhizome_bid_t ( m - > cryptoSignPublic ) ) ;
2013-10-02 15:46:10 +00:00
sqlite_exec_void_retry ( & retry , " ROLLBACK; " , END ) ;
2012-05-28 02:29:35 +00:00
return - 1 ;
2012-01-12 03:38:24 +00:00
}
2013-11-05 07:28:03 +00:00
/* The cursor struct must be zerofilled and the query parameters optionally filled in prior to
* calling this function .
2013-11-04 13:17:09 +00:00
*
* @ author Andrew Bettison < andrew @ servalproject . com >
*/
2014-01-22 06:50:06 +00:00
int rhizome_list_open ( struct rhizome_list_cursor * c )
2013-11-04 13:17:09 +00:00
{
2013-11-19 05:11:12 +00:00
if ( config . debug . rhizome )
DEBUGF ( " c=%p c->service=%s c->name=%s c->sender=%s c->recipient=%s c->rowid_since=% " PRIu64 " c->_rowid_last=% " PRIu64 ,
c ,
alloca_str_toprint ( c - > service ) ,
alloca_str_toprint ( c - > name ) ,
c - > is_sender_set ? alloca_tohex_sid_t ( c - > sender ) : " UNSET " ,
c - > is_recipient_set ? alloca_tohex_sid_t ( c - > recipient ) : " UNSET " ,
c - > rowid_since ,
c - > _rowid_last
) ;
2013-11-04 13:17:09 +00:00
IN ( ) ;
2012-05-20 14:39:14 +00:00
strbuf b = strbuf_alloca ( 1024 ) ;
2013-01-22 04:56:40 +00:00
strbuf_sprintf ( b , " SELECT id, manifest, version, inserttime, author, rowid FROM manifests WHERE 1=1 " ) ;
2013-11-07 06:52:06 +00:00
if ( c - > service )
2013-11-04 13:17:09 +00:00
strbuf_puts ( b , " AND service = @service " ) ;
2013-11-07 06:52:06 +00:00
if ( c - > name )
2013-11-04 13:17:09 +00:00
strbuf_puts ( b , " AND name like @name " ) ;
2013-11-07 06:52:06 +00:00
if ( c - > is_sender_set )
2013-11-04 13:17:09 +00:00
strbuf_puts ( b , " AND sender = @sender " ) ;
2013-11-07 06:52:06 +00:00
if ( c - > is_recipient_set )
2013-11-04 13:17:09 +00:00
strbuf_puts ( b , " AND recipient = @recipient " ) ;
2013-11-19 05:11:12 +00:00
if ( c - > rowid_since ) {
strbuf_puts ( b , " AND rowid > @last ORDER BY rowid ASC " ) ; // oldest first
if ( c - > _rowid_last < c - > rowid_since )
c - > _rowid_last = c - > rowid_since ;
} else {
if ( c - > _rowid_last )
strbuf_puts ( b , " AND rowid < @last " ) ;
strbuf_puts ( b , " ORDER BY rowid DESC " ) ; // most recent first
}
2012-05-20 14:39:14 +00:00
if ( strbuf_overrun ( b ) )
2013-07-15 00:29:24 +00:00
RETURN ( WHYF ( " SQL command too long: %s " , strbuf_str ( b ) ) ) ;
2014-01-22 06:50:06 +00:00
c - > _retry = SQLITE_RETRY_STATE_DEFAULT ;
c - > _statement = sqlite_prepare ( & c - > _retry , strbuf_str ( b ) ) ;
2013-11-07 06:52:06 +00:00
if ( c - > _statement = = NULL )
2012-11-12 04:07:58 +00:00
RETURN ( - 1 ) ;
2014-01-22 06:50:06 +00:00
if ( c - > service & & sqlite_bind ( & c - > _retry , c - > _statement , NAMED | STATIC_TEXT , " @service " , c - > service , END ) = = - 1 )
2013-11-04 13:17:09 +00:00
goto failure ;
2014-01-22 06:50:06 +00:00
if ( c - > name & & sqlite_bind ( & c - > _retry , c - > _statement , NAMED | STATIC_TEXT , " @name " , c - > name , END ) = = - 1 )
2013-11-04 13:17:09 +00:00
goto failure ;
2014-01-22 06:50:06 +00:00
if ( c - > is_sender_set & & sqlite_bind ( & c - > _retry , c - > _statement , NAMED | SID_T , " @sender " , & c - > sender , END ) = = - 1 )
2013-11-04 13:17:09 +00:00
goto failure ;
2014-01-22 06:50:06 +00:00
if ( c - > is_recipient_set & & sqlite_bind ( & c - > _retry , c - > _statement , NAMED | SID_T , " @recipient " , & c - > recipient , END ) = = - 1 )
2013-11-04 13:17:09 +00:00
goto failure ;
2014-01-22 06:50:06 +00:00
if ( c - > _rowid_last & & sqlite_bind ( & c - > _retry , c - > _statement , NAMED | INT64 , " @last " , c - > _rowid_last , END ) = = - 1 )
2013-11-04 13:17:09 +00:00
goto failure ;
2013-11-07 06:52:06 +00:00
c - > manifest = NULL ;
2013-11-13 06:28:28 +00:00
c - > _rowid_current = 0 ;
2013-11-04 13:17:09 +00:00
RETURN ( 0 ) ;
OUT ( ) ;
failure :
2013-11-07 06:52:06 +00:00
sqlite3_finalize ( c - > _statement ) ;
c - > _statement = NULL ;
2013-11-04 13:17:09 +00:00
RETURN ( - 1 ) ;
OUT ( ) ;
}
2013-11-13 06:28:28 +00:00
/* Guaranteed to return manifests with monotonically descending rowid. The first manifest will have
* the greatest rowid .
*
* Returns 1 if a new manifest has been fetched from the list , in which case the cursor ' manifest '
* field points to the fetched manifest . Returns 0 if there are no more manifests in the list .
*
* @ author Andrew Bettison < andrew @ servalproject . com >
*/
2014-01-22 06:50:06 +00:00
int rhizome_list_next ( struct rhizome_list_cursor * c )
2013-11-04 13:17:09 +00:00
{
2013-11-19 05:11:12 +00:00
if ( config . debug . rhizome )
DEBUGF ( " c=%p c->service=%s c->name=%s c->sender=%s c->recipient=%s c->rowid_since=% " PRIu64 " c->_rowid_last=% " PRIu64 ,
c ,
alloca_str_toprint ( c - > service ) ,
alloca_str_toprint ( c - > name ) ,
c - > is_sender_set ? alloca_tohex_sid_t ( c - > sender ) : " UNSET " ,
c - > is_recipient_set ? alloca_tohex_sid_t ( c - > recipient ) : " UNSET " ,
c - > rowid_since ,
c - > _rowid_last
) ;
2013-11-04 13:17:09 +00:00
IN ( ) ;
2014-01-22 06:50:06 +00:00
if ( c - > _statement = = NULL & & rhizome_list_open ( c ) = = - 1 )
2013-11-04 13:17:09 +00:00
RETURN ( - 1 ) ;
2013-11-13 06:28:28 +00:00
while ( 1 ) {
2013-11-07 06:52:06 +00:00
if ( c - > manifest ) {
rhizome_manifest_free ( c - > manifest ) ;
2013-11-13 06:28:28 +00:00
c - > _rowid_current = 0 ;
2013-11-07 06:52:06 +00:00
c - > manifest = NULL ;
2013-11-04 13:17:09 +00:00
}
2014-01-22 06:50:06 +00:00
if ( sqlite_step_retry ( & c - > _retry , c - > _statement ) ! = SQLITE_ROW )
2013-11-13 06:28:28 +00:00
break ;
2013-11-07 06:52:06 +00:00
assert ( sqlite3_column_count ( c - > _statement ) = = 6 ) ;
assert ( sqlite3_column_type ( c - > _statement , 0 ) = = SQLITE_TEXT ) ;
assert ( sqlite3_column_type ( c - > _statement , 1 ) = = SQLITE_BLOB ) ;
assert ( sqlite3_column_type ( c - > _statement , 2 ) = = SQLITE_INTEGER ) ;
assert ( sqlite3_column_type ( c - > _statement , 3 ) = = SQLITE_INTEGER ) ;
assert ( sqlite3_column_type ( c - > _statement , 4 ) = = SQLITE_TEXT | | sqlite3_column_type ( c - > _statement , 4 ) = = SQLITE_NULL ) ;
assert ( sqlite3_column_type ( c - > _statement , 5 ) = = SQLITE_INTEGER ) ;
2013-11-13 06:28:28 +00:00
uint64_t q_rowid = sqlite3_column_int64 ( c - > _statement , 5 ) ;
2013-11-19 05:11:12 +00:00
if ( c - > _rowid_current & & ( c - > rowid_since ? q_rowid > = c - > _rowid_current : q_rowid < = c - > _rowid_current ) ) {
2013-11-13 06:28:28 +00:00
WHYF ( " Query returned rowid=% " PRIu64 " out of order (last was % " PRIu64 " ) -- skipped " , q_rowid , c - > _rowid_current ) ;
continue ;
}
c - > _rowid_current = q_rowid ;
if ( q_rowid < = c - > rowid_since ) {
WHYF ( " Query returned rowid=% " PRIu64 " <= rowid_since=% " PRIu64 " -- skipped " , q_rowid , c - > rowid_since ) ;
continue ;
}
2013-11-07 06:52:06 +00:00
const char * q_manifestid = ( const char * ) sqlite3_column_text ( c - > _statement , 0 ) ;
const char * manifestblob = ( char * ) sqlite3_column_blob ( c - > _statement , 1 ) ;
size_t manifestblobsize = sqlite3_column_bytes ( c - > _statement , 1 ) ; // must call after sqlite3_column_blob()
2013-12-11 00:41:34 +00:00
uint64_t q_version = sqlite3_column_int64 ( c - > _statement , 2 ) ;
2013-11-07 06:52:06 +00:00
int64_t q_inserttime = sqlite3_column_int64 ( c - > _statement , 3 ) ;
const char * q_author = ( const char * ) sqlite3_column_text ( c - > _statement , 4 ) ;
2013-11-05 07:28:03 +00:00
sid_t * author = NULL ;
2013-11-04 13:17:09 +00:00
if ( q_author ) {
author = alloca ( sizeof * author ) ;
if ( str_to_sid_t ( author , q_author ) = = - 1 ) {
WHYF ( " MANIFESTS row id=%s has invalid author column %s -- skipped " , q_manifestid , alloca_str_toprint ( q_author ) ) ;
continue ;
}
}
2013-11-07 06:52:06 +00:00
rhizome_manifest * m = c - > manifest = rhizome_new_manifest ( ) ;
2013-11-05 07:28:03 +00:00
if ( m = = NULL )
RETURN ( - 1 ) ;
2013-12-19 08:37:14 +00:00
memcpy ( m - > manifestdata , manifestblob , manifestblobsize ) ;
m - > manifest_all_bytes = manifestblobsize ;
if ( rhizome_manifest_parse ( m ) = = - 1
2013-11-28 07:14:37 +00:00
| | ! rhizome_manifest_validate ( m )
) {
2013-11-05 07:28:03 +00:00
WHYF ( " MANIFESTS row id=%s has invalid manifest blob -- skipped " , q_manifestid ) ;
2013-11-04 13:17:09 +00:00
continue ;
}
if ( m - > version ! = q_version ) {
2013-12-11 00:41:34 +00:00
WHYF ( " MANIFESTS row id=%s version=% " PRIu64 " does not match manifest blob version=% " PRIu64 " -- skipped " ,
2013-11-04 13:17:09 +00:00
q_manifestid , q_version , m - > version ) ;
continue ;
}
2013-11-05 07:28:03 +00:00
if ( author )
rhizome_manifest_set_author ( m , author ) ;
2013-11-11 07:43:38 +00:00
rhizome_manifest_set_rowid ( m , q_rowid ) ;
2013-11-05 07:28:03 +00:00
rhizome_manifest_set_inserttime ( m , q_inserttime ) ;
2013-11-07 06:52:06 +00:00
if ( c - > service & & ! ( m - > service & & strcasecmp ( c - > service , m - > service ) = = 0 ) )
2013-11-04 13:17:09 +00:00
continue ;
2013-11-07 06:52:06 +00:00
if ( c - > is_sender_set & & ! ( m - > has_sender & & cmp_sid_t ( & c - > sender , & m - > sender ) = = 0 ) )
2013-11-04 13:17:09 +00:00
continue ;
2013-11-07 06:52:06 +00:00
if ( c - > is_recipient_set & & ! ( m - > has_recipient & & cmp_sid_t ( & c - > recipient , & m - > recipient ) = = 0 ) )
2013-11-04 13:17:09 +00:00
continue ;
2013-11-13 06:28:28 +00:00
assert ( c - > _rowid_current ! = 0 ) ;
2013-11-04 13:17:09 +00:00
// Don't do rhizome_verify_author(m); too CPU expensive for a listing. Save that for when
// the bundle is extracted or exported.
RETURN ( 1 ) ;
2013-01-17 01:13:31 +00:00
}
2013-11-13 06:28:28 +00:00
assert ( c - > _rowid_current = = 0 ) ;
2013-11-04 13:17:09 +00:00
RETURN ( 0 ) ;
OUT ( ) ;
}
2013-11-08 07:59:49 +00:00
void rhizome_list_commit ( struct rhizome_list_cursor * c )
2013-11-04 13:17:09 +00:00
{
2013-11-19 05:11:12 +00:00
if ( config . debug . rhizome )
DEBUGF ( " c=%p c->rowid_since=% " PRIu64 " c->_rowid_current=% " PRIu64 " c->_rowid_last=% " PRIu64 ,
c , c - > rowid_since , c - > _rowid_current , c - > _rowid_last ) ;
2013-11-13 06:28:28 +00:00
assert ( c - > _rowid_current ! = 0 ) ;
2013-11-19 05:11:12 +00:00
if ( c - > _rowid_last = = 0 | | ( c - > rowid_since ? c - > _rowid_current > c - > _rowid_last : c - > _rowid_current < c - > _rowid_last ) )
2013-11-13 06:28:28 +00:00
c - > _rowid_last = c - > _rowid_current ;
2013-11-04 13:17:09 +00:00
}
2013-11-07 06:52:06 +00:00
void rhizome_list_release ( struct rhizome_list_cursor * c )
2013-11-04 13:17:09 +00:00
{
2013-11-19 05:11:12 +00:00
if ( config . debug . rhizome )
DEBUGF ( " c=%p " , c ) ;
2013-11-07 06:52:06 +00:00
if ( c - > manifest ) {
rhizome_manifest_free ( c - > manifest ) ;
2013-11-13 06:28:28 +00:00
c - > _rowid_current = 0 ;
2013-11-07 06:52:06 +00:00
c - > manifest = NULL ;
2013-11-04 13:17:09 +00:00
}
2013-11-07 06:52:06 +00:00
if ( c - > _statement ) {
sqlite3_finalize ( c - > _statement ) ;
c - > _statement = NULL ;
2013-01-22 04:56:40 +00:00
}
2012-04-02 08:12:40 +00:00
}
2012-01-12 03:38:24 +00:00
2012-05-23 06:34:00 +00:00
void rhizome_bytes_to_hex_upper ( unsigned const char * in , char * out , int byteCount )
2012-01-12 03:38:24 +00:00
{
2013-10-09 08:24:21 +00:00
( void ) tohex ( out , byteCount * 2 , in ) ;
2012-01-12 03:38:24 +00:00
}
2012-05-26 04:12:33 +00:00
int rhizome_update_file_priority ( const char * fileid )
2012-01-12 03:38:24 +00:00
{
2012-05-28 09:54:02 +00:00
/* work out the highest priority of any referrer */
2013-12-11 00:41:34 +00:00
uint64_t highestPriority = 0 ;
2012-08-23 08:13:35 +00:00
sqlite_retry_state retry = SQLITE_RETRY_STATE_DEFAULT ;
2013-12-11 00:41:34 +00:00
if ( sqlite_exec_uint64_retry ( & retry , & highestPriority ,
2013-10-02 15:46:10 +00:00
" SELECT max(grouplist.priority) FROM MANIFESTS, GROUPMEMBERSHIPS, GROUPLIST "
" WHERE MANIFESTS.filehash = ? "
" AND GROUPMEMBERSHIPS.manifestid = MANIFESTS.id "
" AND GROUPMEMBERSHIPS.groupid = GROUPLIST.id; " ,
2013-12-11 00:41:34 +00:00
TEXT_TOUPPER , fileid , END
) = = - 1
)
2012-06-08 03:43:26 +00:00
return - 1 ;
2013-12-11 00:41:34 +00:00
if ( sqlite_exec_void_retry ( & retry ,
" UPDATE files SET highestPriority = ? WHERE id = ?; " ,
INT64 , highestPriority , TEXT_TOUPPER , fileid , END
) = = - 1
2013-10-02 15:46:10 +00:00
)
2013-02-20 04:14:29 +00:00
return WHYF ( " cannot update priority for fileid=%s " , fileid ) ;
2012-01-12 03:38:24 +00:00
return 0 ;
}
2012-04-12 09:00:52 +00:00
2013-09-30 06:45:01 +00:00
/* Search the database for a manifest having the same name and payload content, and if the version
2013-12-19 08:43:39 +00:00
* is known , having the same version . Returns RHIZOME_BUNDLE_STATUS_DUPLICATE if a duplicate is found
* ( setting * found to point to the duplicate ' s manifest ) , returns RHIZOME_BUNDLE_STATUS_NEW if no
* duplicate is found ( leaving * found unchanged ) . Returns - 1 on error ( leaving * found undefined ) .
2013-09-30 06:45:01 +00:00
*
* @ author Andrew Bettison < andrew @ servalproject . com >
2012-04-12 09:00:52 +00:00
*/
2013-12-19 08:43:39 +00:00
enum rhizome_bundle_status rhizome_find_duplicate ( const rhizome_manifest * m , rhizome_manifest * * found )
2012-04-12 09:00:52 +00:00
{
Refactor manifest: specific setter functions
Replace generic rhizome_manifest_set() and rhizome_manifest_set_ll()
with per-field setter functions, eg, rhizome_manifest_set_filesize().
Struct rhizome_manifest elements for all known fields, to replace the
use of rhizome_manifest_get() and rhizome_manifest_get_ll() everywhere:
sender, recipient, service, name, date, bundle_key.
Add boolean validity flags for binary blob types, to avoid having to compare
with many bytes of all-zero to detect presence, eg, has_sender, has_recipient,
has_author, has_bundle_key. These maintained by the setter functions.
Rename existing manifest struct elements to be the same as their field
names: fileLength -> filesize, journalTail -> tail.
More use of unsigned int, size_t and uint64_t for payload sizes, offsets, byte
counts, etc. especially in rhizome_store.c and meshms.c. More uniform use of
size_t to dimension memory buffers. Fix some printf(3) style format strings
for 64-bit correctness on 32-bit systems. Use new constant RHIZOME_SIZE_UNSET
instead of -1 to indicate unknown dimension, and explicitly assert its absence
before comparisons and arithmetic, for safety.
Replace some 'int' loop variables with 'unsigned' where appropriate.
Fix bugs discovered in MeshMS bundle private/public key generation and
bundle secret key handling for export/extract commands.
Instrument the first MeshMS test case to aid debugging.
New debug config flag: debug.manifest logs all modifications to all manifest
fields by setter functions.
Rename debug config flag: debug.rhizome_bind -> debug.rhizome_sql_bind.
2013-10-30 12:52:19 +00:00
if ( m - > service = = NULL )
2012-05-20 14:39:14 +00:00
return WHY ( " Manifest has no service " ) ;
2012-04-12 09:00:52 +00:00
char sqlcmd [ 1024 ] ;
2012-07-16 09:01:00 +00:00
strbuf b = strbuf_local ( sqlcmd , sizeof sqlcmd ) ;
2013-08-01 02:07:35 +00:00
strbuf_puts ( b , " SELECT id, manifest, author FROM manifests WHERE filesize = ? AND service = ? " ) ;
Refactor manifest: specific setter functions
Replace generic rhizome_manifest_set() and rhizome_manifest_set_ll()
with per-field setter functions, eg, rhizome_manifest_set_filesize().
Struct rhizome_manifest elements for all known fields, to replace the
use of rhizome_manifest_get() and rhizome_manifest_get_ll() everywhere:
sender, recipient, service, name, date, bundle_key.
Add boolean validity flags for binary blob types, to avoid having to compare
with many bytes of all-zero to detect presence, eg, has_sender, has_recipient,
has_author, has_bundle_key. These maintained by the setter functions.
Rename existing manifest struct elements to be the same as their field
names: fileLength -> filesize, journalTail -> tail.
More use of unsigned int, size_t and uint64_t for payload sizes, offsets, byte
counts, etc. especially in rhizome_store.c and meshms.c. More uniform use of
size_t to dimension memory buffers. Fix some printf(3) style format strings
for 64-bit correctness on 32-bit systems. Use new constant RHIZOME_SIZE_UNSET
instead of -1 to indicate unknown dimension, and explicitly assert its absence
before comparisons and arithmetic, for safety.
Replace some 'int' loop variables with 'unsigned' where appropriate.
Fix bugs discovered in MeshMS bundle private/public key generation and
bundle secret key handling for export/extract commands.
Instrument the first MeshMS test case to aid debugging.
New debug config flag: debug.manifest logs all modifications to all manifest
fields by setter functions.
Rename debug config flag: debug.rhizome_bind -> debug.rhizome_sql_bind.
2013-10-30 12:52:19 +00:00
assert ( m - > filesize ! = RHIZOME_SIZE_UNSET ) ;
if ( m - > filesize > 0 )
2013-08-01 02:07:35 +00:00
strbuf_puts ( b , " AND filehash = ? " ) ;
Refactor manifest: specific setter functions
Replace generic rhizome_manifest_set() and rhizome_manifest_set_ll()
with per-field setter functions, eg, rhizome_manifest_set_filesize().
Struct rhizome_manifest elements for all known fields, to replace the
use of rhizome_manifest_get() and rhizome_manifest_get_ll() everywhere:
sender, recipient, service, name, date, bundle_key.
Add boolean validity flags for binary blob types, to avoid having to compare
with many bytes of all-zero to detect presence, eg, has_sender, has_recipient,
has_author, has_bundle_key. These maintained by the setter functions.
Rename existing manifest struct elements to be the same as their field
names: fileLength -> filesize, journalTail -> tail.
More use of unsigned int, size_t and uint64_t for payload sizes, offsets, byte
counts, etc. especially in rhizome_store.c and meshms.c. More uniform use of
size_t to dimension memory buffers. Fix some printf(3) style format strings
for 64-bit correctness on 32-bit systems. Use new constant RHIZOME_SIZE_UNSET
instead of -1 to indicate unknown dimension, and explicitly assert its absence
before comparisons and arithmetic, for safety.
Replace some 'int' loop variables with 'unsigned' where appropriate.
Fix bugs discovered in MeshMS bundle private/public key generation and
bundle secret key handling for export/extract commands.
Instrument the first MeshMS test case to aid debugging.
New debug config flag: debug.manifest logs all modifications to all manifest
fields by setter functions.
Rename debug config flag: debug.rhizome_bind -> debug.rhizome_sql_bind.
2013-10-30 12:52:19 +00:00
if ( m - > name )
2013-08-01 02:07:35 +00:00
strbuf_puts ( b , " AND name = ? " ) ;
Refactor manifest: specific setter functions
Replace generic rhizome_manifest_set() and rhizome_manifest_set_ll()
with per-field setter functions, eg, rhizome_manifest_set_filesize().
Struct rhizome_manifest elements for all known fields, to replace the
use of rhizome_manifest_get() and rhizome_manifest_get_ll() everywhere:
sender, recipient, service, name, date, bundle_key.
Add boolean validity flags for binary blob types, to avoid having to compare
with many bytes of all-zero to detect presence, eg, has_sender, has_recipient,
has_author, has_bundle_key. These maintained by the setter functions.
Rename existing manifest struct elements to be the same as their field
names: fileLength -> filesize, journalTail -> tail.
More use of unsigned int, size_t and uint64_t for payload sizes, offsets, byte
counts, etc. especially in rhizome_store.c and meshms.c. More uniform use of
size_t to dimension memory buffers. Fix some printf(3) style format strings
for 64-bit correctness on 32-bit systems. Use new constant RHIZOME_SIZE_UNSET
instead of -1 to indicate unknown dimension, and explicitly assert its absence
before comparisons and arithmetic, for safety.
Replace some 'int' loop variables with 'unsigned' where appropriate.
Fix bugs discovered in MeshMS bundle private/public key generation and
bundle secret key handling for export/extract commands.
Instrument the first MeshMS test case to aid debugging.
New debug config flag: debug.manifest logs all modifications to all manifest
fields by setter functions.
Rename debug config flag: debug.rhizome_bind -> debug.rhizome_sql_bind.
2013-10-30 12:52:19 +00:00
if ( m - > has_sender )
2013-08-01 02:07:35 +00:00
strbuf_puts ( b , " AND sender = ? " ) ;
Refactor manifest: specific setter functions
Replace generic rhizome_manifest_set() and rhizome_manifest_set_ll()
with per-field setter functions, eg, rhizome_manifest_set_filesize().
Struct rhizome_manifest elements for all known fields, to replace the
use of rhizome_manifest_get() and rhizome_manifest_get_ll() everywhere:
sender, recipient, service, name, date, bundle_key.
Add boolean validity flags for binary blob types, to avoid having to compare
with many bytes of all-zero to detect presence, eg, has_sender, has_recipient,
has_author, has_bundle_key. These maintained by the setter functions.
Rename existing manifest struct elements to be the same as their field
names: fileLength -> filesize, journalTail -> tail.
More use of unsigned int, size_t and uint64_t for payload sizes, offsets, byte
counts, etc. especially in rhizome_store.c and meshms.c. More uniform use of
size_t to dimension memory buffers. Fix some printf(3) style format strings
for 64-bit correctness on 32-bit systems. Use new constant RHIZOME_SIZE_UNSET
instead of -1 to indicate unknown dimension, and explicitly assert its absence
before comparisons and arithmetic, for safety.
Replace some 'int' loop variables with 'unsigned' where appropriate.
Fix bugs discovered in MeshMS bundle private/public key generation and
bundle secret key handling for export/extract commands.
Instrument the first MeshMS test case to aid debugging.
New debug config flag: debug.manifest logs all modifications to all manifest
fields by setter functions.
Rename debug config flag: debug.rhizome_bind -> debug.rhizome_sql_bind.
2013-10-30 12:52:19 +00:00
if ( m - > has_recipient )
2013-08-01 02:07:35 +00:00
strbuf_puts ( b , " AND recipient = ? " ) ;
2012-07-16 09:01:00 +00:00
if ( strbuf_overrun ( b ) )
return WHYF ( " SQL command too long: %s " , strbuf_str ( b ) ) ;
2013-12-19 08:43:39 +00:00
int ret = RHIZOME_BUNDLE_STATUS_NEW ;
2012-10-04 05:00:20 +00:00
sqlite_retry_state retry = SQLITE_RETRY_STATE_DEFAULT ;
Refactor manifest: specific setter functions
Replace generic rhizome_manifest_set() and rhizome_manifest_set_ll()
with per-field setter functions, eg, rhizome_manifest_set_filesize().
Struct rhizome_manifest elements for all known fields, to replace the
use of rhizome_manifest_get() and rhizome_manifest_get_ll() everywhere:
sender, recipient, service, name, date, bundle_key.
Add boolean validity flags for binary blob types, to avoid having to compare
with many bytes of all-zero to detect presence, eg, has_sender, has_recipient,
has_author, has_bundle_key. These maintained by the setter functions.
Rename existing manifest struct elements to be the same as their field
names: fileLength -> filesize, journalTail -> tail.
More use of unsigned int, size_t and uint64_t for payload sizes, offsets, byte
counts, etc. especially in rhizome_store.c and meshms.c. More uniform use of
size_t to dimension memory buffers. Fix some printf(3) style format strings
for 64-bit correctness on 32-bit systems. Use new constant RHIZOME_SIZE_UNSET
instead of -1 to indicate unknown dimension, and explicitly assert its absence
before comparisons and arithmetic, for safety.
Replace some 'int' loop variables with 'unsigned' where appropriate.
Fix bugs discovered in MeshMS bundle private/public key generation and
bundle secret key handling for export/extract commands.
Instrument the first MeshMS test case to aid debugging.
New debug config flag: debug.manifest logs all modifications to all manifest
fields by setter functions.
Rename debug config flag: debug.rhizome_bind -> debug.rhizome_sql_bind.
2013-10-30 12:52:19 +00:00
sqlite3_stmt * statement = sqlite_prepare_bind ( & retry , strbuf_str ( b ) , INT64 , m - > filesize , STATIC_TEXT , m - > service , END ) ;
2012-08-23 08:13:35 +00:00
if ( ! statement )
return - 1 ;
2013-10-10 07:53:06 +00:00
int field = 2 ;
Refactor manifest: specific setter functions
Replace generic rhizome_manifest_set() and rhizome_manifest_set_ll()
with per-field setter functions, eg, rhizome_manifest_set_filesize().
Struct rhizome_manifest elements for all known fields, to replace the
use of rhizome_manifest_get() and rhizome_manifest_get_ll() everywhere:
sender, recipient, service, name, date, bundle_key.
Add boolean validity flags for binary blob types, to avoid having to compare
with many bytes of all-zero to detect presence, eg, has_sender, has_recipient,
has_author, has_bundle_key. These maintained by the setter functions.
Rename existing manifest struct elements to be the same as their field
names: fileLength -> filesize, journalTail -> tail.
More use of unsigned int, size_t and uint64_t for payload sizes, offsets, byte
counts, etc. especially in rhizome_store.c and meshms.c. More uniform use of
size_t to dimension memory buffers. Fix some printf(3) style format strings
for 64-bit correctness on 32-bit systems. Use new constant RHIZOME_SIZE_UNSET
instead of -1 to indicate unknown dimension, and explicitly assert its absence
before comparisons and arithmetic, for safety.
Replace some 'int' loop variables with 'unsigned' where appropriate.
Fix bugs discovered in MeshMS bundle private/public key generation and
bundle secret key handling for export/extract commands.
Instrument the first MeshMS test case to aid debugging.
New debug config flag: debug.manifest logs all modifications to all manifest
fields by setter functions.
Rename debug config flag: debug.rhizome_bind -> debug.rhizome_sql_bind.
2013-10-30 12:52:19 +00:00
if ( m - > filesize > 0 )
2013-10-10 07:53:06 +00:00
sqlite_bind ( & retry , statement , INDEX | RHIZOME_FILEHASH_T , + + field , & m - > filehash , END ) ;
Refactor manifest: specific setter functions
Replace generic rhizome_manifest_set() and rhizome_manifest_set_ll()
with per-field setter functions, eg, rhizome_manifest_set_filesize().
Struct rhizome_manifest elements for all known fields, to replace the
use of rhizome_manifest_get() and rhizome_manifest_get_ll() everywhere:
sender, recipient, service, name, date, bundle_key.
Add boolean validity flags for binary blob types, to avoid having to compare
with many bytes of all-zero to detect presence, eg, has_sender, has_recipient,
has_author, has_bundle_key. These maintained by the setter functions.
Rename existing manifest struct elements to be the same as their field
names: fileLength -> filesize, journalTail -> tail.
More use of unsigned int, size_t and uint64_t for payload sizes, offsets, byte
counts, etc. especially in rhizome_store.c and meshms.c. More uniform use of
size_t to dimension memory buffers. Fix some printf(3) style format strings
for 64-bit correctness on 32-bit systems. Use new constant RHIZOME_SIZE_UNSET
instead of -1 to indicate unknown dimension, and explicitly assert its absence
before comparisons and arithmetic, for safety.
Replace some 'int' loop variables with 'unsigned' where appropriate.
Fix bugs discovered in MeshMS bundle private/public key generation and
bundle secret key handling for export/extract commands.
Instrument the first MeshMS test case to aid debugging.
New debug config flag: debug.manifest logs all modifications to all manifest
fields by setter functions.
Rename debug config flag: debug.rhizome_bind -> debug.rhizome_sql_bind.
2013-10-30 12:52:19 +00:00
if ( m - > name )
sqlite_bind ( & retry , statement , INDEX | STATIC_TEXT , + + field , m - > name , END ) ;
if ( m - > has_sender )
sqlite_bind ( & retry , statement , INDEX | SID_T , + + field , & m - > sender , END ) ;
if ( m - > has_recipient )
sqlite_bind ( & retry , statement , INDEX | SID_T , + + field , & m - > recipient , END ) ;
2013-10-10 07:53:06 +00:00
2013-07-15 00:29:24 +00:00
int rows = 0 ;
2012-08-24 05:56:25 +00:00
while ( sqlite_step_retry ( & retry , statement ) = = SQLITE_ROW ) {
2012-08-23 08:13:35 +00:00
+ + rows ;
2013-08-01 02:07:35 +00:00
if ( config . debug . rhizome )
DEBUGF ( " Row %d " , rows ) ;
2012-08-23 08:13:35 +00:00
rhizome_manifest * blob_m = rhizome_new_manifest ( ) ;
if ( blob_m = = NULL ) {
ret = WHY ( " Out of manifests " ) ;
break ;
}
2013-08-01 02:07:35 +00:00
const unsigned char * q_manifestid = sqlite3_column_text ( statement , 0 ) ;
const char * manifestblob = ( char * ) sqlite3_column_blob ( statement , 1 ) ;
size_t manifestblobsize = sqlite3_column_bytes ( statement , 1 ) ; // must call after sqlite3_column_blob()
2013-12-19 08:37:14 +00:00
memcpy ( blob_m - > manifestdata , manifestblob , manifestblobsize ) ;
blob_m - > manifest_all_bytes = manifestblobsize ;
if ( rhizome_manifest_parse ( blob_m ) = = - 1
2013-11-28 07:14:37 +00:00
| | ! rhizome_manifest_validate ( blob_m )
2013-12-19 08:43:39 +00:00
) {
2012-08-23 08:13:35 +00:00
WARNF ( " MANIFESTS row id=%s has invalid manifest blob -- skipped " , q_manifestid ) ;
2013-08-01 02:07:35 +00:00
goto next ;
}
2013-11-28 07:14:37 +00:00
if ( ! rhizome_manifest_verify ( blob_m ) ) {
2012-08-23 08:13:35 +00:00
WARNF ( " MANIFESTS row id=%s fails verification -- skipped " , q_manifestid ) ;
2013-08-01 02:07:35 +00:00
goto next ;
}
const char * q_author = ( const char * ) sqlite3_column_text ( statement , 2 ) ;
if ( q_author ) {
Refactor manifest: specific setter functions
Replace generic rhizome_manifest_set() and rhizome_manifest_set_ll()
with per-field setter functions, eg, rhizome_manifest_set_filesize().
Struct rhizome_manifest elements for all known fields, to replace the
use of rhizome_manifest_get() and rhizome_manifest_get_ll() everywhere:
sender, recipient, service, name, date, bundle_key.
Add boolean validity flags for binary blob types, to avoid having to compare
with many bytes of all-zero to detect presence, eg, has_sender, has_recipient,
has_author, has_bundle_key. These maintained by the setter functions.
Rename existing manifest struct elements to be the same as their field
names: fileLength -> filesize, journalTail -> tail.
More use of unsigned int, size_t and uint64_t for payload sizes, offsets, byte
counts, etc. especially in rhizome_store.c and meshms.c. More uniform use of
size_t to dimension memory buffers. Fix some printf(3) style format strings
for 64-bit correctness on 32-bit systems. Use new constant RHIZOME_SIZE_UNSET
instead of -1 to indicate unknown dimension, and explicitly assert its absence
before comparisons and arithmetic, for safety.
Replace some 'int' loop variables with 'unsigned' where appropriate.
Fix bugs discovered in MeshMS bundle private/public key generation and
bundle secret key handling for export/extract commands.
Instrument the first MeshMS test case to aid debugging.
New debug config flag: debug.manifest logs all modifications to all manifest
fields by setter functions.
Rename debug config flag: debug.rhizome_bind -> debug.rhizome_sql_bind.
2013-10-30 12:52:19 +00:00
sid_t author ;
if ( str_to_sid_t ( & author , q_author ) = = - 1 )
WARNF ( " MANIFESTS row id=%s has invalid author=%s -- ignored " , q_manifestid , alloca_str_toprint ( q_author ) ) ;
else
rhizome_manifest_set_author ( blob_m , & author ) ;
2013-08-01 02:07:35 +00:00
}
// check that we can re-author this manifest
2013-11-05 07:28:03 +00:00
rhizome_authenticate_author ( blob_m ) ;
if ( m - > authorship ! = AUTHOR_AUTHENTIC )
2013-08-01 02:07:35 +00:00
goto next ;
* found = blob_m ;
if ( config . debug . rhizome )
DEBUGF ( " Found duplicate payload, %s " , q_manifestid ) ;
2013-12-19 08:43:39 +00:00
ret = RHIZOME_BUNDLE_STATUS_DUPLICATE ;
2013-08-01 02:07:35 +00:00
break ;
next :
2012-08-23 08:13:35 +00:00
if ( blob_m )
rhizome_manifest_free ( blob_m ) ;
2012-04-12 09:00:52 +00:00
}
sqlite3_finalize ( statement ) ;
return ret ;
}
2012-05-02 06:33:09 +00:00
2013-10-03 13:46:45 +00:00
static int unpack_manifest_row ( rhizome_manifest * m , sqlite3_stmt * statement )
{
const char * q_id = ( const char * ) sqlite3_column_text ( statement , 0 ) ;
const char * q_blob = ( char * ) sqlite3_column_blob ( statement , 1 ) ;
2013-12-11 00:41:34 +00:00
uint64_t q_version = sqlite3_column_int64 ( statement , 2 ) ;
2013-10-03 13:46:45 +00:00
int64_t q_inserttime = sqlite3_column_int64 ( statement , 3 ) ;
const char * q_author = ( const char * ) sqlite3_column_text ( statement , 4 ) ;
size_t q_blobsize = sqlite3_column_bytes ( statement , 1 ) ; // must call after sqlite3_column_blob()
2013-11-11 07:43:38 +00:00
uint64_t q_rowid = sqlite3_column_int64 ( statement , 5 ) ;
2013-12-19 08:37:14 +00:00
memcpy ( m - > manifestdata , q_blob , q_blobsize ) ;
m - > manifest_all_bytes = q_blobsize ;
if ( rhizome_manifest_parse ( m ) = = - 1 | | ! rhizome_manifest_validate ( m ) )
2013-11-28 07:14:37 +00:00
return WHYF ( " Manifest bid=%s in database but invalid " , q_id ) ;
2013-10-03 13:46:45 +00:00
if ( q_author ) {
Refactor manifest: specific setter functions
Replace generic rhizome_manifest_set() and rhizome_manifest_set_ll()
with per-field setter functions, eg, rhizome_manifest_set_filesize().
Struct rhizome_manifest elements for all known fields, to replace the
use of rhizome_manifest_get() and rhizome_manifest_get_ll() everywhere:
sender, recipient, service, name, date, bundle_key.
Add boolean validity flags for binary blob types, to avoid having to compare
with many bytes of all-zero to detect presence, eg, has_sender, has_recipient,
has_author, has_bundle_key. These maintained by the setter functions.
Rename existing manifest struct elements to be the same as their field
names: fileLength -> filesize, journalTail -> tail.
More use of unsigned int, size_t and uint64_t for payload sizes, offsets, byte
counts, etc. especially in rhizome_store.c and meshms.c. More uniform use of
size_t to dimension memory buffers. Fix some printf(3) style format strings
for 64-bit correctness on 32-bit systems. Use new constant RHIZOME_SIZE_UNSET
instead of -1 to indicate unknown dimension, and explicitly assert its absence
before comparisons and arithmetic, for safety.
Replace some 'int' loop variables with 'unsigned' where appropriate.
Fix bugs discovered in MeshMS bundle private/public key generation and
bundle secret key handling for export/extract commands.
Instrument the first MeshMS test case to aid debugging.
New debug config flag: debug.manifest logs all modifications to all manifest
fields by setter functions.
Rename debug config flag: debug.rhizome_bind -> debug.rhizome_sql_bind.
2013-10-30 12:52:19 +00:00
sid_t author ;
if ( str_to_sid_t ( & author , q_author ) = = - 1 )
WARNF ( " MANIFESTS row id=%s has invalid author=%s -- ignored " , q_id , alloca_str_toprint ( q_author ) ) ;
else
rhizome_manifest_set_author ( m , & author ) ;
2013-10-03 13:46:45 +00:00
}
if ( m - > version ! = q_version )
2013-12-11 00:41:34 +00:00
WARNF ( " Version mismatch, manifest is % " PRIu64 " , database is % " PRIu64 , m - > version , q_version ) ;
2013-11-11 07:43:38 +00:00
rhizome_manifest_set_rowid ( m , q_rowid ) ;
2013-11-05 07:28:03 +00:00
rhizome_manifest_set_inserttime ( m , q_inserttime ) ;
2013-10-03 13:46:45 +00:00
return 0 ;
}
/* Retrieve a manifest from the database, given its Bundle ID.
2012-05-02 08:27:35 +00:00
*
2013-01-02 00:42:15 +00:00
* Returns 0 if manifest is found
* Returns 1 if manifest is not found
* Returns - 1 on error
* Caller is responsible for allocating and freeing rhizome_manifest
2012-05-02 06:33:09 +00:00
*/
2013-10-03 17:42:52 +00:00
int rhizome_retrieve_manifest ( const rhizome_bid_t * bidp , rhizome_manifest * m )
2013-02-20 04:14:29 +00:00
{
2012-10-04 05:00:20 +00:00
sqlite_retry_state retry = SQLITE_RETRY_STATE_DEFAULT ;
2013-10-03 07:14:06 +00:00
sqlite3_stmt * statement = sqlite_prepare_bind ( & retry ,
2013-11-11 07:43:38 +00:00
" SELECT id, manifest, version, inserttime, author, rowid FROM manifests WHERE id = ? " ,
2013-10-03 17:42:52 +00:00
RHIZOME_BID_T , bidp ,
2013-10-03 07:14:06 +00:00
END ) ;
2012-08-23 08:13:35 +00:00
if ( ! statement )
return - 1 ;
2013-10-03 13:46:45 +00:00
int ret = 1 ;
if ( sqlite_step_retry ( & retry , statement ) = = SQLITE_ROW )
ret = unpack_manifest_row ( m , statement ) ;
else
2013-10-03 17:42:52 +00:00
INFOF ( " Manifest id=%s not found " , alloca_tohex_rhizome_bid_t ( * bidp ) ) ;
2013-10-03 13:46:45 +00:00
sqlite3_finalize ( statement ) ;
return ret ;
}
2013-01-02 00:42:15 +00:00
2013-10-03 13:46:45 +00:00
/* Retrieve any manifest from the database whose Bundle ID starts with the given prefix.
*
* Returns 0 if a manifest is found
* Returns 1 if no manifest is found
* Returns - 1 on error
* Caller is responsible for allocating and freeing rhizome_manifest
*/
int rhizome_retrieve_manifest_by_prefix ( const unsigned char * prefix , unsigned prefix_len , rhizome_manifest * m )
{
sqlite_retry_state retry = SQLITE_RETRY_STATE_DEFAULT ;
2013-10-09 08:24:21 +00:00
const unsigned prefix_strlen = prefix_len * 2 ;
char like [ prefix_strlen + 2 ] ;
tohex ( like , prefix_strlen , prefix ) ;
like [ prefix_strlen ] = ' % ' ;
like [ prefix_strlen + 1 ] = ' \0 ' ;
2013-10-03 13:46:45 +00:00
sqlite3_stmt * statement = sqlite_prepare_bind ( & retry ,
2013-11-11 07:43:38 +00:00
" SELECT id, manifest, version, inserttime, author, rowid FROM manifests WHERE id like ? " ,
2013-10-03 13:46:45 +00:00
TEXT , like ,
END ) ;
if ( ! statement )
return - 1 ;
int ret = 1 ;
if ( sqlite_step_retry ( & retry , statement ) = = SQLITE_ROW )
ret = unpack_manifest_row ( m , statement ) ;
else
INFOF ( " Manifest with id prefix=`%s` not found " , like ) ;
2012-05-02 06:33:09 +00:00
sqlite3_finalize ( statement ) ;
2013-10-03 13:46:45 +00:00
return ret ;
2012-05-02 06:33:09 +00:00
}
2013-02-12 00:04:04 +00:00
2013-10-03 13:46:45 +00:00
static int rhizome_delete_manifest_retry ( sqlite_retry_state * retry , const rhizome_bid_t * bidp )
2013-02-20 04:14:29 +00:00
{
2013-10-03 07:14:06 +00:00
sqlite3_stmt * statement = sqlite_prepare_bind ( retry ,
" DELETE FROM manifests WHERE id = ? " ,
2013-10-03 13:46:45 +00:00
RHIZOME_BID_T , bidp ,
2013-10-03 07:14:06 +00:00
END ) ;
2013-02-20 04:14:29 +00:00
if ( ! statement )
return - 1 ;
2013-10-03 05:45:30 +00:00
if ( _sqlite_exec ( __WHENCE__ , LOG_LEVEL_ERROR , retry , statement ) = = - 1 )
2013-02-20 04:14:29 +00:00
return - 1 ;
return sqlite3_changes ( rhizome_db ) ? 0 : 1 ;
}
2013-10-10 07:53:06 +00:00
static int rhizome_delete_file_retry ( sqlite_retry_state * retry , const rhizome_filehash_t * hashp )
2013-02-20 04:14:29 +00:00
{
int ret = 0 ;
2013-10-10 07:53:06 +00:00
rhizome_delete_external ( hashp ) ;
sqlite3_stmt * statement = sqlite_prepare_bind ( retry , " DELETE FROM files WHERE id = ? " , RHIZOME_FILEHASH_T , hashp , END ) ;
2013-10-03 07:14:06 +00:00
if ( ! statement | | sqlite_exec_retry ( retry , statement ) = = - 1 )
2013-02-20 04:14:29 +00:00
ret = - 1 ;
2013-10-10 07:53:06 +00:00
statement = sqlite_prepare_bind ( retry , " DELETE FROM fileblobs WHERE id = ? " , RHIZOME_FILEHASH_T , hashp , END ) ;
2013-10-03 07:14:06 +00:00
if ( ! statement | | sqlite_exec_retry ( retry , statement ) = = - 1 )
2013-02-20 04:14:29 +00:00
ret = - 1 ;
return ret = = - 1 ? - 1 : sqlite3_changes ( rhizome_db ) ? 0 : 1 ;
}
2013-10-03 13:46:45 +00:00
static int rhizome_delete_payload_retry ( sqlite_retry_state * retry , const rhizome_bid_t * bidp )
2013-02-20 04:14:29 +00:00
{
strbuf fh = strbuf_alloca ( RHIZOME_FILEHASH_STRLEN + 1 ) ;
2013-10-03 13:46:45 +00:00
int rows = sqlite_exec_strbuf_retry ( retry , fh , " SELECT filehash FROM manifests WHERE id = ? " , RHIZOME_BID_T , bidp , END ) ;
2013-02-20 04:14:29 +00:00
if ( rows = = - 1 )
return - 1 ;
2013-10-10 07:53:06 +00:00
rhizome_filehash_t hash ;
if ( str_to_rhizome_filehash_t ( & hash , strbuf_str ( fh ) ) = = - 1 )
return WHYF ( " invalid field FILES.id=%s " , strbuf_str ( fh ) ) ;
if ( rows & & rhizome_delete_file_retry ( retry , & hash ) = = - 1 )
2013-02-20 04:14:29 +00:00
return - 1 ;
return 0 ;
}
2013-09-30 06:45:59 +00:00
2013-02-20 04:14:29 +00:00
/* Remove a manifest and its bundle from the database, given its manifest ID.
*
* Returns 0 if manifest is found and removed and bundle was either absent or removed
* Returns 1 if manifest is not found
* Returns - 1 on error
*
* @ author Andrew Bettison < andrew @ servalproject . com >
*/
2013-10-03 13:46:45 +00:00
int rhizome_delete_bundle ( const rhizome_bid_t * bidp )
2013-02-20 04:14:29 +00:00
{
sqlite_retry_state retry = SQLITE_RETRY_STATE_DEFAULT ;
2013-10-03 13:46:45 +00:00
if ( rhizome_delete_payload_retry ( & retry , bidp ) = = - 1 )
2013-02-20 04:14:29 +00:00
return - 1 ;
2013-10-03 13:46:45 +00:00
if ( rhizome_delete_manifest_retry ( & retry , bidp ) = = - 1 )
2013-02-20 04:14:29 +00:00
return - 1 ;
return 0 ;
}
/* Remove a manifest from the database, given its manifest ID, leaving its bundle (fileblob)
* untouched if present .
*
* Returns 0 if manifest is found and removed
* Returns 1 if manifest is not found
* Returns - 1 on error
*
* @ author Andrew Bettison < andrew @ servalproject . com >
*/
2013-10-03 13:46:45 +00:00
int rhizome_delete_manifest ( const rhizome_bid_t * bidp )
2013-02-20 04:14:29 +00:00
{
sqlite_retry_state retry = SQLITE_RETRY_STATE_DEFAULT ;
2013-10-03 13:46:45 +00:00
return rhizome_delete_manifest_retry ( & retry , bidp ) ;
2013-02-20 04:14:29 +00:00
}
/* Remove a bundle's payload (file) from the database, given its manifest ID, leaving its manifest
* untouched if present .
*
* Returns 0 if manifest is found , its payload is found and removed
* Returns 1 if manifest or payload is not found
* Returns - 1 on error
*
* @ author Andrew Bettison < andrew @ servalproject . com >
*/
2013-10-03 13:46:45 +00:00
int rhizome_delete_payload ( const rhizome_bid_t * bidp )
2013-02-20 04:14:29 +00:00
{
sqlite_retry_state retry = SQLITE_RETRY_STATE_DEFAULT ;
2013-10-03 13:46:45 +00:00
return rhizome_delete_payload_retry ( & retry , bidp ) ;
2013-02-20 04:14:29 +00:00
}
/* Remove a file from the database, given its file hash.
*
* Returns 0 if file is found and removed
* Returns 1 if file is not found
* Returns - 1 on error
*
* @ author Andrew Bettison < andrew @ servalproject . com >
*/
2013-10-10 07:53:06 +00:00
int rhizome_delete_file ( const rhizome_filehash_t * hashp )
2013-02-20 04:14:29 +00:00
{
sqlite_retry_state retry = SQLITE_RETRY_STATE_DEFAULT ;
2013-10-10 07:53:06 +00:00
return rhizome_delete_file_retry ( & retry , hashp ) ;
2013-02-20 04:14:29 +00:00
}
2013-02-20 06:52:53 +00:00
2013-12-11 00:41:34 +00:00
static int is_interesting ( const char * id_hex , uint64_t version )
2013-08-22 05:44:21 +00:00
{
2013-02-15 06:37:45 +00:00
IN ( ) ;
2013-02-12 00:04:04 +00:00
int ret = 1 ;
2013-04-11 05:54:13 +00:00
2013-02-14 22:42:01 +00:00
// do we have this bundle [or later]?
2013-02-12 00:04:04 +00:00
sqlite_retry_state retry = SQLITE_RETRY_STATE_DEFAULT ;
2013-10-03 19:20:37 +00:00
sqlite3_stmt * statement = sqlite_prepare_bind ( & retry ,
2013-10-10 07:53:06 +00:00
" SELECT filehash FROM MANIFESTS WHERE id LIKE ? AND version >= ? " ,
2013-10-03 07:14:06 +00:00
TEXT_TOUPPER , id_hex ,
INT64 , version ,
END ) ;
2013-06-18 03:57:26 +00:00
if ( ! statement )
RETURN ( - 1 ) ;
2013-02-12 00:04:04 +00:00
if ( sqlite_step_retry ( & retry , statement ) = = SQLITE_ROW ) {
2013-08-22 05:44:21 +00:00
const char * q_filehash = ( const char * ) sqlite3_column_text ( statement , 0 ) ;
2013-02-12 00:04:04 +00:00
ret = 0 ;
2013-10-10 07:53:06 +00:00
if ( q_filehash & & * q_filehash ) {
rhizome_filehash_t hash ;
if ( str_to_rhizome_filehash_t ( & hash , q_filehash ) = = - 1 ) {
WARNF ( " invalid field MANIFESTS.filehash=%s -- ignored " , alloca_str_toprint ( q_filehash ) ) ;
ret = 1 ;
} else if ( ! rhizome_exists ( & hash ) )
ret = 1 ;
}
2013-08-28 06:45:28 +00:00
}
2013-02-12 00:04:04 +00:00
sqlite3_finalize ( statement ) ;
2013-02-15 06:37:45 +00:00
RETURN ( ret ) ;
2013-02-16 17:47:24 +00:00
OUT ( ) ;
2013-04-04 07:07:49 +00:00
}
2013-08-22 05:44:21 +00:00
2014-01-09 01:57:48 +00:00
int rhizome_is_bar_interesting ( const unsigned char * bar )
2013-08-22 05:44:21 +00:00
{
2013-12-11 00:41:34 +00:00
uint64_t version = rhizome_bar_version ( bar ) ;
2013-11-21 05:59:10 +00:00
char id_hex [ RHIZOME_BAR_PREFIX_BYTES * 2 + 2 ] ;
2013-10-09 08:24:21 +00:00
tohex ( id_hex , RHIZOME_BAR_PREFIX_BYTES * 2 , & bar [ RHIZOME_BAR_PREFIX_OFFSET ] ) ;
2013-08-22 05:44:21 +00:00
strcat ( id_hex , " % " ) ;
return is_interesting ( id_hex , version ) ;
}
int rhizome_is_manifest_interesting ( rhizome_manifest * m )
{
Refactor manifest: specific setter functions
Replace generic rhizome_manifest_set() and rhizome_manifest_set_ll()
with per-field setter functions, eg, rhizome_manifest_set_filesize().
Struct rhizome_manifest elements for all known fields, to replace the
use of rhizome_manifest_get() and rhizome_manifest_get_ll() everywhere:
sender, recipient, service, name, date, bundle_key.
Add boolean validity flags for binary blob types, to avoid having to compare
with many bytes of all-zero to detect presence, eg, has_sender, has_recipient,
has_author, has_bundle_key. These maintained by the setter functions.
Rename existing manifest struct elements to be the same as their field
names: fileLength -> filesize, journalTail -> tail.
More use of unsigned int, size_t and uint64_t for payload sizes, offsets, byte
counts, etc. especially in rhizome_store.c and meshms.c. More uniform use of
size_t to dimension memory buffers. Fix some printf(3) style format strings
for 64-bit correctness on 32-bit systems. Use new constant RHIZOME_SIZE_UNSET
instead of -1 to indicate unknown dimension, and explicitly assert its absence
before comparisons and arithmetic, for safety.
Replace some 'int' loop variables with 'unsigned' where appropriate.
Fix bugs discovered in MeshMS bundle private/public key generation and
bundle secret key handling for export/extract commands.
Instrument the first MeshMS test case to aid debugging.
New debug config flag: debug.manifest logs all modifications to all manifest
fields by setter functions.
Rename debug config flag: debug.rhizome_bind -> debug.rhizome_sql_bind.
2013-10-30 12:52:19 +00:00
return is_interesting ( alloca_tohex_rhizome_bid_t ( m - > cryptoSignPublic ) , m - > version ) ;
2013-09-30 06:50:50 +00:00
}