2012-01-12 03:38:24 +00:00
/*
2012-05-19 04:39:50 +00:00
Serval Rhizome file sharing
2012-08-23 08:13:35 +00:00
Copyright ( C ) 2012 The Serval Project , Inc .
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"
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-08-21 06:15:18 +00:00
static int rhizome_delete_file_retry ( sqlite_retry_state * retry , const char * fileid ) ;
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
}
2012-01-12 03:38:24 +00:00
sqlite3 * rhizome_db = NULL ;
/* 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-07-13 05:17:06 +00:00
int64_t result = 0 ;
2012-08-23 08:13:35 +00:00
if ( sqlite_exec_int64_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 ;
2012-10-10 02:52:30 +00:00
2012-10-09 07:13:34 +00:00
static void sqlite_trace_callback ( void * context , const char * rendered_sql )
{
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 ) ;
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-10-02 08:49:20 +00:00
void sqlite_log ( void * ignored , int result , const char * msg )
{
2013-01-06 03:48:36 +00:00
WARNF ( " Sqlite: %d %s " , result , msg ) ;
}
2013-06-14 02:28:19 +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; " ) ;
while ( sqlite_step_retry ( & retry , statement ) = = SQLITE_ROW ) {
sqlite3_int64 rowid = sqlite3_column_int64 ( statement , 0 ) ;
const void * manifest = sqlite3_column_blob ( statement , 1 ) ;
int manifest_length = sqlite3_column_bytes ( statement , 1 ) ;
rhizome_manifest * m = rhizome_new_manifest ( ) ;
int ret = 0 ;
ret = rhizome_read_manifest_file ( m , manifest , manifest_length ) ;
if ( ret = = 0 & & m - > errors )
ret = - 1 ;
if ( ret = = 0 )
ret = rhizome_manifest_verify ( m ) ;
if ( ret = = 0 ) {
m - > finalised = 1 ;
m - > manifest_bytes = m - > manifest_all_bytes ;
// store it again, to ensure it is valid and stored correctly with matching file content.
ret = rhizome_store_bundle ( m ) ;
}
if ( ret ! = 0 ) {
DEBUGF ( " Removing invalid manifest entry @%lld " , rowid ) ;
2013-10-02 15:46:10 +00:00
sqlite_exec_void_retry ( & retry , " DELETE FROM MANIFESTS WHERE ROWID = ?; " , INT64 , rowid , END ) ;
2013-01-16 00:26:09 +00:00
}
rhizome_manifest_free ( m ) ;
}
sqlite3_finalize ( statement ) ;
}
2012-10-15 07:38:31 +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
2013-10-03 05:45:30 +00:00
* locally by a non - secret identity , so no verification need be performed for one ' s own
2012-10-15 07:38:31 +00:00
* bundles while they remain in the 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 an author is
* found , 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 .
* - 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 ( )
{
if ( rhizome_db ) return 0 ;
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 ) ;
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 */
2012-12-11 05:29:46 +00:00
if ( config . debug . rhizome ) {
2012-12-04 03:42:28 +00:00
DEBUGF ( " Rhizome will use %lluB of storage for its database. " , ( unsigned long long ) config . rhizome . database_size ) ;
2012-05-18 09:23:27 +00:00
}
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-07-13 05:17:06 +00:00
int64_t version ;
2013-10-02 15:46:10 +00:00
if ( sqlite_exec_int64_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 " ) ) ;
}
2012-10-03 15:54:39 +00:00
2013-01-06 02:34:49 +00:00
/* 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 ) ;
2013-01-06 02:34:49 +00:00
2013-10-02 15:46:10 +00:00
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-01-17 01:13:31 +00:00
// TODO recreate tables with collate nocase on hex columns
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 */
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 ) ;
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-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-02 08:49:20 +00:00
enum sqlbind_type typ ;
2013-10-02 15:46:10 +00:00
int index_counter = 0 ;
2013-10-03 05:45:30 +00:00
strbuf ext = NULL ;
2013-10-02 08:49:20 +00:00
do {
typ = va_arg ( ap , int ) ;
int index ;
2013-10-03 05:45:30 +00:00
const char * name = NULL ;
2013-10-02 15:46:10 +00:00
if ( ( typ & 0xffff0000 ) = = INDEX ) {
typ & = 0xffff ;
index = va_arg ( ap , int ) ;
if ( index < 1 | | index > index_limit ) {
LOGF ( log_level , " illegal index %d: %s " , index , sqlite3_sql ( statement ) ) ;
return - 1 ;
}
2013-10-03 05:45:30 +00:00
if ( config . debug . rhizome )
strbuf_sprintf ( ( ext = strbuf_alloca ( 25 ) ) , " |INDEX 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-02 08:49:20 +00:00
index = sqlite3_bind_parameter_index ( statement , name ) ;
if ( index = = 0 ) {
LOGF ( log_level , " no parameter %s in query: %s " , 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 ) {
ext = strbuf_alloca ( 20 + toprint_str_len ( name , " \" \" " ) ) ;
strbuf_puts ( ext , " |NAMED name= " ) ;
strbuf_toprint_quoted ( ext , " \" \" " , name ) ;
}
} else {
2013-10-02 15:46:10 +00:00
index = + + index_counter ;
2013-10-03 05:45:30 +00:00
if ( config . debug . rhizome )
ext = strbuf_alloca ( 1 ) ;
}
# define BIND_DEBUG(TYP,FUNC,ARGFMT,...) \
2013-10-03 18:16:52 +00:00
if ( config . debug . rhizome_bind ) \
2013-10-03 05:45:30 +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-02 08:49:20 +00:00
switch ( typ ) {
2013-10-03 05:45:30 +00:00
case END :
break ;
2013-10-02 08:49:20 +00:00
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-02 15:46:10 +00:00
case INT : {
int value = va_arg ( ap , int ) ;
2013-10-03 05:45:30 +00:00
BIND_DEBUG ( INT , sqlite3_bind_int , " %d " , value ) ;
2013-10-02 15:46:10 +00:00
BIND_RETRY ( sqlite3_bind_int , value ) ;
}
2013-10-02 08:49:20 +00:00
break ;
2013-10-03 05:45:30 +00:00
case INT_TOSTR : {
int value = va_arg ( ap , int ) ;
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 ) ;
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 ;
2013-10-02 15:46:10 +00:00
case INT64 : {
2013-10-03 17:42:52 +00:00
sqlite3_int64 value = va_arg ( ap , int64_t ) ;
BIND_DEBUG ( INT64 , sqlite3_bind_int64 , " % " PRId64 , ( int64_t ) value ) ;
2013-10-02 15:46:10 +00:00
BIND_RETRY ( sqlite3_bind_int64 , value ) ;
}
2013-10-02 08:49:20 +00:00
break ;
2013-10-03 05:45:30 +00:00
case INT64_TOSTR : {
int64_t value = va_arg ( ap , int64_t ) ;
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 ) ;
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 ;
2013-10-03 13:46:02 +00:00
case TEXT : {
const char * text = va_arg ( ap , const char * ) ;
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 ) ;
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 ;
2013-10-02 15:46:10 +00:00
case STATIC_TEXT : {
const char * text = va_arg ( ap , const char * ) ;
2013-10-03 05:45:30 +00:00
BIND_DEBUG ( STATIC_TEXT , sqlite3_bind_text , " %s,-1,SQLITE_STATIC " , alloca_str_toprint ( text ) ) ;
2013-10-02 15:46:10 +00:00
BIND_RETRY ( sqlite3_bind_text , text , - 1 , SQLITE_STATIC ) ;
}
2013-10-02 08:49:20 +00:00
break ;
case STATIC_TEXT_LEN : {
const char * text = va_arg ( ap , const char * ) ;
int bytes = va_arg ( ap , int ) ;
2013-10-03 05:45:30 +00:00
BIND_DEBUG ( STATIC_TEXT_LEN , sqlite3_bind_text , " %s,%d,SQLITE_STATIC " , alloca_str_toprint ( text ) , bytes ) ;
2013-10-02 15:46:10 +00:00
BIND_RETRY ( sqlite3_bind_text , text , bytes , SQLITE_STATIC ) ;
2013-10-02 08:49:20 +00:00
}
break ;
case STATIC_BLOB : {
const void * blob = va_arg ( ap , const void * ) ;
int bytes = va_arg ( ap , int ) ;
2013-10-03 05:45:30 +00:00
BIND_DEBUG ( STATIC_BLOB , sqlite3_bind_blob , " %s,%d,SQLITE_STATIC " , alloca_toprint ( 20 , blob , bytes ) , bytes ) ;
2013-10-02 15:46:10 +00:00
BIND_RETRY ( sqlite3_bind_blob , blob , bytes , SQLITE_STATIC ) ;
2013-10-02 08:49:20 +00:00
} ;
break ;
2013-10-03 05:45:30 +00:00
case ZEROBLOB : {
int bytes = va_arg ( ap , int ) ;
BIND_DEBUG ( ZEROBLOB , sqlite3_bind_zeroblob , " %d,SQLITE_STATIC " , bytes ) ;
BIND_RETRY ( sqlite3_bind_zeroblob , bytes ) ;
} ;
break ;
2013-10-02 08:49:20 +00:00
case SID_T : {
const sid_t * sidp = va_arg ( ap , const sid_t * ) ;
const char * sid_hex = alloca_tohex_sid_t ( * sidp ) ;
2013-10-03 05:45:30 +00:00
BIND_DEBUG ( SID_T , sqlite3_bind_text , " %s,%d,SQLITE_TRANSIENT " , sid_hex , SID_STRLEN ) ;
2013-10-02 15:46:10 +00:00
BIND_RETRY ( sqlite3_bind_text , sid_hex , SID_STRLEN , SQLITE_TRANSIENT ) ;
2013-10-02 08:49:20 +00:00
}
break ;
2013-10-03 13:46:45 +00:00
case RHIZOME_BID_T : {
const rhizome_bid_t * bidp = va_arg ( ap , const rhizome_bid_t * ) ;
const char * bid_hex = alloca_tohex_rhizome_bid_t ( * bidp ) ;
BIND_DEBUG ( RHIZOME_BID_T , sqlite3_bind_text , " %s,%d,SQLITE_TRANSIENT " , bid_hex , RHIZOME_MANIFEST_ID_STRLEN ) ;
2013-10-02 15:46:10 +00:00
BIND_RETRY ( sqlite3_bind_text , bid_hex , RHIZOME_MANIFEST_ID_STRLEN , SQLITE_TRANSIENT ) ;
2013-10-02 08:49:20 +00:00
}
break ;
case FILEHASH_T : {
const char * hash_hex = alloca_tohex ( va_arg ( ap , const unsigned char * ) , RHIZOME_FILEHASH_BYTES ) ;
2013-10-03 05:45:30 +00:00
BIND_DEBUG ( FILEHASH_T , sqlite3_bind_text , " %s,%d,SQLITE_TRANSIENT " , hash_hex , RHIZOME_FILEHASH_STRLEN ) ;
2013-10-02 15:46:10 +00:00
BIND_RETRY ( sqlite3_bind_text , hash_hex , RHIZOME_FILEHASH_STRLEN , SQLITE_TRANSIENT ) ;
2013-10-02 08:49:20 +00:00
}
break ;
case TOHEX : {
const unsigned char * binary = va_arg ( ap , const unsigned char * ) ;
unsigned bytes = va_arg ( ap , unsigned ) ;
2013-10-03 05:45:30 +00:00
const char * hex = alloca_tohex ( binary , bytes ) ;
BIND_DEBUG ( TOHEX , sqlite3_bind_text , " %s,%d,SQLITE_TRANSIENT " , hex , bytes * 2 ) ;
2013-10-02 15:46:10 +00:00
BIND_RETRY ( sqlite3_bind_text , hex , bytes * 2 , SQLITE_TRANSIENT ) ;
2013-10-02 08:49:20 +00:00
}
break ;
case TEXT_TOUPPER : {
const char * text = va_arg ( ap , const char * ) ;
unsigned bytes = strlen ( text ) ;
2013-10-03 05:45:30 +00:00
char upper [ bytes + 1 ] ;
str_toupper_inplace ( strcpy ( upper , text ) ) ;
BIND_DEBUG ( TEXT_TOUPPER , sqlite3_bind_text , " %s,%d,SQLITE_TRANSIENT " , alloca_toprint ( - 1 , upper , bytes ) , bytes ) ;
2013-10-02 15:46:10 +00:00
BIND_RETRY ( sqlite3_bind_text , upper , bytes , SQLITE_TRANSIENT ) ;
2013-10-02 08:49:20 +00:00
}
break ;
case TEXT_LEN_TOUPPER : {
const char * text = va_arg ( ap , const char * ) ;
unsigned bytes = va_arg ( ap , unsigned ) ;
char upper [ bytes ] ;
unsigned i ;
for ( i = 0 ; i ! = bytes ; + + i )
upper [ i ] = toupper ( text [ i ] ) ;
2013-10-03 05:45:30 +00:00
BIND_DEBUG ( TEXT_LEN_TOUPPER , sqlite3_bind_text , " %s,%d,SQLITE_TRANSIENT " , alloca_toprint ( - 1 , upper , bytes ) , bytes ) ;
2013-10-02 15:46:10 +00:00
BIND_RETRY ( sqlite3_bind_text , upper , bytes , SQLITE_TRANSIENT ) ;
2013-10-02 08:49:20 +00:00
}
break ;
2013-10-03 05:45:30 +00:00
# undef BIND_RETRY
2013-10-02 08:49:20 +00:00
default :
2013-10-03 05:45:30 +00:00
FATALF ( " unsupported bind code, index=%d typ=0x%08x: %s " , index , typ , sqlite3_sql ( statement ) ) ;
2013-10-02 08:49:20 +00:00
}
} while ( typ ! = END ) ;
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-10-02 15:46:10 +00:00
static int _sqlite_vexec_int64 ( struct __sourceloc __whence , sqlite_retry_state * retry , int64_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-09-30 06:46:44 +00:00
DEBUGF ( " rowcount=%d changes=%d result=%lld " , rowcount , sqlite3_changes ( rhizome_db ) , ( long long ) * 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-07-13 05:17:06 +00:00
int _sqlite_exec_int64 ( struct __sourceloc __whence , int64_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 ;
2012-10-16 06:16:52 +00:00
int ret = _sqlite_vexec_int64 ( __whence , & retry , result , sqlformat , ap ) ;
2012-08-22 09:39:30 +00:00
va_end ( ap ) ;
return ret ;
}
/* Same as sqlite_exec_int64() 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-07-13 05:17:06 +00:00
int _sqlite_exec_int64_retry ( struct __sourceloc __whence , sqlite_retry_state * retry , int64_t * result , const char * sqlformat , . . . )
2012-08-22 09:39:30 +00:00
{
va_list ap ;
va_start ( ap , sqlformat ) ;
2012-10-16 06:16:52 +00:00
int ret = _sqlite_vexec_int64 ( __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-07-13 05:17:06 +00:00
int64_t rhizome_database_used_bytes ( )
2012-01-12 03:38:24 +00:00
{
2013-07-13 05:17:06 +00:00
int64_t db_page_size ;
int64_t db_page_count ;
int64_t db_free_page_count ;
2013-10-02 15:46:10 +00:00
if ( sqlite_exec_int64 ( & db_page_size , " PRAGMA page_size; " , END ) = = - 1LL
| | sqlite_exec_int64 ( & db_page_count , " PRAGMA page_count; " , END ) = = - 1LL
| | sqlite_exec_int64 ( & db_free_page_count , " PRAGMA free_count; " , END ) = = - 1LL
2012-06-12 08:42:36 +00:00
)
return WHY ( " Cannot measure database used bytes " ) ;
return db_page_size * ( db_page_count - db_free_page_count ) ;
2012-01-12 03:38:24 +00:00
}
2013-10-03 19:20:37 +00:00
int rhizome_database_filehash_from_id ( const rhizome_bid_t * bidp , uint64_t version , char hash [ SHA512_DIGEST_STRING_LENGTH ] )
2013-02-16 09:55:26 +00:00
{
IN ( ) ;
2013-02-20 03:59:08 +00:00
strbuf hash_sb = strbuf_local ( hash , SHA512_DIGEST_STRING_LENGTH ) ;
2013-10-03 19:20:37 +00:00
RETURN ( sqlite_exec_strbuf ( hash_sb , " SELECT filehash FROM MANIFESTS WHERE version = ? AND id = ?; " ,
INT64 , version , RHIZOME_BID_T , bidp , END ) ) ;
2013-02-16 17:47:24 +00:00
OUT ( ) ;
2013-02-16 09:55:26 +00:00
}
2013-08-21 06:15:18 +00:00
static int rhizome_delete_external ( const char * fileid )
{
// attempt to remove any external blob
char blob_path [ 1024 ] ;
if ( ! FORM_RHIZOME_DATASTORE_PATH ( blob_path , fileid ) )
return - 1 ;
return unlink ( blob_path ) ;
}
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-09-30 07:02:08 +00:00
int rhizome_remove_file_datainvalid ( sqlite_retry_state * retry , const char * fileid )
{
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; " ,
TEXT_TOUPPER , fileid , END
) = = - 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 ); " ,
TEXT_TOUPPER , fileid , END
) = = - 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-09-30 06:50:50 +00:00
if ( rhizome_delete_external ( id ) = = 0 & & report )
2013-10-06 17:12:17 +00:00
+ + 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-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-09-30 06:50:50 +00:00
if ( rhizome_delete_external ( id ) = = 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
}
2012-01-12 03:38:24 +00:00
int rhizome_make_space ( int group_priority , long long bytes )
{
/* Asked for impossibly large amount */
2012-12-04 03:42:28 +00:00
if ( bytes > = ( config . rhizome . database_size - 65536 ) )
2012-08-24 05:56:25 +00:00
return WHYF ( " bytes=%lld is too large " , bytes ) ;
2012-01-12 03:38:24 +00:00
2012-06-12 08:42:36 +00:00
long long db_used = rhizome_database_used_bytes ( ) ;
if ( db_used = = - 1 )
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 */
2012-12-04 03:42:28 +00:00
if ( db_used < = ( config . rhizome . database_size - bytes - 65536 ) )
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 ;
2012-12-04 03:42:28 +00:00
while ( bytes > ( config . rhizome . database_size - 65536 - rhizome_database_used_bytes ( ) )
2012-08-24 05:56:25 +00:00
& & sqlite_step_retry ( & retry , statement ) = = SQLITE_ROW
) {
/* Make sure we can drop this blob, and if so drop it, and recalculate number of bytes required */
const unsigned char * id ;
/* Get values */
if ( sqlite3_column_type ( statement , 0 ) = = SQLITE_TEXT )
id = sqlite3_column_text ( statement , 0 ) ;
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
}
2012-08-24 05:56:25 +00:00
/* 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 ( ( char * ) id , group_priority + 1 ) ;
}
2012-01-12 03:38:24 +00:00
sqlite3_finalize ( statement ) ;
2012-05-10 04:37:11 +00:00
//long long equal_priority_larger_file_space_used = sqlite_exec_int64("SELECT COUNT(length) FROM FILES WHERE highestpriority=%d and length>%lld",group_priority,bytes);
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 .
*/
int rhizome_drop_stored_file ( const char * id , int maximum_priority )
2012-01-12 03:38:24 +00:00
{
2012-08-23 08:13:35 +00:00
if ( ! rhizome_str_is_file_hash ( id ) )
2013-10-02 15:46:10 +00:00
return WHYF ( " invalid file hash id=%s " , alloca_str_toprint ( id ) ) ;
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 FROM MANIFESTS WHERE filehash = ? " , TEXT_TOUPPER , id , END ) ;
2012-08-23 08:13:35 +00:00
if ( ! statement )
return WHYF ( " Could not drop stored file id=%s " , id ) ;
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-03 19:20:37 +00:00
WHYF ( " Cannot drop fileid=%s due to error, bid=%s " , id , alloca_tohex_rhizome_bid_t ( bid ) ) ;
2012-08-23 08:13:35 +00:00
else if ( priority > maximum_priority ) {
2013-10-03 19:20:37 +00:00
WHYF ( " Cannot drop fileid=%s due to manifest priority, bid=%s " , id , 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 )
rhizome_delete_file_retry ( & retry , id ) ;
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
*/
2012-05-25 15:20:48 +00:00
int rhizome_store_bundle ( rhizome_manifest * m )
2012-01-12 03:38:24 +00:00
{
2012-05-23 06:34:00 +00:00
if ( ! m - > finalised ) return WHY ( " Manifest was not finalised " ) ;
2012-01-12 03:38:24 +00:00
2012-05-27 23:33:49 +00:00
if ( m - > haveSecret ) {
/* We used to store the secret in the database, but we don't anymore, as we use
the BK field in the manifest . So nothing to do here . */
} else {
/* We don't have the secret for this manifest, so only allow updates if
the self - signature is valid */
2012-07-16 09:01:00 +00:00
if ( ! m - > selfSigned )
2012-05-27 23:33:49 +00:00
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-05-23 06:34:00 +00:00
char manifestid [ RHIZOME_MANIFEST_ID_STRLEN + 1 ] ;
rhizome_manifest_get ( m , " id " , manifestid , sizeof manifestid ) ;
str_toupper_inplace ( manifestid ) ;
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) */
2012-07-16 09:01:00 +00:00
char filehash [ RHIZOME_FILEHASH_STRLEN + 1 ] ;
if ( m - > fileLength > 0 ) {
strncpy ( filehash , m - > fileHexHash , sizeof filehash ) ;
str_toupper_inplace ( filehash ) ;
2012-10-02 21:36:06 +00:00
2012-12-20 04:48:59 +00:00
if ( ! rhizome_exists ( filehash ) )
return WHY ( " File should already be stored by now " ) ;
2012-07-16 09:01:00 +00:00
} else {
filehash [ 0 ] = ' \0 ' ;
2012-01-12 03:38:24 +00:00
}
2012-08-23 08:13:35 +00:00
2012-10-09 07:13:34 +00:00
const char * author = is_sid_any ( m - > author ) ? NULL : alloca_tohex_sid ( m - > author ) ;
2013-01-16 00:26:09 +00:00
const char * name = rhizome_manifest_get ( m , " name " , NULL , 0 ) ;
const char * sender = rhizome_manifest_get ( m , " sender " , NULL , 0 ) ;
const char * recipient = rhizome_manifest_get ( m , " recipient " , NULL , 0 ) ;
const char * service = rhizome_manifest_get ( m , " service " , NULL , 0 ) ;
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-02-12 00:04:04 +00:00
2012-08-23 08:13:35 +00:00
sqlite3_stmt * stmt ;
2013-07-22 05:34:02 +00:00
if ( ( stmt = sqlite_prepare ( & retry ,
" INSERT OR REPLACE INTO MANIFESTS( "
" id, "
" manifest, "
" version, "
" inserttime, "
" bar, "
" filesize, "
" filehash, "
" author, "
" service, "
" name, "
" sender, "
" recipient, "
" tail "
" ) VALUES( "
" ?,?,?,?,?,?,?,?,?,?,?,?,? "
" ); " ) ) = = NULL )
2012-08-23 08:13:35 +00:00
goto rollback ;
2013-02-12 00:04:04 +00:00
if ( ! ( sqlite_code_ok ( sqlite3_bind_text ( stmt , 1 , manifestid , - 1 , SQLITE_STATIC ) )
& & sqlite_code_ok ( sqlite3_bind_blob ( stmt , 2 , m - > manifestdata , m - > manifest_bytes , SQLITE_STATIC ) )
2012-08-23 08:13:35 +00:00
& & sqlite_code_ok ( sqlite3_bind_int64 ( stmt , 3 , m - > version ) )
& & sqlite_code_ok ( sqlite3_bind_int64 ( stmt , 4 , ( long long ) gettime_ms ( ) ) )
2013-02-12 00:04:04 +00:00
& & sqlite_code_ok ( sqlite3_bind_blob ( stmt , 5 , bar , RHIZOME_BAR_BYTES , SQLITE_STATIC ) )
2012-08-23 08:13:35 +00:00
& & sqlite_code_ok ( sqlite3_bind_int64 ( stmt , 6 , m - > fileLength ) )
2013-02-12 00:04:04 +00:00
& & sqlite_code_ok ( sqlite3_bind_text ( stmt , 7 , filehash , - 1 , SQLITE_STATIC ) )
& & sqlite_code_ok ( sqlite3_bind_text ( stmt , 8 , author , - 1 , SQLITE_STATIC ) )
& & sqlite_code_ok ( sqlite3_bind_text ( stmt , 9 , service , - 1 , SQLITE_STATIC ) )
& & sqlite_code_ok ( sqlite3_bind_text ( stmt , 10 , name , - 1 , SQLITE_STATIC ) )
& & sqlite_code_ok ( sqlite3_bind_text ( stmt , 11 , sender , - 1 , SQLITE_STATIC ) )
& & sqlite_code_ok ( sqlite3_bind_text ( stmt , 12 , recipient , - 1 , SQLITE_STATIC ) )
2013-07-22 05:34:02 +00:00
& & sqlite_code_ok ( sqlite3_bind_int64 ( stmt , 13 , m - > journalTail ) )
2012-08-23 08:13:35 +00:00
) ) {
2012-08-24 05:56:25 +00:00
WHYF ( " query failed, %s: %s " , sqlite3_errmsg ( rhizome_db ) , sqlite3_sql ( stmt ) ) ;
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-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
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 ;
2012-10-04 05:00:20 +00:00
if ( ( stmt = sqlite_prepare ( & retry , " INSERT OR REPLACE INTO GROUPLIST(id,closed,ciphered,priority) VALUES (?,?,?,?); " ) ) = = NULL )
2012-08-23 08:13:35 +00:00
goto rollback ;
if ( ! ( sqlite_code_ok ( sqlite3_bind_text ( stmt , 1 , manifestid , - 1 , SQLITE_TRANSIENT ) )
& & sqlite_code_ok ( sqlite3_bind_int ( stmt , 2 , closed ) )
& & sqlite_code_ok ( sqlite3_bind_int ( stmt , 3 , ciphered ) )
& & sqlite_code_ok ( sqlite3_bind_int ( stmt , 4 , RHIZOME_PRIORITY_DEFAULT ) )
) ) {
2012-08-24 05:56:25 +00:00
WHYF ( " query failed, %s: %s " , sqlite3_errmsg ( rhizome_db ) , sqlite3_sql ( stmt ) ) ;
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
}
2012-08-23 08:13:35 +00:00
if ( m - > group_count > 0 ) {
2012-10-04 05:00:20 +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 ;
2012-05-28 02:29:35 +00:00
int i ;
for ( i = 0 ; i < m - > group_count ; i + + ) {
2012-08-23 08:13:35 +00:00
if ( ! ( sqlite_code_ok ( sqlite3_bind_text ( stmt , 1 , manifestid , - 1 , SQLITE_TRANSIENT ) )
& & sqlite_code_ok ( sqlite3_bind_text ( stmt , 2 , m - > groups [ i ] , - 1 , SQLITE_TRANSIENT ) )
) ) {
2012-08-24 05:56:25 +00:00
WHYF ( " query failed, %s: %s " , sqlite3_errmsg ( rhizome_db ) , sqlite3_sql ( stmt ) ) ;
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
}
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.
const char * service = rhizome_manifest_get ( m , " service " , NULL , 0 ) ;
2013-07-15 00:29:24 +00:00
INFOF ( " RHIZOME ADD MANIFEST service=%s bid=%s version=% " PRId64 ,
2013-01-15 00:02:48 +00:00
service ? 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
) ;
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 ) ;
WHYF ( " Failed to store bundle bid=%s " , manifestid ) ;
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-07-03 07:21:27 +00:00
int rhizome_list_manifests ( struct cli_context * context , const char * service , const char * name ,
2013-01-17 01:13:31 +00:00
const char * sender_sid , const char * recipient_sid ,
2013-01-22 04:56:40 +00:00
int limit , int offset , char count_rows )
2012-04-02 08:12:40 +00:00
{
2012-07-03 05:42:42 +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-01-17 01:13:31 +00:00
if ( service & & * service )
strbuf_sprintf ( b , " AND service = ?1 " ) ;
if ( name & & * name )
strbuf_sprintf ( b , " AND name like ?2 " ) ;
if ( sender_sid & & * sender_sid )
strbuf_sprintf ( b , " AND sender = ?3 " ) ;
if ( recipient_sid & & * recipient_sid )
strbuf_sprintf ( b , " AND recipient = ?4 " ) ;
strbuf_sprintf ( b , " ORDER BY inserttime DESC " ) ;
2012-05-20 14:39:14 +00:00
if ( offset )
strbuf_sprintf ( b , " OFFSET %u " , offset ) ;
2013-01-17 01:13:31 +00:00
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 ) ) ) ;
2013-01-17 01:13:31 +00:00
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 ( & retry , strbuf_str ( b ) ) ;
2012-08-24 05:56:25 +00:00
if ( ! statement )
2012-11-12 04:07:58 +00:00
RETURN ( - 1 ) ;
2013-01-17 01:13:31 +00:00
2012-04-02 08:12:40 +00:00
int ret = 0 ;
2013-01-17 01:13:31 +00:00
if ( service & & * service )
ret = sqlite3_bind_text ( statement , 1 , service , - 1 , SQLITE_STATIC ) ;
if ( ret = = SQLITE_OK & & name & & * name )
ret = sqlite3_bind_text ( statement , 2 , name , - 1 , SQLITE_STATIC ) ;
if ( ret = = SQLITE_OK & & sender_sid & & * sender_sid )
ret = sqlite3_bind_text ( statement , 3 , sender_sid , - 1 , SQLITE_STATIC ) ;
if ( ret = = SQLITE_OK & & recipient_sid & & * recipient_sid )
ret = sqlite3_bind_text ( statement , 4 , recipient_sid , - 1 , SQLITE_STATIC ) ;
if ( ret ! = SQLITE_OK ) {
ret = WHYF ( " Failed to bind parameters: %s " , sqlite3_errmsg ( rhizome_db ) ) ;
goto cleanup ;
}
ret = 0 ;
2012-08-24 05:56:25 +00:00
size_t rows = 0 ;
2013-01-22 04:56:40 +00:00
const char * names [ ] = {
" _id " ,
" service " ,
" id " ,
" version " ,
" date " ,
" .inserttime " ,
" .author " ,
" .fromhere " ,
" filesize " ,
" filehash " ,
" sender " ,
" recipient " ,
" name "
} ;
2013-07-03 07:21:27 +00:00
cli_columns ( context , 13 , names ) ;
2013-01-22 04:56:40 +00:00
2012-08-24 05:56:25 +00:00
while ( sqlite_step_retry ( & retry , statement ) = = SQLITE_ROW ) {
+ + rows ;
2013-01-22 04:56:40 +00:00
if ( limit > 0 & & rows > limit )
break ;
if ( ! ( sqlite3_column_count ( statement ) = = 6
2012-08-24 05:56:25 +00:00
& & sqlite3_column_type ( statement , 0 ) = = SQLITE_TEXT
& & sqlite3_column_type ( statement , 1 ) = = SQLITE_BLOB
& & sqlite3_column_type ( statement , 2 ) = = SQLITE_INTEGER
& & sqlite3_column_type ( statement , 3 ) = = SQLITE_INTEGER
2012-10-09 07:13:34 +00:00
& & ( sqlite3_column_type ( statement , 4 ) = = SQLITE_TEXT
| | sqlite3_column_type ( statement , 4 ) = = SQLITE_NULL
)
2012-08-24 05:56:25 +00:00
) ) {
ret = WHY ( " Incorrect statement column " ) ;
break ;
}
rhizome_manifest * m = rhizome_new_manifest ( ) ;
if ( m = = NULL ) {
ret = WHY ( " Out of manifests " ) ;
break ;
}
const char * q_manifestid = ( const char * ) 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()
long long q_version = sqlite3_column_int64 ( statement , 2 ) ;
long long q_inserttime = sqlite3_column_int64 ( statement , 3 ) ;
2012-10-09 07:13:34 +00:00
const char * q_author = ( const char * ) sqlite3_column_text ( statement , 4 ) ;
2013-01-22 04:56:40 +00:00
long long rowid = sqlite3_column_int64 ( statement , 5 ) ;
2012-08-24 05:56:25 +00:00
if ( rhizome_read_manifest_file ( m , manifestblob , manifestblobsize ) = = - 1 ) {
WARNF ( " MANIFESTS row id=%s has invalid manifest blob -- skipped " , q_manifestid ) ;
} else {
long long blob_version = rhizome_manifest_get_ll ( m , " version " ) ;
if ( blob_version ! = q_version )
WARNF ( " MANIFESTS row id=%s version=%lld does not match manifest blob.version=%lld " , q_manifestid , q_version , blob_version ) ;
int match = 1 ;
2013-01-22 04:56:40 +00:00
2012-08-24 05:56:25 +00:00
const char * blob_service = rhizome_manifest_get ( m , " service " , NULL , 0 ) ;
if ( service [ 0 ] & & ! ( blob_service & & strcasecmp ( service , blob_service ) = = 0 ) )
match = 0 ;
const char * blob_sender = rhizome_manifest_get ( m , " sender " , NULL , 0 ) ;
const char * blob_recipient = rhizome_manifest_get ( m , " recipient " , NULL , 0 ) ;
if ( match & & sender_sid [ 0 ] ) {
if ( ! ( blob_sender & & strcasecmp ( sender_sid , blob_sender ) = = 0 ) )
match = 0 ;
2012-05-25 06:08:13 +00:00
}
2012-08-24 05:56:25 +00:00
if ( match & & recipient_sid [ 0 ] ) {
if ( ! ( blob_recipient & & strcasecmp ( recipient_sid , blob_recipient ) = = 0 ) )
2012-05-21 05:11:22 +00:00
match = 0 ;
}
2013-01-22 04:56:40 +00:00
2012-08-24 05:56:25 +00:00
if ( match ) {
const char * blob_name = rhizome_manifest_get ( m , " name " , NULL , 0 ) ;
long long blob_date = rhizome_manifest_get_ll ( m , " date " ) ;
const char * blob_filehash = rhizome_manifest_get ( m , " filehash " , NULL , 0 ) ;
2012-10-09 07:13:34 +00:00
int from_here = 0 ;
2013-01-22 04:56:40 +00:00
unsigned char senderSid [ SID_SIZE ] ;
unsigned char recipientSid [ SID_SIZE ] ;
if ( blob_sender )
stowSid ( senderSid , 0 , blob_sender ) ;
if ( blob_recipient )
stowSid ( recipientSid , 0 , blob_recipient ) ;
2012-10-09 07:13:34 +00:00
if ( q_author ) {
2012-12-11 05:29:46 +00:00
if ( config . debug . rhizome ) DEBUGF ( " q_author=%s " , alloca_str_toprint ( q_author ) ) ;
2013-01-22 04:56:40 +00:00
stowSid ( m - > author , 0 , q_author ) ;
2012-10-09 07:13:34 +00:00
int cn = 0 , in = 0 , kp = 0 ;
2013-01-22 04:56:40 +00:00
from_here = keyring_find_sid ( keyring , & cn , & in , & kp , m - > author ) ;
2012-10-09 07:13:34 +00:00
}
if ( ! from_here & & blob_sender ) {
2012-12-11 05:29:46 +00:00
if ( config . debug . rhizome ) DEBUGF ( " blob_sender=%s " , alloca_str_toprint ( blob_sender ) ) ;
2012-10-09 07:13:34 +00:00
int cn = 0 , in = 0 , kp = 0 ;
from_here = keyring_find_sid ( keyring , & cn , & in , & kp , senderSid ) ;
}
2013-01-22 04:56:40 +00:00
2013-07-03 07:21:27 +00:00
cli_put_long ( context , rowid , " : " ) ;
cli_put_string ( context , blob_service , " : " ) ;
2013-10-03 13:46:45 +00:00
cli_put_hexvalue ( context , m - > cryptoSignPublic . binary , sizeof m - > cryptoSignPublic . binary , " : " ) ;
2013-07-03 07:21:27 +00:00
cli_put_long ( context , blob_version , " : " ) ;
cli_put_long ( context , blob_date , " : " ) ;
cli_put_long ( context , q_inserttime , " : " ) ;
cli_put_hexvalue ( context , q_author ? m - > author : NULL , SID_SIZE , " : " ) ;
cli_put_long ( context , from_here , " : " ) ;
cli_put_long ( context , m - > fileLength , " : " ) ;
2013-01-22 04:56:40 +00:00
unsigned char filehash [ SHA512_DIGEST_LENGTH ] ;
if ( m - > fileLength )
fromhex ( filehash , blob_filehash , SHA512_DIGEST_LENGTH ) ;
2013-07-03 07:21:27 +00:00
cli_put_hexvalue ( context , m - > fileLength ? filehash : NULL , SHA512_DIGEST_LENGTH , " : " ) ;
2013-01-22 04:56:40 +00:00
2013-07-03 07:21:27 +00:00
cli_put_hexvalue ( context , blob_sender ? senderSid : NULL , SID_SIZE , " : " ) ;
cli_put_hexvalue ( context , blob_recipient ? recipientSid : NULL , SID_SIZE , " : " ) ;
cli_put_string ( context , blob_name , " \n " ) ;
2012-08-24 05:56:25 +00:00
}
2012-04-02 08:12:40 +00:00
}
2012-08-24 05:56:25 +00:00
if ( m ) rhizome_manifest_free ( m ) ;
2012-04-02 08:12:40 +00:00
}
2013-01-22 04:56:40 +00:00
if ( ret = = 0 & & count_rows ) {
while ( sqlite_step_retry ( & retry , statement ) = = SQLITE_ROW )
+ + rows ;
}
2013-07-03 07:21:27 +00:00
cli_row_count ( context , rows ) ;
2013-01-22 04:56:40 +00:00
2013-01-17 01:13:31 +00:00
cleanup :
2012-04-02 08:12:40 +00:00
sqlite3_finalize ( statement ) ;
2012-07-03 05:42:42 +00:00
RETURN ( ret ) ;
2013-02-16 17:47:24 +00:00
OUT ( ) ;
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
{
2012-06-08 05:57:15 +00:00
( void ) tohex ( out , in , byteCount ) ;
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-07-13 05:17:06 +00:00
int64_t highestPriority = - 1 ;
2012-08-23 08:13:35 +00:00
sqlite_retry_state retry = SQLITE_RETRY_STATE_DEFAULT ;
2012-08-22 09:39:30 +00:00
if ( sqlite_exec_int64_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; " ,
TEXT_TOUPPER , fileid , END ) = = - 1 )
2012-06-08 03:43:26 +00:00
return - 1 ;
2013-10-02 15:46:10 +00:00
if ( highestPriority > = 0
& & sqlite_exec_void_retry ( & retry ,
" UPDATE files SET highestPriority = ? WHERE id = ?; " ,
INT , highestPriority , TEXT_TOUPPER , fileid , END
) = = - 1
)
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
* is known , having the same version . Returns 1 if a duplicate is found ( setting * found to point to
* the duplicate ' s manifest ) , returns 0 if no duplicate is found ( leaving * found unchanged ) .
* Returns - 1 on error ( leaving * found undefined ) .
*
* @ author Andrew Bettison < andrew @ servalproject . com >
2012-04-12 09:00:52 +00:00
*/
2013-08-01 02:07:35 +00:00
int rhizome_find_duplicate ( const rhizome_manifest * m , rhizome_manifest * * found )
2012-04-12 09:00:52 +00:00
{
2012-05-20 14:39:14 +00:00
const char * service = rhizome_manifest_get ( m , " service " , NULL , 0 ) ;
2013-08-01 02:07:35 +00:00
if ( service = = NULL )
2012-05-20 14:39:14 +00:00
return WHY ( " Manifest has no service " ) ;
2013-08-01 02:07:35 +00:00
const char * name = rhizome_manifest_get ( m , " name " , NULL , 0 ) ;
const char * sender = rhizome_manifest_get ( m , " sender " , NULL , 0 ) ;
const char * recipient = rhizome_manifest_get ( m , " recipient " , NULL , 0 ) ;
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 = ? " ) ;
if ( m - > fileLength ! = 0 )
strbuf_puts ( b , " AND filehash = ? " ) ;
if ( name )
strbuf_puts ( b , " AND name = ? " ) ;
if ( sender )
strbuf_puts ( b , " AND sender = ? " ) ;
if ( recipient )
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-08-01 02:07:35 +00:00
2012-08-23 08:13:35 +00:00
int ret = 0 ;
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 ( & retry , strbuf_str ( b ) ) ;
2012-08-23 08:13:35 +00:00
if ( ! statement )
return - 1 ;
2013-08-01 02:07:35 +00:00
2012-08-23 08:13:35 +00:00
int field = 1 ;
2013-08-01 02:07:35 +00:00
sqlite3_bind_int ( statement , field + + , m - > fileLength ) ;
sqlite3_bind_text ( statement , field + + , service , - 1 , SQLITE_STATIC ) ;
if ( m - > fileLength ! = 0 )
sqlite3_bind_text ( statement , field + + , m - > fileHexHash , - 1 , SQLITE_STATIC ) ;
if ( name )
sqlite3_bind_text ( statement , field + + , name , - 1 , SQLITE_STATIC ) ;
if ( sender )
sqlite3_bind_text ( statement , field + + , sender , - 1 , SQLITE_STATIC ) ;
if ( recipient )
sqlite3_bind_text ( statement , field + + , recipient , - 1 , SQLITE_STATIC ) ;
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()
2012-08-23 08:13:35 +00:00
if ( rhizome_read_manifest_file ( blob_m , manifestblob , manifestblobsize ) = = - 1 ) {
WARNF ( " MANIFESTS row id=%s has invalid manifest blob -- skipped " , q_manifestid ) ;
2013-08-01 02:07:35 +00:00
goto next ;
}
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 ) {
2012-12-11 05:29:46 +00:00
if ( config . debug . rhizome )
2013-08-01 02:07:35 +00:00
strbuf_sprintf ( b , " .author=%s " , q_author ) ;
stowSid ( blob_m - > author , 0 , q_author ) ;
}
// check that we can re-author this manifest
if ( rhizome_extract_privatekey ( blob_m , NULL ) ) {
goto next ;
2012-04-12 09:00:52 +00:00
}
2013-08-01 02:07:35 +00:00
* found = blob_m ;
if ( config . debug . rhizome )
DEBUGF ( " Found duplicate payload, %s " , q_manifestid ) ;
ret = 1 ;
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 ) ;
int64_t q_version = sqlite3_column_int64 ( statement , 2 ) ;
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()
if ( rhizome_read_manifest_file ( m , q_blob , q_blobsize ) )
return WHYF ( " Manifest %s exists but is invalid " , q_id ) ;
if ( q_author ) {
if ( stowSid ( m - > author , 0 , q_author ) = = - 1 )
WARNF ( " manifest id=%s contains invalid author=%s -- ignored " , q_id , alloca_str_toprint ( q_author ) ) ;
}
if ( m - > version ! = q_version )
WARNF ( " Version mismatch, manifest is % " PRId64 " , database is % " PRId64 , m - > version , q_version ) ;
m - > inserttime = q_inserttime ;
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-10-03 13:46:45 +00:00
" SELECT id, manifest, version, inserttime, author 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 ;
char like [ prefix_len * 2 + 2 ] ;
tohex ( like , prefix , prefix_len ) ;
like [ prefix_len * 2 ] = ' % ' ;
like [ prefix_len * 2 + 1 ] = ' \0 ' ;
sqlite3_stmt * statement = sqlite_prepare_bind ( & retry ,
" SELECT id, manifest, version, inserttime, author FROM manifests WHERE id like ? " ,
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 ;
}
static int rhizome_delete_file_retry ( sqlite_retry_state * retry , const char * fileid )
{
int ret = 0 ;
2013-08-21 06:15:18 +00:00
rhizome_delete_external ( fileid ) ;
2013-10-03 07:14:06 +00:00
sqlite3_stmt * statement = sqlite_prepare_bind ( retry , " DELETE FROM files WHERE id = ? " , TEXT_TOUPPER , fileid , END ) ;
if ( ! statement | | sqlite_exec_retry ( retry , statement ) = = - 1 )
2013-02-20 04:14:29 +00:00
ret = - 1 ;
2013-10-03 07:14:06 +00:00
statement = sqlite_prepare_bind ( retry , " DELETE FROM fileblobs WHERE id = ? " , TEXT_TOUPPER , fileid , END ) ;
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 ;
if ( rows & & rhizome_delete_file_retry ( retry , strbuf_str ( fh ) ) = = - 1 )
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 >
*/
int rhizome_delete_file ( const char * fileid )
{
sqlite_retry_state retry = SQLITE_RETRY_STATE_DEFAULT ;
return rhizome_delete_file_retry ( & retry , fileid ) ;
}
2013-02-20 06:52:53 +00:00
2013-08-22 05:44:21 +00:00
static int is_interesting ( const char * id_hex , int64_t version )
{
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-03 07:14:06 +00:00
" SELECT filehash FROM manifests WHERE id like ? and version >= ? " ,
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-08-28 06:45:28 +00:00
if ( q_filehash & & * q_filehash & & ! rhizome_exists ( q_filehash ) )
2013-08-22 05:44:21 +00:00
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
int rhizome_is_bar_interesting ( unsigned char * bar )
{
int64_t version = rhizome_bar_version ( bar ) ;
char id_hex [ RHIZOME_MANIFEST_ID_STRLEN ] ;
tohex ( id_hex , & bar [ RHIZOME_BAR_PREFIX_OFFSET ] , RHIZOME_BAR_PREFIX_BYTES ) ;
strcat ( id_hex , " % " ) ;
return is_interesting ( id_hex , version ) ;
}
int rhizome_is_manifest_interesting ( rhizome_manifest * m )
{
char id [ RHIZOME_MANIFEST_ID_STRLEN + 1 ] ;
if ( ! rhizome_manifest_get ( m , " id " , id , sizeof id ) )
// dodgy manifest, we don't want to receive it
return WHY ( " Ignoring bad manifest (no ID field) " ) ;
str_toupper_inplace ( id ) ;
return is_interesting ( id , m - > version ) ;
2013-09-30 06:50:50 +00:00
}