2012-12-17 05:11:27 +00:00
# include "serval.h"
# include "rhizome.h"
# include "conf.h"
2013-01-08 06:50:42 +00:00
# include "strlcpy.h"
2012-12-17 05:11:27 +00:00
# define RHIZOME_BUFFER_MAXIMUM_SIZE (1024*1024)
int rhizome_exists ( const char * fileHash ) {
2013-07-13 05:17:06 +00:00
int64_t gotfile = 0 ;
2012-12-17 05:11:27 +00:00
if ( sqlite_exec_int64 ( & gotfile ,
2013-02-20 03:59:08 +00:00
" SELECT COUNT(*) FROM FILES WHERE ID='%s' and datavalid=1; " ,
2012-12-17 05:11:27 +00:00
fileHash ) ! = 1 ) {
return 0 ;
}
return gotfile ;
}
int rhizome_open_write ( struct rhizome_write * write , char * expectedFileHash , int64_t file_length , int priority ) {
2013-06-18 06:58:26 +00:00
write - > blob_fd = - 1 ;
2013-07-18 07:34:12 +00:00
2012-12-17 05:11:27 +00:00
if ( expectedFileHash ) {
if ( rhizome_exists ( expectedFileHash ) )
return 1 ;
strlcpy ( write - > id , expectedFileHash , SHA512_DIGEST_STRING_LENGTH ) ;
write - > id_known = 1 ;
} else {
snprintf ( write - > id , sizeof ( write - > id ) , " %lld " , gettime_ms ( ) ) ;
write - > id_known = 0 ;
}
2013-02-20 03:59:08 +00:00
sqlite_retry_state retry = SQLITE_RETRY_STATE_DEFAULT ;
2013-02-20 04:14:29 +00:00
if ( sqlite_exec_void_retry ( & retry , " BEGIN TRANSACTION; " ) = = - 1 )
2013-02-20 03:59:08 +00:00
return WHY ( " Failed to begin transaction " ) ;
/*
we have to write incrementally so that we can handle blobs larger than available memory .
This is possible using :
int sqlite3_bind_zeroblob ( sqlite3_stmt * , int , int n ) ;
That binds an all zeroes blob to a field . We can then populate the data by
opening a handle to the blob using :
int sqlite3_blob_write ( sqlite3_blob * , const void * z , int n , int iOffset ) ;
*/
sqlite3_stmt * statement = NULL ;
int ret = sqlite_exec_void_retry ( & retry ,
" INSERT OR REPLACE INTO FILES(id,length,highestpriority,datavalid,inserttime) VALUES('%s',%lld,%d,0,%lld); " ,
write - > id , ( long long ) file_length , priority , ( long long ) gettime_ms ( ) ) ;
2013-02-20 06:52:53 +00:00
if ( ret = = - 1 )
2013-02-20 03:59:08 +00:00
goto insert_row_fail ;
char blob_path [ 1024 ] ;
2013-07-18 07:34:12 +00:00
if ( config . rhizome . external_blobs | | file_length > 128 * 1024 ) {
2013-02-20 03:59:08 +00:00
if ( ! FORM_RHIZOME_DATASTORE_PATH ( blob_path , write - > id ) ) {
WHY ( " Invalid path " ) ;
goto insert_row_fail ;
}
if ( config . debug . externalblobs )
DEBUGF ( " Attempting to put blob for %s in %s " ,
write - > id , blob_path ) ;
write - > blob_fd = open ( blob_path , O_CREAT | O_TRUNC | O_WRONLY , 0664 ) ;
if ( write - > blob_fd < 0 )
goto insert_row_fail ;
if ( config . debug . externalblobs )
2013-06-18 06:58:26 +00:00
DEBUGF ( " Writing to new blob file %s (fd=%d) " , blob_path , write - > blob_fd ) ;
2013-02-20 03:59:08 +00:00
} else {
statement = sqlite_prepare ( & retry , " INSERT OR REPLACE INTO FILEBLOBS(id,data) VALUES('%s',?) " , write - > id ) ;
if ( ! statement ) {
WHYF ( " Failed to insert into fileblobs: %s " , sqlite3_errmsg ( rhizome_db ) ) ;
goto insert_row_fail ;
}
/* Bind appropriate sized zero-filled blob to data field */
if ( sqlite3_bind_zeroblob ( statement , 1 , file_length ) ! = SQLITE_OK ) {
WHYF ( " sqlite3_bind_zeroblob() failed: %s: %s " , sqlite3_errmsg ( rhizome_db ) , sqlite3_sql ( statement ) ) ;
goto insert_row_fail ;
}
/* Do actual insert, and abort if it fails */
int rowcount = 0 ;
int stepcode ;
while ( ( stepcode = _sqlite_step_retry ( __WHENCE__ , LOG_LEVEL_ERROR , & retry , statement ) ) = = SQLITE_ROW )
+ + rowcount ;
if ( rowcount )
WARNF ( " void query unexpectedly returned %d row%s " , rowcount , rowcount = = 1 ? " " : " s " ) ;
if ( ! sqlite_code_ok ( stepcode ) ) {
insert_row_fail :
WHYF ( " Failed to insert row for fileid=%s " , write - > id ) ;
if ( statement ) sqlite3_finalize ( statement ) ;
sqlite_exec_void_retry ( & retry , " ROLLBACK; " ) ;
return - 1 ;
}
sqlite3_finalize ( statement ) ;
statement = NULL ;
/* Get rowid for inserted row, so that we can modify the blob */
write - > blob_rowid = sqlite3_last_insert_rowid ( rhizome_db ) ;
if ( config . debug . rhizome_rx )
2013-07-15 00:29:24 +00:00
DEBUGF ( " Got rowid % " PRId64 " for %s " , write - > blob_rowid , write - > id ) ;
2013-02-20 03:59:08 +00:00
}
2013-02-20 06:52:53 +00:00
if ( sqlite_exec_void_retry ( & retry , " COMMIT; " ) = = - 1 ) {
2013-06-18 06:58:26 +00:00
if ( write - > blob_fd > = 0 ) {
if ( config . debug . externalblobs )
DEBUGF ( " Cancel write to fd %d " , write - > blob_fd ) ;
2013-02-20 03:59:08 +00:00
close ( write - > blob_fd ) ;
2013-06-18 06:58:26 +00:00
write - > blob_fd = - 1 ;
2013-02-20 03:59:08 +00:00
unlink ( blob_path ) ;
}
2013-02-20 04:14:29 +00:00
return - 1 ;
2013-02-20 03:59:08 +00:00
}
2012-12-17 05:11:27 +00:00
write - > file_length = file_length ;
write - > file_offset = 0 ;
2013-07-18 07:34:12 +00:00
write - > written_offset = 0 ;
2013-02-20 03:59:08 +00:00
2012-12-17 05:11:27 +00:00
SHA512_Init ( & write - > sha512_context ) ;
2012-12-28 02:16:07 +00:00
return 0 ;
2012-12-17 05:11:27 +00:00
}
2013-07-18 04:22:42 +00:00
/* blob_open / close will lock the database, this is bad for other processes that might attempt to
* use it at the same time . However , opening a blob has about O ( n ^ 2 ) performance .
* */
// encrypt and hash data, data buffers must be passed in file order.
static int prepare_data ( struct rhizome_write * write_state , unsigned char * buffer , int data_size ) {
if ( data_size < = 0 )
return WHY ( " No content supplied " ) ;
2013-02-20 03:59:08 +00:00
/* Make sure we aren't being asked to write more data than we expected */
2013-07-05 06:54:50 +00:00
if ( write_state - > file_offset + data_size > write_state - > file_length )
2013-07-18 04:22:42 +00:00
return WHYF ( " Too much content supplied, % " PRId64 " + %d > % " PRId64 ,
write_state - > file_offset , data_size , write_state - > file_length ) ;
2013-02-20 03:59:08 +00:00
if ( write_state - > crypt ) {
2013-07-22 05:34:02 +00:00
if ( rhizome_crypt_xor_block (
buffer , data_size ,
write_state - > file_offset + write_state - > tail ,
write_state - > key , write_state - > nonce ) )
2013-07-18 04:22:42 +00:00
return - 1 ;
2012-12-21 00:23:47 +00:00
}
2012-12-18 00:21:12 +00:00
2013-07-18 04:22:42 +00:00
SHA512_Update ( & write_state - > sha512_context , buffer , data_size ) ;
write_state - > file_offset + = data_size ;
2013-07-18 07:34:12 +00:00
if ( config . debug . rhizome )
2013-07-19 00:58:15 +00:00
DEBUGF ( " Processed % " PRId64 " of % " PRId64 , write_state - > file_offset , write_state - > file_length ) ;
2013-07-18 04:22:42 +00:00
return 0 ;
}
// open database locks
static int write_get_lock ( struct rhizome_write * write_state ) {
if ( write_state - > blob_fd > = 0 | | write_state - > sql_blob )
return 0 ;
sqlite_retry_state retry = SQLITE_RETRY_STATE_DEFAULT ;
2013-08-02 04:32:56 +00:00
// use an explicit transaction so we can delay I/O failures until COMMIT so they can be retried.
if ( sqlite_exec_void_retry ( & retry , " BEGIN TRANSACTION; " ) = = - 1 )
return - 1 ;
2013-07-18 04:22:42 +00:00
while ( 1 ) {
int ret = sqlite3_blob_open ( rhizome_db , " main " , " FILEBLOBS " , " data " ,
write_state - > blob_rowid , 1 /* read/write */ , & write_state - > sql_blob ) ;
2013-08-02 04:32:56 +00:00
if ( ret = = SQLITE_OK ) {
sqlite_retry_done ( & retry , " sqlite3_blob_open " ) ;
2013-07-18 04:22:42 +00:00
return 0 ;
2013-08-02 04:32:56 +00:00
}
2013-07-18 04:22:42 +00:00
if ( ! sqlite_code_busy ( ret ) )
2013-08-20 23:51:40 +00:00
return WHYF ( " sqlite3_blob_open() failed: %s " ,
2013-07-18 04:22:42 +00:00
sqlite3_errmsg ( rhizome_db ) ) ;
if ( sqlite_retry ( & retry , " sqlite3_blob_open " ) = = 0 )
2013-08-02 04:32:56 +00:00
return WHYF ( " Giving up " ) ;
2013-07-18 04:22:42 +00:00
}
}
// write data to disk
static int write_data ( struct rhizome_write * write_state , uint64_t file_offset , unsigned char * buffer , int data_size ) {
if ( data_size < = 0 )
return 0 ;
2013-07-18 07:34:12 +00:00
if ( file_offset ! = write_state - > written_offset )
WARNF ( " Writing file data out of order! [% " PRId64 " ,% " PRId64 " ] " , file_offset , write_state - > written_offset ) ;
2013-07-18 04:22:42 +00:00
2013-07-03 01:23:25 +00:00
if ( write_state - > blob_fd > = 0 ) {
2013-02-20 03:59:08 +00:00
int ofs = 0 ;
// keep trying until all of the data is written.
2013-07-18 04:22:42 +00:00
lseek ( write_state - > blob_fd , file_offset , SEEK_SET ) ;
2013-07-05 06:54:50 +00:00
while ( ofs < data_size ) {
int r = write ( write_state - > blob_fd , buffer + ofs , data_size - ofs ) ;
2013-02-20 03:59:08 +00:00
if ( r < 0 )
2013-07-18 04:22:42 +00:00
return WHY_perror ( " write " ) ;
2013-06-18 06:58:26 +00:00
if ( config . debug . externalblobs )
DEBUGF ( " Wrote %d bytes to fd %d " , r , write_state - > blob_fd ) ;
2013-02-20 03:59:08 +00:00
ofs + = r ;
2012-12-27 04:45:23 +00:00
}
2013-02-20 03:59:08 +00:00
} else {
2013-07-18 04:22:42 +00:00
if ( ! write_state - > sql_blob )
return WHY ( " Must call write_get_lock() before write_data ( ) " ) ;
2013-02-20 03:59:08 +00:00
sqlite_retry_state retry = SQLITE_RETRY_STATE_DEFAULT ;
2013-07-18 04:22:42 +00:00
while ( 1 ) {
int ret = sqlite3_blob_write ( write_state - > sql_blob , buffer , data_size , file_offset ) ;
2013-08-02 04:32:56 +00:00
if ( ret = = SQLITE_OK ) {
sqlite_retry_done ( & retry , " sqlite3_blob_write " ) ;
2013-02-20 03:59:08 +00:00
break ;
2013-08-02 04:32:56 +00:00
}
2013-07-18 04:22:42 +00:00
if ( ! sqlite_code_busy ( ret ) )
return WHYF ( " sqlite3_blob_write() failed: %s " ,
sqlite3_errmsg ( rhizome_db ) ) ;
2013-02-20 03:59:08 +00:00
if ( sqlite_retry ( & retry , " sqlite3_blob_write " ) = = 0 )
2013-08-02 04:32:56 +00:00
return WHY ( " Giving up " ) ;
2013-07-18 04:22:42 +00:00
}
2013-02-20 03:59:08 +00:00
}
2013-07-18 07:34:12 +00:00
write_state - > written_offset = file_offset + data_size ;
2012-12-17 05:11:27 +00:00
if ( config . debug . rhizome )
2013-07-18 07:34:12 +00:00
DEBUGF ( " Wrote % " PRId64 " of % " PRId64 , file_offset + data_size , write_state - > file_length ) ;
2013-07-18 04:22:42 +00:00
return 0 ;
}
// close database locks
static int write_release_lock ( struct rhizome_write * write_state ) {
2013-08-02 04:32:56 +00:00
int ret = 0 ;
2013-07-18 04:22:42 +00:00
if ( write_state - > blob_fd > = 0 )
return 0 ;
2013-08-02 04:32:56 +00:00
if ( write_state - > sql_blob ) {
ret = sqlite3_blob_close ( write_state - > sql_blob ) ;
if ( ret )
WHYF ( " sqlite3_blob_close() failed: %s " ,
sqlite3_errmsg ( rhizome_db ) ) ;
sqlite_retry_state retry = SQLITE_RETRY_STATE_DEFAULT ;
if ( sqlite_exec_void_retry ( & retry , " COMMIT; " ) = = - 1 )
ret = - 1 ;
}
2013-07-18 04:22:42 +00:00
write_state - > sql_blob = NULL ;
2013-08-02 04:32:56 +00:00
return ret ;
2013-07-18 04:22:42 +00:00
}
// Write data buffers in any order, the data will be cached and streamed into the database in file order.
// Though there is an upper bound on the amount of cached data
2013-07-17 06:17:35 +00:00
int rhizome_random_write ( struct rhizome_write * write_state , int64_t offset , unsigned char * buffer , int data_size ) {
if ( offset + data_size > write_state - > file_length )
data_size = write_state - > file_length - offset ;
2013-07-18 07:34:12 +00:00
struct rhizome_write_buffer * * ptr = & write_state - > buffer_list ;
int ret = 0 ;
int should_write = 0 ;
if ( write_state - > blob_fd > = 0 ) {
should_write = 1 ;
} else {
int64_t new_size = write_state - > written_offset + write_state - > buffer_size + data_size ;
if ( new_size > = write_state - > file_length | | new_size > = RHIZOME_BUFFER_MAXIMUM_SIZE )
should_write = 1 ;
}
int64_t last_offset = write_state - > written_offset ;
2013-07-17 06:17:35 +00:00
while ( 1 ) {
2013-07-18 07:34:12 +00:00
// can we process this existing data block now?
2013-07-17 06:17:35 +00:00
if ( * ptr & & ( * ptr ) - > offset = = write_state - > file_offset ) {
2013-07-18 07:34:12 +00:00
if ( prepare_data ( write_state , ( * ptr ) - > data , ( * ptr ) - > data_size ) ) {
ret = - 1 ;
break ;
}
continue ;
}
// if existing data should be written, do so now
if ( should_write & & * ptr & & ( * ptr ) - > offset = = write_state - > written_offset ) {
2013-07-17 06:17:35 +00:00
struct rhizome_write_buffer * n = * ptr ;
2013-07-18 07:34:12 +00:00
if ( write_get_lock ( write_state ) ) {
ret = - 1 ;
break ;
}
if ( write_data ( write_state , n - > offset , n - > data , n - > data_size ) ) {
ret = - 1 ;
break ;
}
write_state - > buffer_size - = n - > data_size ;
last_offset = n - > offset + n - > data_size ;
2013-07-17 06:17:35 +00:00
* ptr = n - > _next ;
free ( n ) ;
continue ;
}
2013-07-18 07:34:12 +00:00
// skip over incoming data that we've already received
2013-07-17 06:17:35 +00:00
if ( offset < last_offset ) {
int64_t delta = last_offset - offset ;
if ( delta > = data_size )
2013-07-18 07:34:12 +00:00
break ;
2013-07-17 06:17:35 +00:00
data_size - = delta ;
offset + = delta ;
buffer + = delta ;
}
if ( data_size < = 0 )
2013-07-18 07:34:12 +00:00
break ;
// can we process the incoming data block now?
if ( data_size > 0 & & offset = = write_state - > file_offset ) {
if ( prepare_data ( write_state , buffer , data_size ) ) {
ret = - 1 ;
break ;
}
continue ;
}
2013-07-17 06:17:35 +00:00
if ( ! * ptr | | offset < ( * ptr ) - > offset ) {
// found the insert position in the list
int64_t size = data_size ;
// allow for buffers to overlap, we may need to split the incoming buffer into multiple pieces.
if ( * ptr & & offset + size > ( * ptr ) - > offset )
size = ( * ptr ) - > offset - offset ;
2013-07-18 07:34:12 +00:00
if ( should_write & & offset = = write_state - > file_offset ) {
if ( write_get_lock ( write_state ) ) {
ret = - 1 ;
break ;
}
if ( write_data ( write_state , offset , buffer , size ) ) {
ret = - 1 ;
break ;
}
2013-07-17 06:17:35 +00:00
// we need to go around the loop again to re-test if this buffer can now be written
} else {
// impose a limit on the total amount of cached data
2013-07-18 07:34:12 +00:00
if ( write_state - > buffer_size + size > RHIZOME_BUFFER_MAXIMUM_SIZE )
size = RHIZOME_BUFFER_MAXIMUM_SIZE - write_state - > buffer_size ;
2013-07-17 06:17:35 +00:00
if ( size < = 0 )
2013-07-18 07:34:12 +00:00
break ;
2013-07-17 06:17:35 +00:00
if ( config . debug . rhizome )
2013-07-18 07:34:12 +00:00
DEBUGF ( " Caching block @% " PRId64 " , % " PRId64 , offset , size ) ;
2013-07-17 06:17:35 +00:00
struct rhizome_write_buffer * i = emalloc ( size + sizeof ( struct rhizome_write_buffer ) ) ;
2013-07-18 07:34:12 +00:00
if ( ! i ) {
ret = - 1 ;
break ;
}
2013-07-17 06:17:35 +00:00
i - > offset = offset ;
i - > buffer_size = i - > data_size = size ;
bcopy ( buffer , i - > data , size ) ;
i - > _next = * ptr ;
2013-07-18 07:34:12 +00:00
write_state - > buffer_size + = size ;
2013-07-17 06:17:35 +00:00
* ptr = i ;
// if there's any overlap of this buffer and the current one, we may need to add another buffer.
ptr = & ( ( * ptr ) - > _next ) ;
}
data_size - = size ;
offset + = size ;
buffer + = size ;
continue ;
}
last_offset = ( * ptr ) - > offset + ( * ptr ) - > data_size ;
ptr = & ( ( * ptr ) - > _next ) ;
}
2013-08-02 04:32:56 +00:00
if ( write_release_lock ( write_state ) )
ret = - 1 ;
2013-07-18 07:34:12 +00:00
return ret ;
}
int rhizome_write_buffer ( struct rhizome_write * write_state , unsigned char * buffer , int data_size ) {
return rhizome_random_write ( write_state , write_state - > file_offset , buffer , data_size ) ;
2013-07-05 06:54:50 +00:00
}
2013-02-20 03:59:08 +00:00
/* Expects file to be at least file_length in size, ignoring anything longer than that */
2012-12-18 00:21:12 +00:00
int rhizome_write_file ( struct rhizome_write * write , const char * filename ) {
FILE * f = fopen ( filename , " r " ) ;
if ( ! f )
return WHY_perror ( " fopen " ) ;
2013-07-17 06:17:35 +00:00
unsigned char buffer [ RHIZOME_CRYPT_PAGE_SIZE ] ;
int ret = 0 ;
2013-08-20 23:51:40 +00:00
ret = write_get_lock ( write ) ;
if ( ret )
goto end ;
2012-12-18 00:21:12 +00:00
while ( write - > file_offset < write - > file_length ) {
2013-07-18 04:22:42 +00:00
int size = sizeof ( buffer ) ;
2012-12-18 00:21:12 +00:00
if ( write - > file_offset + size > write - > file_length )
size = write - > file_length - write - > file_offset ;
2013-07-17 06:17:35 +00:00
int r = fread ( buffer , 1 , size , f ) ;
2012-12-18 00:21:12 +00:00
if ( r = = - 1 ) {
2013-07-17 06:17:35 +00:00
ret = WHY_perror ( " fread " ) ;
goto end ;
2012-12-18 00:21:12 +00:00
}
2013-07-19 00:58:15 +00:00
if ( rhizome_write_buffer ( write , buffer , r ) ) {
2013-07-17 06:17:35 +00:00
ret = - 1 ;
goto end ;
2012-12-18 00:21:12 +00:00
}
}
2013-07-17 06:17:35 +00:00
end :
2013-08-02 04:32:56 +00:00
if ( write_release_lock ( write ) )
ret = - 1 ;
2012-12-18 00:21:12 +00:00
fclose ( f ) ;
2013-07-17 06:17:35 +00:00
return ret ;
2012-12-18 00:21:12 +00:00
}
2013-02-20 03:59:08 +00:00
int rhizome_store_delete ( const char * id ) {
char blob_path [ 1024 ] ;
if ( ! FORM_RHIZOME_DATASTORE_PATH ( blob_path , id ) )
return - 1 ;
2013-02-20 06:52:53 +00:00
if ( unlink ( blob_path ) ) {
if ( config . debug . externalblobs )
DEBUG_perror ( " unlink " ) ;
return - 1 ;
}
return 0 ;
2013-02-20 03:59:08 +00:00
}
2012-12-17 05:11:27 +00:00
int rhizome_fail_write ( struct rhizome_write * write ) {
2013-06-18 06:58:26 +00:00
if ( write - > blob_fd > = 0 ) {
if ( config . debug . externalblobs )
DEBUGF ( " Closing and removing fd %d " , write - > blob_fd ) ;
2013-02-20 03:59:08 +00:00
close ( write - > blob_fd ) ;
2013-06-18 06:58:26 +00:00
write - > blob_fd = - 1 ;
2013-02-20 03:59:08 +00:00
rhizome_store_delete ( write - > id ) ;
}
2013-07-18 04:22:42 +00:00
write_release_lock ( write ) ;
2013-07-18 07:34:12 +00:00
while ( write - > buffer_list ) {
struct rhizome_write_buffer * n = write - > buffer_list ;
write - > buffer_list = n - > _next ;
free ( n ) ;
}
2012-12-17 05:11:27 +00:00
// don't worry too much about sql failures.
sqlite_retry_state retry = SQLITE_RETRY_STATE_DEFAULT ;
2013-07-17 06:17:35 +00:00
if ( write - > blob_rowid > = 0 ) {
2013-02-20 06:52:53 +00:00
sqlite_exec_void_retry_loglevel ( LOG_LEVEL_WARN , & retry ,
2013-02-20 03:59:08 +00:00
" DELETE FROM FILEBLOBS WHERE rowid=%lld " , write - > blob_rowid ) ;
2013-07-17 06:17:35 +00:00
write - > blob_rowid = - 1 ;
}
2013-02-20 06:52:53 +00:00
sqlite_exec_void_retry_loglevel ( LOG_LEVEL_WARN , & retry ,
2012-12-17 05:11:27 +00:00
" DELETE FROM FILES WHERE id='%s' " ,
write - > id ) ;
2013-07-17 06:17:35 +00:00
return 0 ;
2012-12-17 05:11:27 +00:00
}
int rhizome_finish_write ( struct rhizome_write * write ) {
2013-07-18 07:34:12 +00:00
if ( write - > buffer_list ) {
if ( rhizome_random_write ( write , 0 , NULL , 0 ) )
goto failure ;
if ( write - > buffer_list ) {
WHYF ( " Buffer was not cleared " ) ;
goto failure ;
}
}
if ( write - > file_offset < write - > file_length ) {
WHYF ( " Only processed % " PRId64 " bytes, expected % " PRId64 , write - > file_offset , write - > file_length ) ;
}
2013-07-03 01:23:25 +00:00
int fd = write - > blob_fd ;
if ( fd > = 0 ) {
2013-06-18 06:58:26 +00:00
if ( config . debug . externalblobs )
2013-07-03 01:23:25 +00:00
DEBUGF ( " Closing fd %d " , fd ) ;
close ( fd ) ;
2013-06-18 06:58:26 +00:00
write - > blob_fd = - 1 ;
}
2013-08-02 04:32:56 +00:00
if ( write_release_lock ( write ) )
goto failure ;
2012-12-17 05:11:27 +00:00
char hash_out [ SHA512_DIGEST_STRING_LENGTH + 1 ] ;
SHA512_End ( & write - > sha512_context , hash_out ) ;
sqlite_retry_state retry = SQLITE_RETRY_STATE_DEFAULT ;
2013-02-20 04:14:29 +00:00
if ( sqlite_exec_void_retry ( & retry , " BEGIN TRANSACTION; " ) = = - 1 )
2013-08-20 23:51:40 +00:00
goto dbfailure ;
2012-12-17 05:11:27 +00:00
if ( write - > id_known ) {
if ( strcasecmp ( write - > id , hash_out ) ) {
WHYF ( " Expected hash=%s, got %s " , write - > id , hash_out ) ;
2013-08-20 23:51:40 +00:00
goto dbfailure ;
2012-12-17 05:11:27 +00:00
}
2013-02-20 04:14:29 +00:00
if ( sqlite_exec_void_retry ( & retry , " UPDATE FILES SET inserttime=%lld, datavalid=1 WHERE id='%s' " ,
gettime_ms ( ) , write - > id ) = = - 1 )
2013-08-20 23:51:40 +00:00
goto dbfailure ;
2012-12-17 05:11:27 +00:00
} else {
str_toupper_inplace ( hash_out ) ;
if ( rhizome_exists ( hash_out ) ) {
// ooops, we've already got that file, delete the new copy.
rhizome_fail_write ( write ) ;
} else {
// delete any half finished records
2013-02-20 04:14:29 +00:00
sqlite_exec_void_retry_loglevel ( LOG_LEVEL_WARN , & retry , " DELETE FROM FILEBLOBS WHERE id='%s'; " , hash_out ) ;
sqlite_exec_void_retry_loglevel ( LOG_LEVEL_WARN , & retry , " DELETE FROM FILES WHERE id='%s'; " , hash_out ) ;
2012-12-17 05:11:27 +00:00
if ( sqlite_exec_void_retry ( & retry ,
2012-12-31 04:39:12 +00:00
" UPDATE FILES SET id='%s', inserttime=%lld, datavalid=1 WHERE id='%s' " ,
2013-02-20 04:14:29 +00:00
hash_out , gettime_ms ( ) , write - > id ) = = - 1 )
2013-08-20 23:51:40 +00:00
goto dbfailure ;
2013-02-20 03:59:08 +00:00
2013-07-03 01:23:25 +00:00
if ( fd > = 0 ) {
2013-02-20 03:59:08 +00:00
char blob_path [ 1024 ] ;
char dest_path [ 1024 ] ;
if ( ! FORM_RHIZOME_DATASTORE_PATH ( blob_path , write - > id ) ) {
WHYF ( " Failed to generate file path " ) ;
2013-08-20 23:51:40 +00:00
goto dbfailure ;
2013-02-20 03:59:08 +00:00
}
if ( ! FORM_RHIZOME_DATASTORE_PATH ( dest_path , hash_out ) ) {
WHYF ( " Failed to generate file path " ) ;
2013-08-20 23:51:40 +00:00
goto dbfailure ;
2013-02-20 03:59:08 +00:00
}
if ( link ( blob_path , dest_path ) ) {
WHY_perror ( " link " ) ;
2013-08-20 23:51:40 +00:00
goto dbfailure ;
2013-02-20 03:59:08 +00:00
}
if ( unlink ( blob_path ) )
WHY_perror ( " unlink " ) ;
} else {
if ( sqlite_exec_void_retry ( & retry ,
" UPDATE FILEBLOBS SET id='%s' WHERE rowid=%lld " ,
2013-02-20 06:52:53 +00:00
hash_out , write - > blob_rowid ) = = - 1 ) {
2013-08-20 23:51:40 +00:00
goto dbfailure ;
2013-02-20 03:59:08 +00:00
}
2012-12-17 05:11:27 +00:00
}
}
strlcpy ( write - > id , hash_out , SHA512_DIGEST_STRING_LENGTH ) ;
}
2013-02-20 04:14:29 +00:00
if ( sqlite_exec_void_retry ( & retry , " COMMIT; " ) = = - 1 )
2013-08-20 23:51:40 +00:00
goto dbfailure ;
2013-07-17 06:17:35 +00:00
write - > blob_rowid = - 1 ;
2012-12-28 02:16:07 +00:00
return 0 ;
2012-12-17 05:11:27 +00:00
2013-08-20 23:51:40 +00:00
dbfailure :
2012-12-26 23:09:35 +00:00
sqlite_exec_void_retry ( & retry , " ROLLBACK; " ) ;
2013-08-20 23:51:40 +00:00
failure :
2012-12-17 05:11:27 +00:00
rhizome_fail_write ( write ) ;
return - 1 ;
}
2012-12-19 05:46:49 +00:00
// import a file for an existing bundle with a known file hash
int rhizome_import_file ( rhizome_manifest * m , const char * filepath )
{
if ( m - > fileLength < = 0 )
return 0 ;
/* Import the file first, checking the hash as we go */
struct rhizome_write write ;
bzero ( & write , sizeof ( write ) ) ;
int ret = rhizome_open_write ( & write , m - > fileHexHash , m - > fileLength , RHIZOME_PRIORITY_DEFAULT ) ;
if ( ret ! = 0 )
return ret ;
// file payload is not in the store yet
if ( rhizome_write_file ( & write , filepath ) ) {
rhizome_fail_write ( & write ) ;
return - 1 ;
}
2013-01-06 02:34:49 +00:00
if ( rhizome_finish_write ( & write ) ) {
rhizome_fail_write ( & write ) ;
return - 1 ;
}
return 0 ;
2012-12-19 05:46:49 +00:00
}
2013-08-20 23:51:40 +00:00
// store a whole payload from a single buffer
int rhizome_import_buffer ( rhizome_manifest * m , unsigned char * buffer , int length )
{
if ( m - > fileLength < = 0 )
return 0 ;
if ( length ! = m - > fileLength )
return WHYF ( " Expected % " PRId64 " bytes, got %d " , m - > fileLength , length ) ;
/* Import the file first, checking the hash as we go */
struct rhizome_write write ;
bzero ( & write , sizeof ( write ) ) ;
int ret = rhizome_open_write ( & write , m - > fileHexHash , m - > fileLength , RHIZOME_PRIORITY_DEFAULT ) ;
if ( ret ! = 0 )
return ret ;
// file payload is not in the store yet
if ( rhizome_write_buffer ( & write , buffer , length ) ) {
rhizome_fail_write ( & write ) ;
return - 1 ;
}
if ( rhizome_finish_write ( & write ) ) {
rhizome_fail_write ( & write ) ;
return - 1 ;
}
return 0 ;
}
2012-12-20 04:48:59 +00:00
int rhizome_stat_file ( rhizome_manifest * m , const char * filepath )
2012-12-19 05:46:49 +00:00
{
2013-01-15 00:02:48 +00:00
long long existing = rhizome_manifest_get_ll ( m , " filesize " ) ;
2012-12-19 05:46:49 +00:00
m - > fileLength = 0 ;
if ( filepath [ 0 ] ) {
struct stat stat ;
if ( lstat ( filepath , & stat ) )
return WHYF ( " Could not stat() payload file ' % s ' " ,filepath) ;
m - > fileLength = stat . st_size ;
}
2013-01-15 00:02:48 +00:00
// fail if the file is shorter than specified by the manifest
if ( existing > m - > fileLength )
return WHY ( " Manifest length is longer than the file " ) ;
// if the file is longer than specified by the manifest, ignore the end.
if ( existing ! = - 1 & & existing < m - > fileLength )
m - > fileLength = existing ;
2012-12-19 05:46:49 +00:00
rhizome_manifest_set_ll ( m , " filesize " , m - > fileLength ) ;
if ( m - > fileLength = = 0 ) {
m - > fileHexHash [ 0 ] = ' \0 ' ;
rhizome_manifest_del ( m , " filehash " ) ;
}
2012-12-20 04:48:59 +00:00
return 0 ;
}
2013-07-05 06:54:50 +00:00
static int rhizome_write_derive_key ( rhizome_manifest * m , rhizome_bk_t * bsk , struct rhizome_write * write )
{
2013-07-19 06:45:05 +00:00
if ( ! m - > payloadEncryption )
return 0 ;
2013-07-05 06:54:50 +00:00
// if the manifest specifies encryption, make sure we can generate the payload key and encrypt the contents as we go
if ( rhizome_derive_key ( m , bsk ) )
return - 1 ;
2013-07-19 06:45:05 +00:00
if ( config . debug . rhizome )
2013-07-26 05:06:11 +00:00
DEBUGF ( " Encrypting payload contents for %s, % " PRId64 , alloca_tohex_bid ( m - > cryptoSignPublic ) , m - > version ) ;
2013-07-19 06:45:05 +00:00
write - > crypt = 1 ;
2013-07-26 05:43:22 +00:00
if ( m - > journalTail > 0 )
write - > tail = m - > journalTail ;
2013-07-22 05:34:02 +00:00
2013-07-05 06:54:50 +00:00
bcopy ( m - > payloadKey , write - > key , sizeof ( write - > key ) ) ;
bcopy ( m - > payloadNonce , write - > nonce , sizeof ( write - > nonce ) ) ;
return 0 ;
}
2013-07-25 05:12:30 +00:00
int rhizome_write_open_manifest ( struct rhizome_write * write , rhizome_manifest * m )
{
if ( rhizome_open_write ( write , NULL , m - > fileLength , RHIZOME_PRIORITY_DEFAULT ) )
return - 1 ;
if ( rhizome_write_derive_key ( m , NULL , write ) )
return - 1 ;
return 0 ;
}
2012-12-20 04:48:59 +00:00
// import a file for a new bundle with an unknown file hash
// update the manifest with the details of the file
int rhizome_add_file ( rhizome_manifest * m , const char * filepath )
{
2012-12-19 05:46:49 +00:00
// Stream the file directly into the database, encrypting & hashing as we go.
struct rhizome_write write ;
bzero ( & write , sizeof ( write ) ) ;
2013-07-25 05:12:30 +00:00
if ( rhizome_write_open_manifest ( & write , m ) )
goto failure ;
if ( rhizome_write_file ( & write , filepath ) )
goto failure ;
2013-07-05 06:54:50 +00:00
if ( rhizome_finish_write ( & write ) )
goto failure ;
2012-12-19 05:46:49 +00:00
strlcpy ( m - > fileHexHash , write . id , SHA512_DIGEST_STRING_LENGTH ) ;
rhizome_manifest_set ( m , " filehash " , m - > fileHexHash ) ;
return 0 ;
2013-07-05 06:54:50 +00:00
failure :
rhizome_fail_write ( & write ) ;
return - 1 ;
2012-12-19 05:46:49 +00:00
}
2012-12-20 04:48:59 +00:00
2013-02-20 04:14:29 +00:00
/* Return -1 on error, 0 if file blob found, 1 if not found.
*/
int rhizome_open_read ( struct rhizome_read * read , const char * fileid , int hash )
{
2012-12-28 01:04:22 +00:00
strncpy ( read - > id , fileid , sizeof read - > id ) ;
read - > id [ RHIZOME_FILEHASH_STRLEN ] = ' \0 ' ;
str_toupper_inplace ( read - > id ) ;
2013-02-22 03:55:48 +00:00
read - > blob_rowid = - 1 ;
read - > blob_fd = - 1 ;
if ( sqlite_exec_int64 ( & read - > blob_rowid , " SELECT FILEBLOBS.rowid FROM FILEBLOBS, FILES WHERE FILEBLOBS.id = FILES.id AND FILES.id = '%s' AND FILES.datavalid != 0 " , read - > id ) = = - 1 )
return - 1 ;
if ( read - > blob_rowid ! = - 1 ) {
read - > length = - 1 ; // discover the length on opening the db BLOB
} else {
// No row in FILEBLOBS, look for an external blob file.
2013-02-20 03:59:08 +00:00
char blob_path [ 1024 ] ;
if ( ! FORM_RHIZOME_DATASTORE_PATH ( blob_path , read - > id ) )
return - 1 ;
2013-02-22 03:55:48 +00:00
read - > blob_fd = open ( blob_path , O_RDONLY ) ;
if ( read - > blob_fd = = - 1 ) {
if ( errno = = ENOENT )
return 1 ; // file not available
return WHYF_perror ( " open(%s) " , alloca_str_toprint(blob_path)) ;
}
if ( ( read - > length = lseek ( read - > blob_fd , 0 , SEEK_END ) ) = = - 1 )
return WHYF_perror ( " lseek(%s,0,SEEK_END) " , alloca_str_toprint(blob_path)) ;
2013-06-18 06:58:26 +00:00
if ( config . debug . externalblobs )
2013-07-15 00:29:24 +00:00
DEBUGF ( " Opened stored file %s as fd %d, len % " PRIx64 , blob_path , read - > blob_fd , read - > length ) ;
2012-12-28 01:04:22 +00:00
}
2013-02-22 03:55:48 +00:00
read - > hash = hash ;
read - > offset = 0 ;
2012-12-28 01:04:22 +00:00
if ( hash )
SHA512_Init ( & read - > sha512_context ) ;
2013-02-22 03:55:48 +00:00
return 0 ; // file opened
2012-12-20 04:48:59 +00:00
}
2013-02-20 03:59:08 +00:00
/* Read content from the store, hashing and decrypting as we go.
Random access is supported , but hashing requires reads to be sequential though we don ' t enforce this . */
2012-12-28 01:04:22 +00:00
// returns the number of bytes read
2013-02-22 03:55:48 +00:00
int rhizome_read ( struct rhizome_read * read_state , unsigned char * buffer , int buffer_length )
{
2013-02-16 09:55:26 +00:00
IN ( ) ;
2013-02-22 03:55:48 +00:00
int bytes_read = 0 ;
2013-06-18 06:58:26 +00:00
if ( read_state - > blob_fd > = 0 ) {
2013-02-22 03:55:48 +00:00
if ( lseek ( read_state - > blob_fd , read_state - > offset , SEEK_SET ) = = - 1 )
RETURN ( WHYF_perror ( " lseek(%d,%ld,SEEK_SET) " , read_state - > blob_fd , ( long ) read_state - > offset ) ) ;
2013-02-20 03:59:08 +00:00
bytes_read = read ( read_state - > blob_fd , buffer , buffer_length ) ;
2013-02-22 03:55:48 +00:00
if ( bytes_read = = - 1 )
RETURN ( WHYF_perror ( " read(%d,%p,%ld) " , read_state - > blob_fd , buffer , ( long ) buffer_length ) ) ;
2013-06-18 06:58:26 +00:00
if ( config . debug . externalblobs )
2013-07-15 00:29:24 +00:00
DEBUGF ( " Read %d bytes from fd %d @% " PRIx64 , bytes_read , read_state - > blob_fd , read_state - > offset ) ;
2013-02-22 03:55:48 +00:00
} else if ( read_state - > blob_rowid ! = - 1 ) {
2013-02-20 03:59:08 +00:00
sqlite_retry_state retry = SQLITE_RETRY_STATE_DEFAULT ;
do {
sqlite3_blob * blob = NULL ;
int ret = sqlite3_blob_open ( rhizome_db , " main " , " FILEBLOBS " , " data " , read_state - > blob_rowid , 0 /* read only */ , & blob ) ;
2012-12-28 01:04:22 +00:00
if ( sqlite_code_busy ( ret ) )
goto again ;
2013-02-20 03:59:08 +00:00
else if ( ret ! = SQLITE_OK )
RETURN ( WHYF ( " sqlite3_blob_open failed: %s " , sqlite3_errmsg ( rhizome_db ) ) ) ;
if ( read_state - > length = = - 1 )
read_state - > length = sqlite3_blob_bytes ( blob ) ;
bytes_read = read_state - > length - read_state - > offset ;
if ( bytes_read > buffer_length )
bytes_read = buffer_length ;
// allow the caller to do a dummy read, just to work out the length
if ( ! buffer )
bytes_read = 0 ;
if ( bytes_read > 0 ) {
ret = sqlite3_blob_read ( blob , buffer , bytes_read , read_state - > offset ) ;
if ( sqlite_code_busy ( ret ) )
goto again ;
else if ( ret ! = SQLITE_OK ) {
WHYF ( " sqlite3_blob_read failed: %s " , sqlite3_errmsg ( rhizome_db ) ) ;
sqlite3_blob_close ( blob ) ;
2013-02-20 06:52:53 +00:00
RETURN ( - 1 ) ;
2012-12-28 01:04:22 +00:00
}
}
2013-02-20 03:59:08 +00:00
sqlite3_blob_close ( blob ) ;
break ;
again :
if ( blob ) sqlite3_blob_close ( blob ) ;
2013-02-22 03:55:48 +00:00
if ( ! sqlite_retry ( & retry , " sqlite3_blob_open " ) )
2013-02-20 06:52:53 +00:00
RETURN ( - 1 ) ;
2013-02-22 03:55:48 +00:00
} while ( 1 ) ;
} else
RETURN ( WHY ( " file not open " ) ) ;
2013-02-20 03:59:08 +00:00
if ( read_state - > hash ) {
if ( buffer & & bytes_read > 0 )
SHA512_Update ( & read_state - > sha512_context , buffer , bytes_read ) ;
2013-08-02 04:32:56 +00:00
2013-02-20 03:59:08 +00:00
if ( read_state - > offset + bytes_read > = read_state - > length ) {
char hash_out [ SHA512_DIGEST_STRING_LENGTH + 1 ] ;
SHA512_End ( & read_state - > sha512_context , hash_out ) ;
if ( strcasecmp ( read_state - > id , hash_out ) ) {
2013-08-02 04:32:56 +00:00
RETURN ( WHYF ( " Expected hash=%s, got %s " , read_state - > id , hash_out ) ) ;
2013-02-20 03:59:08 +00:00
}
read_state - > hash = 0 ;
2012-12-28 01:04:22 +00:00
}
2013-02-20 03:59:08 +00:00
}
if ( read_state - > crypt & & buffer & & bytes_read > 0 ) {
2013-07-22 05:34:02 +00:00
if ( rhizome_crypt_xor_block (
buffer , bytes_read ,
read_state - > offset + read_state - > tail ,
read_state - > key , read_state - > nonce ) ) {
2013-02-16 09:55:26 +00:00
RETURN ( - 1 ) ;
2013-02-20 03:59:08 +00:00
}
}
read_state - > offset + = bytes_read ;
RETURN ( bytes_read ) ;
2013-02-16 09:55:26 +00:00
OUT ( ) ;
2012-12-20 04:48:59 +00:00
}
2013-01-02 02:23:00 +00:00
2013-07-25 05:12:30 +00:00
/* Read len bytes from read->offset into data, using *buffer to cache any reads */
int rhizome_read_buffered ( struct rhizome_read * read , struct rhizome_read_buffer * buffer , unsigned char * data , int len )
{
2013-07-26 05:06:11 +00:00
int bytes_copied = 0 ;
2013-07-25 05:12:30 +00:00
while ( len > 0 ) {
2013-07-26 05:06:11 +00:00
// make sure we only attempt to read data that actually exists
if ( read - > length ! = - 1 & & read - > offset + len > read - > length )
len = read - > length - read - > offset ;
2013-07-25 05:12:30 +00:00
// if we can supply either the beginning or end of the data from cache, do that first.
uint64_t ofs = read - > offset - buffer - > offset ;
if ( ofs > = 0 & & ofs < = buffer - > len ) {
int size = len ;
if ( size > buffer - > len - ofs )
size = buffer - > len - ofs ;
if ( size > 0 ) {
// copy into the start of the data buffer
bcopy ( buffer - > data + ofs , data , size ) ;
data + = size ;
len - = size ;
read - > offset + = size ;
2013-07-26 05:06:11 +00:00
bytes_copied + = size ;
2013-07-25 05:12:30 +00:00
continue ;
}
}
ofs = ( read - > offset + len ) - buffer - > offset ;
if ( ofs > 0 & & ofs < = buffer - > len ) {
int size = len ;
if ( size > ofs )
size = ofs ;
if ( size > 0 ) {
// copy into the end of the data buffer
bcopy ( buffer - > data + ofs - size , data + len - size , size ) ;
len - = size ;
2013-07-26 05:06:11 +00:00
bytes_copied + = size ;
2013-07-25 05:12:30 +00:00
continue ;
}
}
// ok, so we need to read a new buffer to fulfill the request.
// remember the requested read offset so we can put it back
ofs = read - > offset ;
buffer - > offset = read - > offset = ofs & ~ ( RHIZOME_CRYPT_PAGE_SIZE - 1 ) ;
buffer - > len = rhizome_read ( read , buffer - > data , sizeof ( buffer - > data ) ) ;
read - > offset = ofs ;
if ( buffer - > len < = 0 )
return buffer - > len ;
}
2013-07-26 05:06:11 +00:00
return bytes_copied ;
2013-07-25 05:12:30 +00:00
}
2013-02-22 03:55:48 +00:00
int rhizome_read_close ( struct rhizome_read * read )
{
2013-06-18 06:58:26 +00:00
if ( read - > blob_fd > = 0 ) {
if ( config . debug . externalblobs )
DEBUGF ( " Closing store fd %d " , read - > blob_fd ) ;
2013-02-20 03:59:08 +00:00
close ( read - > blob_fd ) ;
2013-06-18 06:58:26 +00:00
}
2013-02-22 03:55:48 +00:00
read - > blob_fd = - 1 ;
2013-02-20 03:59:08 +00:00
return 0 ;
}
2013-08-16 05:27:28 +00:00
struct cache_entry {
struct cache_entry * _left ;
struct cache_entry * _right ;
unsigned char bundle_id [ RHIZOME_MANIFEST_ID_BYTES ] ;
uint64_t version ;
struct rhizome_read read_state ;
time_ms_t expires ;
} ;
struct cache_entry * root ;
static struct cache_entry * * find_entry_location ( struct cache_entry * * ptr , unsigned char * bundle_id , uint64_t version )
{
while ( * ptr ) {
struct cache_entry * entry = * ptr ;
int cmp = memcmp ( bundle_id , entry - > bundle_id , sizeof entry - > bundle_id ) ;
if ( cmp = = 0 ) {
if ( entry - > version = = version )
break ;
if ( version < entry - > version )
ptr = & entry - > _left ;
else
ptr = & entry - > _right ;
continue ;
}
if ( cmp < 0 )
ptr = & entry - > _left ;
else
ptr = & entry - > _right ;
}
return ptr ;
}
static time_ms_t close_entries ( struct cache_entry * * entry , time_ms_t timeout )
{
if ( ! * entry )
return 0 ;
time_ms_t ret = close_entries ( & ( * entry ) - > _left , timeout ) ;
time_ms_t t_right = close_entries ( & ( * entry ) - > _right , timeout ) ;
if ( t_right ! = 0 & & ( t_right < ret | | ret = = 0 ) )
ret = t_right ;
if ( ( * entry ) - > expires < timeout | | timeout = = 0 ) {
rhizome_read_close ( & ( * entry ) - > read_state ) ;
// remember the two children
struct cache_entry * left = ( * entry ) - > _left ;
struct cache_entry * right = ( * entry ) - > _right ;
// free this entry
free ( * entry ) ;
// re-add both children to the tree
* entry = left ;
if ( right ) {
entry = find_entry_location ( entry , right - > bundle_id , right - > version ) ;
* entry = right ;
}
} else {
if ( ( * entry ) - > expires < ret | | ret = = 0 )
ret = ( * entry ) - > expires ;
}
return ret ;
}
// close any expired cache entries
static void rhizome_cache_alarm ( struct sched_ent * alarm )
{
alarm - > alarm = close_entries ( & root , gettime_ms ( ) ) ;
if ( alarm - > alarm ) {
alarm - > deadline = alarm - > alarm + 1000 ;
schedule ( alarm ) ;
}
}
static struct profile_total cache_alarm_stats = {
. name = " rhizome_cache_alarm " ,
} ;
static struct sched_ent cache_alarm = {
. function = rhizome_cache_alarm ,
. stats = & cache_alarm_stats ,
} ;
// close all cache entries
int rhizome_cache_close ( )
{
close_entries ( & root , 0 ) ;
unschedule ( & cache_alarm ) ;
return 0 ;
}
// read a block of data, caching meta data for reuse
int rhizome_read_cached ( unsigned char * bundle_id , uint64_t version , time_ms_t timeout ,
uint64_t fileOffset , unsigned char * buffer , int length )
{
// look for a cached entry
struct cache_entry * * ptr = find_entry_location ( & root , bundle_id , version ) ;
struct cache_entry * entry = * ptr ;
// if we don't have one yet, create one and open it
if ( ! entry ) {
char * id_str = alloca_tohex_bid ( bundle_id ) ;
char filehash [ SHA512_DIGEST_STRING_LENGTH ] ;
if ( rhizome_database_filehash_from_id ( id_str , version , filehash ) < = 0 )
return - 1 ;
entry = emalloc_zero ( sizeof ( struct cache_entry ) ) ;
if ( rhizome_open_read ( & entry - > read_state , filehash , 0 ) ) {
free ( entry ) ;
2013-08-20 23:51:40 +00:00
return WHYF ( " Payload %s not found " , filehash ) ;
2013-08-16 05:27:28 +00:00
}
bcopy ( bundle_id , entry - > bundle_id , sizeof ( entry - > bundle_id ) ) ;
entry - > version = version ;
* ptr = entry ;
}
entry - > read_state . offset = fileOffset ;
if ( entry - > read_state . length ! = - 1 & & fileOffset > = entry - > read_state . length )
return 0 ;
if ( entry - > expires < timeout ) {
entry - > expires = timeout ;
if ( ! cache_alarm . alarm ) {
cache_alarm . alarm = timeout ;
cache_alarm . deadline = timeout + 1000 ;
schedule ( & cache_alarm ) ;
}
}
return rhizome_read ( & entry - > read_state , buffer , length ) ;
}
2013-02-20 04:14:29 +00:00
/* Returns -1 on error, 0 on success.
*/
2013-01-02 02:23:00 +00:00
static int write_file ( struct rhizome_read * read , const char * filepath ) {
int fd = - 1 , ret = 0 ;
if ( filepath & & filepath [ 0 ] ) {
fd = open ( filepath , O_WRONLY | O_CREAT | O_TRUNC , 0775 ) ;
if ( fd = = - 1 )
return WHY_perror ( " open " ) ;
}
unsigned char buffer [ RHIZOME_CRYPT_PAGE_SIZE ] ;
while ( ( ret = rhizome_read ( read , buffer , sizeof ( buffer ) ) ) > 0 ) {
if ( fd ! = - 1 ) {
if ( write ( fd , buffer , ret ) ! = ret ) {
ret = WHY ( " Failed to write data to file " ) ;
break ;
}
}
}
if ( fd ! = - 1 ) {
if ( close ( fd ) = = - 1 )
ret = WHY_perror ( " close " ) ;
if ( ret < 0 ) {
// TODO delete partial file
}
}
return ret ;
}
2013-07-19 06:45:05 +00:00
static int read_derive_key ( rhizome_manifest * m , rhizome_bk_t * bsk , struct rhizome_read * read_state ) {
read_state - > crypt = m - > payloadEncryption ;
if ( read_state - > crypt ) {
// if the manifest specifies encryption, make sure we can generate the payload key and encrypt
// the contents as we go
if ( rhizome_derive_key ( m , bsk ) ) {
rhizome_read_close ( read_state ) ;
return WHY ( " Unable to decrypt bundle, valid key not found " ) ;
}
if ( config . debug . rhizome )
2013-07-26 05:06:11 +00:00
DEBUGF ( " Decrypting payload contents for %s, % " PRId64 , alloca_tohex_bid ( m - > cryptoSignPublic ) , m - > version ) ;
2013-07-22 05:34:02 +00:00
2013-07-26 05:43:22 +00:00
if ( m - > journalTail > 0 )
read_state - > tail = m - > journalTail ;
2013-07-19 06:45:05 +00:00
bcopy ( m - > payloadKey , read_state - > key , sizeof ( read_state - > key ) ) ;
bcopy ( m - > payloadNonce , read_state - > nonce , sizeof ( read_state - > nonce ) ) ;
}
return 0 ;
}
2013-02-20 03:59:08 +00:00
int rhizome_open_decrypt_read ( rhizome_manifest * m , rhizome_bk_t * bsk , struct rhizome_read * read_state , int hash ) {
2013-01-02 02:23:00 +00:00
// for now, always hash the file
2013-02-22 03:55:48 +00:00
int ret = rhizome_open_read ( read_state , m - > fileHexHash , hash ) ;
2013-07-19 06:45:05 +00:00
if ( ret = = 0 )
ret = read_derive_key ( m , bsk , read_state ) ;
2013-02-22 03:55:48 +00:00
return ret ;
2013-02-20 03:59:08 +00:00
}
2013-02-20 06:52:53 +00:00
/* Extract the file related to a manifest to the file system. The file will be de-crypted and
* verified while reading . If filepath is not supplied , the file will still be checked .
*
* Returns - 1 on error , 0 if extracted successfully , 1 if not found .
2013-02-20 03:59:08 +00:00
*/
2013-02-22 03:55:48 +00:00
int rhizome_extract_file ( rhizome_manifest * m , const char * filepath , rhizome_bk_t * bsk )
{
2013-02-20 03:59:08 +00:00
struct rhizome_read read_state ;
bzero ( & read_state , sizeof read_state ) ;
int ret = rhizome_open_decrypt_read ( m , bsk , & read_state , 1 ) ;
2013-02-22 03:55:48 +00:00
if ( ret = = 0 )
2013-02-20 03:59:08 +00:00
ret = write_file ( & read_state , filepath ) ;
rhizome_read_close ( & read_state ) ;
return ret ;
2013-01-02 02:23:00 +00:00
}
2013-02-20 04:14:29 +00:00
/* dump the raw contents of a file
*
* Returns - 1 on error , 0 if dumped successfully , 1 if not found .
*/
int rhizome_dump_file ( const char * id , const char * filepath , int64_t * length )
{
2013-01-02 02:23:00 +00:00
struct rhizome_read read_state ;
bzero ( & read_state , sizeof read_state ) ;
2013-02-20 03:59:08 +00:00
int ret = rhizome_open_read ( & read_state , id , 1 ) ;
2013-01-02 02:23:00 +00:00
2013-02-22 03:55:48 +00:00
if ( ret = = 0 ) {
ret = write_file ( & read_state , filepath ) ;
2013-02-20 03:59:08 +00:00
if ( length )
* length = read_state . length ;
}
rhizome_read_close ( & read_state ) ;
return ret ;
2013-01-02 02:23:00 +00:00
}
2013-07-05 06:54:50 +00:00
// pipe data from one payload to another
static int rhizome_pipe ( struct rhizome_read * read , struct rhizome_write * write , uint64_t length )
{
if ( length > write - > file_length - write - > file_offset )
return WHY ( " Unable to pipe that much data " ) ;
2013-07-17 06:17:35 +00:00
unsigned char buffer [ RHIZOME_CRYPT_PAGE_SIZE ] ;
2013-07-05 06:54:50 +00:00
while ( length > 0 ) {
2013-07-18 04:22:42 +00:00
int size = sizeof ( buffer ) ;
2013-07-05 06:54:50 +00:00
if ( size > length )
size = length ;
2013-07-17 06:17:35 +00:00
int r = rhizome_read ( read , buffer , size ) ;
2013-07-05 06:54:50 +00:00
if ( r < 0 )
return r ;
length - = r ;
2013-07-19 06:45:05 +00:00
2013-07-17 06:17:35 +00:00
if ( rhizome_write_buffer ( write , buffer , r ) )
2013-07-05 06:54:50 +00:00
return - 1 ;
}
return 0 ;
}
2013-08-15 04:23:37 +00:00
int rhizome_journal_pipe ( struct rhizome_write * write , const char * fileHash , uint64_t start_offset , uint64_t length )
{
struct rhizome_read read_state ;
bzero ( & read_state , sizeof read_state ) ;
if ( rhizome_open_read ( & read_state , fileHash , start_offset > 0 ? 0 : 1 ) )
return - 1 ;
read_state . offset = start_offset ;
int ret = rhizome_pipe ( & read_state , write , length ) ;
rhizome_read_close ( & read_state ) ;
return ret ;
}
2013-07-05 06:54:50 +00:00
// open an existing journal bundle, advance the head pointer, duplicate the existing content and get ready to add more.
int rhizome_write_open_journal ( struct rhizome_write * write , rhizome_manifest * m , rhizome_bk_t * bsk , uint64_t advance_by , uint64_t new_size )
{
int ret = 0 ;
2013-07-08 05:27:47 +00:00
2013-07-05 06:54:50 +00:00
if ( advance_by > m - > fileLength )
return WHY ( " Cannot advance past the existing content " ) ;
uint64_t old_length = m - > fileLength ;
uint64_t copy_length = old_length - advance_by ;
2013-07-22 05:34:02 +00:00
2013-07-05 06:54:50 +00:00
m - > fileLength = m - > fileLength + new_size - advance_by ;
rhizome_manifest_set_ll ( m , " filesize " , m - > fileLength ) ;
2013-07-08 05:27:47 +00:00
if ( advance_by > 0 ) {
m - > journalTail + = advance_by ;
rhizome_manifest_set_ll ( m , " tail " , m - > journalTail ) ;
}
2013-07-05 06:54:50 +00:00
m - > version = m - > fileLength ;
rhizome_manifest_set_ll ( m , " version " , m - > version ) ;
ret = rhizome_open_write ( write , NULL , m - > fileLength , RHIZOME_PRIORITY_DEFAULT ) ;
if ( ret )
goto failure ;
if ( copy_length > 0 ) {
2013-08-15 04:23:37 +00:00
// note that we don't need to bother decrypting the existing journal payload
ret = rhizome_journal_pipe ( write , m - > fileHexHash , advance_by , copy_length ) ;
2013-07-05 06:54:50 +00:00
if ( ret )
goto failure ;
}
2013-07-19 06:45:05 +00:00
ret = rhizome_write_derive_key ( m , bsk , write ) ;
if ( ret )
goto failure ;
2013-07-22 05:34:02 +00:00
2013-07-05 06:54:50 +00:00
return 0 ;
failure :
if ( ret )
rhizome_fail_write ( write ) ;
return ret ;
}
int rhizome_append_journal_buffer ( rhizome_manifest * m , rhizome_bk_t * bsk , uint64_t advance_by , unsigned char * buffer , int len )
{
struct rhizome_write write ;
bzero ( & write , sizeof write ) ;
int ret = rhizome_write_open_journal ( & write , m , bsk , advance_by , len ) ;
if ( ret )
return - 1 ;
if ( buffer & & len ) {
ret = rhizome_write_buffer ( & write , buffer , len ) ;
if ( ret )
goto failure ;
}
ret = rhizome_finish_write ( & write ) ;
if ( ret )
goto failure ;
strlcpy ( m - > fileHexHash , write . id , SHA512_DIGEST_STRING_LENGTH ) ;
rhizome_manifest_set ( m , " filehash " , m - > fileHexHash ) ;
return 0 ;
failure :
if ( ret )
rhizome_fail_write ( & write ) ;
return ret ;
}
int rhizome_append_journal_file ( rhizome_manifest * m , rhizome_bk_t * bsk , uint64_t advance_by , const char * filename )
{
struct stat stat ;
if ( lstat ( filename , & stat ) )
return WHYF ( " Could not stat() payload file ' % s ' " ,filename) ;
struct rhizome_write write ;
bzero ( & write , sizeof write ) ;
int ret = rhizome_write_open_journal ( & write , m , bsk , advance_by , stat . st_size ) ;
if ( ret )
return - 1 ;
if ( stat . st_size ) {
ret = rhizome_write_file ( & write , filename ) ;
if ( ret )
goto failure ;
}
ret = rhizome_finish_write ( & write ) ;
if ( ret )
goto failure ;
strlcpy ( m - > fileHexHash , write . id , SHA512_DIGEST_STRING_LENGTH ) ;
rhizome_manifest_set ( m , " filehash " , m - > fileHexHash ) ;
return 0 ;
failure :
if ( ret )
rhizome_fail_write ( & write ) ;
return ret ;
}