/* ** libMird by Mirar ** please submit bug reports and patches to the author ** ** also see http://www.mirar.org/mird/ ** ** for licence, read the LICENCE file ** ** $Id: mird.h.in,v 1.48 2001/03/26 12:34:04 mirar Exp $ ** */ /* ** ---------------------------------------------------------------- ** visible data structures ** ---------------------------------------------------------------- */ #define MIRD_VERSION "1.0.7" #define MIRD_BUILDER "@MIRD_BUILDER@" #define MIRD_BUILDER_TIME "@MIRD_BUILD_TIME@" typedef @UINT32@ mird_key_t; typedef @UINT32@ mird_size_t; struct transaction_id { mird_key_t msb; mird_key_t lsb; }; struct mird { /* -- public part; settings --------------------------------------- */ /* database flags */ #define MIRD_READONLY 0001 /* database in read-only mode */ #define MIRD_NOCREATE 0002 /* files must not be created */ #define MIRD_EXCL 0004 /* files must be created (opened with O_EXCL) */ #define MIRD_CALL_SYNC 0010 /* call sync(2) after fsync */ #define MIRD_SYNC_END 0020 /* fsync will be called at transaction end */ #define MIRD_LIVE 0100 /* live database; always check superblock */ /* complain about missing journal file */ #define MIRD_JO_COMPLAIN 0200 /* *don't* keep a hash table of all keys checked for usage * (this is a speedup with about 50%, but takes O[n]+O[m] in memory * for n = all keys written since last sync and m is all blocks that * might be freed, in opposite of only O[m] */ #define MIRD_NO_KEY_CHECKED_LIST 01000 /*** for internal use: ***/ #define MIRD_PLEASE_SYNC 0400 /* sync will be called when no transaction */ #define MIRD_DEFAULT_FLAGS 0 int flags; /* database block size; must be even 2^n * database size is limited to 2^32 blocks, ie block_size*2^32 * (8192*2^(32-5) is approximately 1Tb) */ #define MIRD_DEFAULT_BLOCK_SIZE 2048 @UINT32@ block_size; /* this sets the number of frags in a fragmented block, * and address bits to locate them. * the number of frags in a fragmented block is equal * to 2^frag_bits-1, per default 2^5-1=32-1=31 * * This steals bits from the 32 bit address used to point out * a chunk. */ #define MIRD_DEFAULT_FRAG_BITS 5 @UINT32@ frag_bits; /* this is the number of bits in each hash-trie hash; * 4*2^n must be a lot less then block_size */ #define MIRD_DEFAULT_HASHTRIE_BITS 5 @UINT32@ hashtrie_bits; /* this is the number of blocks cached from the database; * this is used for both read- and write cache. * Note that the memory usage is constant, * approximately block_size*cache_size bytes. */ #define MIRD_DEFAULT_CACHE_SIZE 256 @UINT32@ cache_size; /* this is the closed hash maximum search length to find * a block in the cache; note that this will result in * linear time usage. */ #define MIRD_DEFAULT_CACHE_SEARCH_LENGTH 8 unsigned int cache_search_length; /* this is how many blocks with free space that is * kept in a list to be used when a transaction * is running. The closest fit of usage of these blocks are * where the data will be stored. */ #define MIRD_DEFAULT_MAX_FREE_FRAG_BLOCKS 10 unsigned int max_free_frag_blocks; /* * this is the default mode the files are opened with; * although affected by UMASK */ #define MIRD_DEFAULT_FILE_MODE 0666 unsigned int file_mode; /* this is how many journal entries are read back * at once at transaction finish, cancel or copy time * bytes needed is approximately 24*this number */ #define MIRD_DEFAULT_JOURNAL_READBACK_N 512 unsigned int journal_readback_n; /* this is the number of journal entries in write cache * (note that this number doesn't affect the security of the * database; the journal is flushed every transaction end anyway) */ #define MIRD_DEFAULT_JOURNAL_WRITECACHE_N 128 unsigned int journal_writecache_n; /* -- private part ------------------------------------------------ */ /* strdup()ed base filename; * expanded to "", - the database * ".journal", - journal file * ".journal-new", - temporary new journal file */ char *filename; /* file descriptors */ int db_fd; /* database file descriptor */ int jo_fd; /* journal fd */ /* next transcation number */ struct transaction_id next_transaction; /* last commited transaction */ /* used for cache purposes */ struct transaction_id last_commited; /* cached stuff */ mird_key_t cache_table_id,cache_table_root,cache_table_type; /* disk cache */ unsigned char *cache; unsigned char *journal_cache; unsigned int journal_cache_n; /* temporary buffer */ unsigned char *buffer; /* high block used */ @UINT32@ last_used; /* tables hashtrie */ mird_key_t tables; /* free block lists */ struct mird_free_list { mird_key_t *blocks; /* current free block list */ mird_size_t n; /* number of free blocks in current list */ mird_key_t next; /* block of with more free list */ mird_key_t last; /* last block of free list */ } free_list, /* old free blocks (clean) */ new_free_list; /* new free blocks (dirty) */ /* from last superblock */ @UINT32@ last_last_used; mird_key_t last_free_list; mird_key_t last_tables; struct transaction_id last_next_transaction; /* running transactions */ struct mird_transaction *first_transaction; /* statistics: syscalls */ /* need SYSCALL_STATISTICS define to actually count */ int syscalls_counter[10]; }; struct mird_transaction { struct mird *db; struct mird_transaction *next; /* next transaction in our list */ struct transaction_id no; @OFF_T@ offset,cont_offset; mird_key_t tables; mird_key_t flags; #define MIRDT_CLOSED 0001 /* transaction is closed */ #define MIRDT_REWOUND_START 0002 /* rewind has started */ #define MIRDT_DEPENDENCIES 0004 /* we have dependencies, convert to locks */ /* remember this, maybe we can skip resolve */ struct transaction_id last_commited; /* cached stuff */ struct transaction_id cache_last_commited; mird_key_t cache_table_id,cache_table_root,cache_table_type; /* free shortcuts */ struct transaction_free_frags { mird_key_t block_no; long free_bytes; mird_key_t frag_no; /* first free frag */ mird_key_t table_id; } *free_frags; @UINT32@ n_free_frags; /* journal checksum */ @UINT32@ checksum; }; struct mird_error { int error_no; char *s; @UINT32@ x; @UINT32@ y; @UINT32@ z; }; struct mird_scan_result { mird_size_t n; struct mird_scan_tupel { mird_key_t key; unsigned char *value; mird_size_t value_len; } tupel[1]; }; struct mird_s_scan_result { struct mird_scan_result *msr; mird_size_t n; struct mird_s_scan_tupel { mird_size_t key_len; unsigned char *key; mird_size_t value_len; unsigned char *value; } tupel[1]; }; #define MIRD_TABLE_HASHKEY 0x686b6579 /* hashkey table */ #define MIRD_TABLE_STRINGKEY 0x736b6579 /* stringkey table */ #define MIRD_TABLE_MASTER 0x6d7a7472 /* not used physically */ #define MIRD_RES struct mird_error * #define MIRD_OK NULL /* invalid arguments */ #define MIRDE_INVALID_SETUP 100 /* invalid database parameters */ #define MIRDE_TR_CLOSED 102 /* transaction is closed */ #define MIRDE_TR_RUNNING 103 /* there is ongoing transactions */ #define MIRDE_READONLY 104 /* database is readonly, s=func */ #define MIRDE_SCAN_END 105 /* at end of scan, can't continue */ #define MIRDE_NO_TABLE 201 /* no such table, x=table id */ #define MIRDE_MIRD_TABLE_EXISTS 202 /* table exists already, x=table id */ #define MIRDE_WRONG_TABLE 203 /* wrong table type, x=table id */ /* file open/create/lock general errors, s=filename, x=errno */ #define MIRDE_OPEN 501 /* open failed */ #define MIRDE_OPEN_CREATE 502 /* open/create failed */ #define MIRDE_LOCK_FILE 503 /* didn't get lock on file*/ #define MIRDE_RM 504 /* failed to remove file */ /* special case returned from mird_open_file, * meaning the file was created (no other info) */ #define MIRDE_CREATED_FILE 600 /* database file errors, x=block no */ #define MIRDE_DB_LSEEK 1000 /* lseek failure, y=errno */ #define MIRDE_DB_READ 1001 /* read failure, y=errno */ #define MIRDE_DB_READ_SHORT 1002 /* read too few bytes, y=bytes, z=exp. */ #define MIRDE_DB_WRITE 1003 /* read failure, y=errno */ #define MIRDE_DB_WRITE_SHORT 1004 /* read too few bytes, y=bytes, z=exp. */ #define MIRDE_DB_READ_CHECKSUM 1005 /* checksum error in block read */ #define MIRDE_DB_STAT 1010 /* call to fstat failed, y=errno */ #define MIRDE_DB_SYNC 1011 /* call to fsync failed, y=errno */ #define MIRDE_DB_CLOSE 1012 /* call to close failed, y=errno */ /* these may be treated as GARBLED, x=block no */ #define MIRDE_WRONG_BLOCK 1100 /* wrong block type; y=exp, z=got */ #define MIRDE_ILLEGAL_FRAG 1101 /* try to read an illegal frag; y=frag */ #define MIRDE_ILLEGAL_FREE 1102 /* link to a non-freelist block */ #define MIRDE_WRONG_CHUNK 1103 /* x=chunk id, wrong type; y=exp, z=got */ #define MIRDE_CELL_SHORT 1104 /* x=chunk id, cell chain broken early */ #define MIRDE_SMALL_CHUNK 1105 /* x=chunk id, y=needed bytes, z=type */ /* skey errors */ #define MIRDE_DATA_MISSING 1301 /* skey block doesn't contain the data it's supposed to */ /* hashtrie errors; x=hashtrie root y=key z=current */ #define MIRDE_HASHTRIE_RECURSIVE 1150 /* too deep recursion (infinite?) */ #define MIRDE_HASHTRIE_AWAY 1151 /* led to key that doesn't match */ /* reading header, no other info */ #define MIRDE_INVAL_HEADER 1200 /* given file has an invalid header */ #define MIRDE_UNKNOWN_VERSION 1201 /* can't read that version of database */ #define MIRDE_GARBLED 1202 /* database garbled; need resurrection */ /* journal file errors */ #define MIRDE_JO_LSEEK 1500 /* lseek failure, y=errno */ #define MIRDE_JO_WRITE 1501 /* write failure, y=errno */ #define MIRDE_JO_WRITE_SHORT 1502 /* read too few bytes, y=bytes, z=exp. */ #define MIRDE_JO_TOO_BIG 1503 /* journal too big, sync it */ #define MIRDE_JO_READ 1504 /* read failure, x=pos, y=errno */ #define MIRDE_JO_MISSING_CANCEL 1505 /* missing cancel marker just written */ #define MIRDE_JO_SYNC 1506 /* fsync/fdatasync failed, y=errno */ /* reading back journal errors */ #define MIRDE_MISSING_JOURNAL 1203 /* dirty and missing journal file */ #define MIRDE_VERIFY_FAILED 1204 /* transaction verification failed */ /* resource errors */ #define MIRDE_RESOURCE_MEM 2000 /* out of memory; x=bytes needed */ #define MIRDE_RESOURCE_ERR 2001 /* failed to allocate error; x=b needed */ /* resolving conflicts, x=table_id y=key */ #define MIRDE_CONFLICT 3001 /* conflict */ #define MIRDE_DEPEND_BROKEN 3002 /* broken dependency */ #define MIRDE_CONFLICT_S 3003 /* conflict, s=key, z=len */ #define MIRDE_DEPEND_BROKEN_S 3004 /* broken dependency, s=key, z=len */ #define MIRDE_MIRD_TABLE_DEPEND_BROKEN 3005 /* broken table dependency, y=table */ #define MIRD_IS_CONFLICT(RES) \ ((RES) && (RES)->error_no>3000 && (RES)->error_no<3010) /*****************************************************************/ /* declarations of external use */ /*****************************************************************/ /*** database **********************************************************/ MIRD_RES mird_initialize(char *base_filename, struct mird **dest); MIRD_RES mird_open(struct mird *db); MIRD_RES mird_sync(struct mird *db); MIRD_RES mird_sync_please(struct mird *db); MIRD_RES mird_readonly_refresh(struct mird *db); MIRD_RES mird_close(struct mird *db); void mird_free_structure(struct mird *db); void mird_free(unsigned char *buffer); /*** transactions *****************************************************/ MIRD_RES mird_transaction_new(struct mird *db, struct mird_transaction **mtr); MIRD_RES mird_transaction_close(struct mird_transaction *mtr); MIRD_RES mird_transaction_cancel(struct mird_transaction *mtr); MIRD_RES mird_tr_resolve(struct mird_transaction *mtr); void mird_tr_free(struct mird_transaction *mtr); MIRD_RES mird_tr_rewind(struct mird_transaction *mtr); MIRD_RES mird_tr_finish(struct mird_transaction *mtr); /*** tables ************************************************************/ MIRD_RES mird_key_store(struct mird_transaction *mtr, mird_key_t table, mird_key_t key, unsigned char *value, mird_size_t len); MIRD_RES mird_transaction_key_lookup(struct mird_transaction *mtr, mird_key_t table_id, mird_key_t key, unsigned char **data, mird_size_t *len); MIRD_RES mird_key_lookup(struct mird *db, mird_key_t table, mird_key_t key, unsigned char **data, mird_size_t *len); MIRD_RES mird_key_new_table(struct mird_transaction *mtr, mird_key_t table_id); MIRD_RES mird_delete_table(struct mird_transaction *mtr, mird_key_t table_id); MIRD_RES mird_depend_table(struct mird_transaction *mtr, mird_key_t table_id); MIRD_RES mird_table_scan(struct mird *db, mird_key_t table_id, mird_size_t n, struct mird_scan_result *prev, struct mird_scan_result **dest); MIRD_RES mird_transaction_table_scan(struct mird_transaction *mtr, mird_key_t table_id, mird_size_t n, struct mird_scan_result *prev, struct mird_scan_result **dest); void mird_free_scan_result(struct mird_scan_result *msr); MIRD_RES mird_scan_continued(mird_key_t key,struct mird_scan_result **dest); MIRD_RES mird_scan_continuation(struct mird_scan_result *msr,mird_key_t *key); MIRD_RES mird_find_first_unused(struct mird *db, mird_key_t table_id, mird_key_t start_key, mird_key_t *dest_key); MIRD_RES mird_transaction_find_first_unused(struct mird_transaction *mtr, mird_key_t table_id, mird_key_t start_key, mird_key_t *dest_key); MIRD_RES mird_find_first_unused_table(struct mird *db, mird_key_t start_no, mird_key_t *dest_table_id); MIRD_RES mird_transaction_find_first_unused_table(struct mird_transaction *mtr, mird_key_t start_no, mird_key_t *dest_table_id); MIRD_RES mird_get_table_type(struct mird *db, mird_key_t table_id, mird_key_t *table_type); MIRD_RES mird_transaction_get_table_type(struct mird_transaction *mtr, mird_key_t table_id, mird_key_t *table_type); /*** stringkeys *******************************************************/ MIRD_RES mird_s_key_store(struct mird_transaction *mtr, mird_key_t table_id, unsigned char *skey, mird_size_t skey_len, unsigned char *value, mird_size_t len); MIRD_RES mird_transaction_s_key_lookup(struct mird_transaction *mtr, mird_key_t table_id, unsigned char *key, mird_size_t key_len, unsigned char **data, mird_size_t *len); MIRD_RES mird_s_key_lookup(struct mird *db, mird_key_t table_id, unsigned char *key, mird_size_t key_len, unsigned char **data, mird_size_t *len); MIRD_RES mird_s_key_new_table(struct mird_transaction *mtr, mird_key_t table_id); MIRD_RES mird_transaction_s_table_scan(struct mird_transaction *mtr, mird_key_t table_id, mird_size_t n, struct mird_s_scan_result *prev, struct mird_s_scan_result **dest); MIRD_RES mird_s_table_scan(struct mird *db, mird_key_t table_id, mird_size_t n, struct mird_s_scan_result *prev, struct mird_s_scan_result **dest); void mird_free_s_scan_result(struct mird_s_scan_result *mssr); MIRD_RES mird_s_scan_continued(mird_key_t key, struct mird_s_scan_result **dest); MIRD_RES mird_s_scan_continuation(struct mird_s_scan_result *msr, mird_key_t *key); /*** block cache ******************************************************/ MIRD_RES mird_cache_flush(struct mird *db); /*** debug ***********************************************************/ void mird_describe_block(struct mird *db,mird_key_t block_no); void mird_debug_check_free(struct mird *db,int silent); /*** errors ***********************************************************/ void mird_free_error(MIRD_RES error); void mird_describe_error(MIRD_RES error,char **dest); void mird_perror(char *prefix,MIRD_RES error);