diff options
Diffstat (limited to 'ctdb/lib/tdb')
65 files changed, 6474 insertions, 47 deletions
diff --git a/ctdb/lib/tdb/ABI/tdb-1.2.1.sigs b/ctdb/lib/tdb/ABI/tdb-1.2.1.sigs new file mode 100644 index 00000000000..84f200745e2 --- /dev/null +++ b/ctdb/lib/tdb/ABI/tdb-1.2.1.sigs @@ -0,0 +1,95 @@ +tdb_add_flags: void (struct tdb_context *, unsigned int) +tdb_alloc_read: unsigned char *(struct tdb_context *, tdb_off_t, tdb_len_t) +tdb_allocate: tdb_off_t (struct tdb_context *, tdb_len_t, struct tdb_record *) +tdb_allrecord_lock: int (struct tdb_context *, int, enum tdb_lock_flags, bool) +tdb_allrecord_unlock: int (struct tdb_context *, int, bool) +tdb_allrecord_upgrade: int (struct tdb_context *) +tdb_append: int (struct tdb_context *, TDB_DATA, TDB_DATA) +tdb_brlock: int (struct tdb_context *, int, tdb_off_t, size_t, enum tdb_lock_flags) +tdb_brunlock: int (struct tdb_context *, int, tdb_off_t, size_t) +tdb_chainlock: int (struct tdb_context *, TDB_DATA) +tdb_chainlock_mark: int (struct tdb_context *, TDB_DATA) +tdb_chainlock_nonblock: int (struct tdb_context *, TDB_DATA) +tdb_chainlock_read: int (struct tdb_context *, TDB_DATA) +tdb_chainlock_unmark: int (struct tdb_context *, TDB_DATA) +tdb_chainunlock: int (struct tdb_context *, TDB_DATA) +tdb_chainunlock_read: int (struct tdb_context *, TDB_DATA) +tdb_check: int (struct tdb_context *, int (*)(TDB_DATA, TDB_DATA, void *), void *) +tdb_close: int (struct tdb_context *) +tdb_convert: void *(void *, uint32_t) +tdb_delete: int (struct tdb_context *, TDB_DATA) +tdb_do_delete: int (struct tdb_context *, tdb_off_t, struct tdb_record *) +tdb_dump_all: void (struct tdb_context *) +tdb_enable_seqnum: void (struct tdb_context *) +tdb_error: enum TDB_ERROR (struct tdb_context *) +tdb_errorstr: const char *(struct tdb_context *) +tdb_exists: int (struct tdb_context *, TDB_DATA) +tdb_expand: int (struct tdb_context *, tdb_off_t) +tdb_fd: int (struct tdb_context *) +tdb_fetch: TDB_DATA (struct tdb_context *, TDB_DATA) +tdb_find_lock_hash: tdb_off_t (struct tdb_context *, TDB_DATA, uint32_t, int, struct tdb_record *) +tdb_firstkey: TDB_DATA (struct tdb_context *) +tdb_free: int (struct tdb_context *, tdb_off_t, struct tdb_record *) +tdb_freelist_size: int (struct tdb_context *) +tdb_get_flags: int (struct tdb_context *) +tdb_get_logging_private: void *(struct tdb_context *) +tdb_get_seqnum: int (struct tdb_context *) +tdb_hash_size: int (struct tdb_context *) +tdb_have_extra_locks: bool (struct tdb_context *) +tdb_increment_seqnum_nonblock: void (struct tdb_context *) +tdb_io_init: void (struct tdb_context *) +tdb_lock: int (struct tdb_context *, int, int) +tdb_lock_nonblock: int (struct tdb_context *, int, int) +tdb_lock_record: int (struct tdb_context *, tdb_off_t) +tdb_lockall: int (struct tdb_context *) +tdb_lockall_mark: int (struct tdb_context *) +tdb_lockall_nonblock: int (struct tdb_context *) +tdb_lockall_read: int (struct tdb_context *) +tdb_lockall_read_nonblock: int (struct tdb_context *) +tdb_lockall_unmark: int (struct tdb_context *) +tdb_log_fn: tdb_log_func (struct tdb_context *) +tdb_map_size: size_t (struct tdb_context *) +tdb_mmap: void (struct tdb_context *) +tdb_munmap: int (struct tdb_context *) +tdb_name: const char *(struct tdb_context *) +tdb_needs_recovery: bool (struct tdb_context *) +tdb_nest_lock: int (struct tdb_context *, uint32_t, int, enum tdb_lock_flags) +tdb_nest_unlock: int (struct tdb_context *, uint32_t, int, bool) +tdb_nextkey: TDB_DATA (struct tdb_context *, TDB_DATA) +tdb_ofs_read: int (struct tdb_context *, tdb_off_t, tdb_off_t *) +tdb_ofs_write: int (struct tdb_context *, tdb_off_t, tdb_off_t *) +tdb_open: struct tdb_context *(const char *, int, int, int, mode_t) +tdb_open_ex: struct tdb_context *(const char *, int, int, int, mode_t, const struct tdb_logging_context *, tdb_hash_func) +tdb_parse_data: int (struct tdb_context *, TDB_DATA, tdb_off_t, tdb_len_t, int (*)(TDB_DATA, TDB_DATA, void *), void *) +tdb_parse_record: int (struct tdb_context *, TDB_DATA, int (*)(TDB_DATA, TDB_DATA, void *), void *) +tdb_printfreelist: int (struct tdb_context *) +tdb_rec_free_read: int (struct tdb_context *, tdb_off_t, struct tdb_record *) +tdb_rec_read: int (struct tdb_context *, tdb_off_t, struct tdb_record *) +tdb_rec_write: int (struct tdb_context *, tdb_off_t, struct tdb_record *) +tdb_release_transaction_locks: void (struct tdb_context *) +tdb_remove_flags: void (struct tdb_context *, unsigned int) +tdb_reopen: int (struct tdb_context *) +tdb_reopen_all: int (int) +tdb_repack: int (struct tdb_context *) +tdb_set_logging_function: void (struct tdb_context *, const struct tdb_logging_context *) +tdb_set_max_dead: void (struct tdb_context *, int) +tdb_setalarm_sigptr: void (struct tdb_context *, volatile sig_atomic_t *) +tdb_store: int (struct tdb_context *, TDB_DATA, TDB_DATA, int) +tdb_transaction_cancel: int (struct tdb_context *) +tdb_transaction_commit: int (struct tdb_context *) +tdb_transaction_lock: int (struct tdb_context *, int, enum tdb_lock_flags) +tdb_transaction_prepare_commit: int (struct tdb_context *) +tdb_transaction_recover: int (struct tdb_context *) +tdb_transaction_start: int (struct tdb_context *) +tdb_transaction_start_nonblock: int (struct tdb_context *) +tdb_transaction_unlock: int (struct tdb_context *, int) +tdb_traverse: int (struct tdb_context *, tdb_traverse_func, void *) +tdb_traverse_read: int (struct tdb_context *, tdb_traverse_func, void *) +tdb_unlock: int (struct tdb_context *, int, int) +tdb_unlock_record: int (struct tdb_context *, tdb_off_t) +tdb_unlockall: int (struct tdb_context *) +tdb_unlockall_read: int (struct tdb_context *) +tdb_validate_freelist: int (struct tdb_context *, int *) +tdb_wipe_all: int (struct tdb_context *) +tdb_write_lock_record: int (struct tdb_context *, tdb_off_t) +tdb_write_unlock_record: int (struct tdb_context *, tdb_off_t) diff --git a/ctdb/lib/tdb/ABI/tdb-1.2.11.sigs b/ctdb/lib/tdb/ABI/tdb-1.2.11.sigs new file mode 100644 index 00000000000..d727f2163dd --- /dev/null +++ b/ctdb/lib/tdb/ABI/tdb-1.2.11.sigs @@ -0,0 +1,67 @@ +tdb_add_flags: void (struct tdb_context *, unsigned int) +tdb_append: int (struct tdb_context *, TDB_DATA, TDB_DATA) +tdb_chainlock: int (struct tdb_context *, TDB_DATA) +tdb_chainlock_mark: int (struct tdb_context *, TDB_DATA) +tdb_chainlock_nonblock: int (struct tdb_context *, TDB_DATA) +tdb_chainlock_read: int (struct tdb_context *, TDB_DATA) +tdb_chainlock_unmark: int (struct tdb_context *, TDB_DATA) +tdb_chainunlock: int (struct tdb_context *, TDB_DATA) +tdb_chainunlock_read: int (struct tdb_context *, TDB_DATA) +tdb_check: int (struct tdb_context *, int (*)(TDB_DATA, TDB_DATA, void *), void *) +tdb_close: int (struct tdb_context *) +tdb_delete: int (struct tdb_context *, TDB_DATA) +tdb_dump_all: void (struct tdb_context *) +tdb_enable_seqnum: void (struct tdb_context *) +tdb_error: enum TDB_ERROR (struct tdb_context *) +tdb_errorstr: const char *(struct tdb_context *) +tdb_exists: int (struct tdb_context *, TDB_DATA) +tdb_fd: int (struct tdb_context *) +tdb_fetch: TDB_DATA (struct tdb_context *, TDB_DATA) +tdb_firstkey: TDB_DATA (struct tdb_context *) +tdb_freelist_size: int (struct tdb_context *) +tdb_get_flags: int (struct tdb_context *) +tdb_get_logging_private: void *(struct tdb_context *) +tdb_get_seqnum: int (struct tdb_context *) +tdb_hash_size: int (struct tdb_context *) +tdb_increment_seqnum_nonblock: void (struct tdb_context *) +tdb_jenkins_hash: unsigned int (TDB_DATA *) +tdb_lock_nonblock: int (struct tdb_context *, int, int) +tdb_lockall: int (struct tdb_context *) +tdb_lockall_mark: int (struct tdb_context *) +tdb_lockall_nonblock: int (struct tdb_context *) +tdb_lockall_read: int (struct tdb_context *) +tdb_lockall_read_nonblock: int (struct tdb_context *) +tdb_lockall_unmark: int (struct tdb_context *) +tdb_log_fn: tdb_log_func (struct tdb_context *) +tdb_map_size: size_t (struct tdb_context *) +tdb_name: const char *(struct tdb_context *) +tdb_nextkey: TDB_DATA (struct tdb_context *, TDB_DATA) +tdb_null: dptr = 0xXXXX, dsize = 0 +tdb_open: struct tdb_context *(const char *, int, int, int, mode_t) +tdb_open_ex: struct tdb_context *(const char *, int, int, int, mode_t, const struct tdb_logging_context *, tdb_hash_func) +tdb_parse_record: int (struct tdb_context *, TDB_DATA, int (*)(TDB_DATA, TDB_DATA, void *), void *) +tdb_printfreelist: int (struct tdb_context *) +tdb_remove_flags: void (struct tdb_context *, unsigned int) +tdb_reopen: int (struct tdb_context *) +tdb_reopen_all: int (int) +tdb_repack: int (struct tdb_context *) +tdb_rescue: int (struct tdb_context *, void (*)(TDB_DATA, TDB_DATA, void *), void *) +tdb_set_logging_function: void (struct tdb_context *, const struct tdb_logging_context *) +tdb_set_max_dead: void (struct tdb_context *, int) +tdb_setalarm_sigptr: void (struct tdb_context *, volatile sig_atomic_t *) +tdb_store: int (struct tdb_context *, TDB_DATA, TDB_DATA, int) +tdb_summary: char *(struct tdb_context *) +tdb_transaction_cancel: int (struct tdb_context *) +tdb_transaction_commit: int (struct tdb_context *) +tdb_transaction_prepare_commit: int (struct tdb_context *) +tdb_transaction_start: int (struct tdb_context *) +tdb_transaction_start_nonblock: int (struct tdb_context *) +tdb_transaction_write_lock_mark: int (struct tdb_context *) +tdb_transaction_write_lock_unmark: int (struct tdb_context *) +tdb_traverse: int (struct tdb_context *, tdb_traverse_func, void *) +tdb_traverse_read: int (struct tdb_context *, tdb_traverse_func, void *) +tdb_unlock: int (struct tdb_context *, int, int) +tdb_unlockall: int (struct tdb_context *) +tdb_unlockall_read: int (struct tdb_context *) +tdb_validate_freelist: int (struct tdb_context *, int *) +tdb_wipe_all: int (struct tdb_context *) diff --git a/ctdb/lib/tdb/ABI/tdb-1.2.2.sigs b/ctdb/lib/tdb/ABI/tdb-1.2.2.sigs new file mode 100644 index 00000000000..043790d27e6 --- /dev/null +++ b/ctdb/lib/tdb/ABI/tdb-1.2.2.sigs @@ -0,0 +1,60 @@ +tdb_add_flags: void (struct tdb_context *, unsigned int) +tdb_append: int (struct tdb_context *, TDB_DATA, TDB_DATA) +tdb_chainlock: int (struct tdb_context *, TDB_DATA) +tdb_chainlock_mark: int (struct tdb_context *, TDB_DATA) +tdb_chainlock_nonblock: int (struct tdb_context *, TDB_DATA) +tdb_chainlock_read: int (struct tdb_context *, TDB_DATA) +tdb_chainlock_unmark: int (struct tdb_context *, TDB_DATA) +tdb_chainunlock: int (struct tdb_context *, TDB_DATA) +tdb_chainunlock_read: int (struct tdb_context *, TDB_DATA) +tdb_check: int (struct tdb_context *, int (*)(TDB_DATA, TDB_DATA, void *), void *) +tdb_close: int (struct tdb_context *) +tdb_delete: int (struct tdb_context *, TDB_DATA) +tdb_dump_all: void (struct tdb_context *) +tdb_enable_seqnum: void (struct tdb_context *) +tdb_error: enum TDB_ERROR (struct tdb_context *) +tdb_errorstr: const char *(struct tdb_context *) +tdb_exists: int (struct tdb_context *, TDB_DATA) +tdb_fd: int (struct tdb_context *) +tdb_fetch: TDB_DATA (struct tdb_context *, TDB_DATA) +tdb_firstkey: TDB_DATA (struct tdb_context *) +tdb_freelist_size: int (struct tdb_context *) +tdb_get_flags: int (struct tdb_context *) +tdb_get_logging_private: void *(struct tdb_context *) +tdb_get_seqnum: int (struct tdb_context *) +tdb_hash_size: int (struct tdb_context *) +tdb_increment_seqnum_nonblock: void (struct tdb_context *) +tdb_lockall: int (struct tdb_context *) +tdb_lockall_mark: int (struct tdb_context *) +tdb_lockall_nonblock: int (struct tdb_context *) +tdb_lockall_read: int (struct tdb_context *) +tdb_lockall_read_nonblock: int (struct tdb_context *) +tdb_lockall_unmark: int (struct tdb_context *) +tdb_log_fn: tdb_log_func (struct tdb_context *) +tdb_map_size: size_t (struct tdb_context *) +tdb_name: const char *(struct tdb_context *) +tdb_nextkey: TDB_DATA (struct tdb_context *, TDB_DATA) +tdb_null: dptr = 0xXXXX, dsize = 0 +tdb_open: struct tdb_context *(const char *, int, int, int, mode_t) +tdb_open_ex: struct tdb_context *(const char *, int, int, int, mode_t, const struct tdb_logging_context *, tdb_hash_func) +tdb_parse_record: int (struct tdb_context *, TDB_DATA, int (*)(TDB_DATA, TDB_DATA, void *), void *) +tdb_printfreelist: int (struct tdb_context *) +tdb_remove_flags: void (struct tdb_context *, unsigned int) +tdb_reopen: int (struct tdb_context *) +tdb_reopen_all: int (int) +tdb_repack: int (struct tdb_context *) +tdb_set_logging_function: void (struct tdb_context *, const struct tdb_logging_context *) +tdb_set_max_dead: void (struct tdb_context *, int) +tdb_setalarm_sigptr: void (struct tdb_context *, volatile sig_atomic_t *) +tdb_store: int (struct tdb_context *, TDB_DATA, TDB_DATA, int) +tdb_transaction_cancel: int (struct tdb_context *) +tdb_transaction_commit: int (struct tdb_context *) +tdb_transaction_prepare_commit: int (struct tdb_context *) +tdb_transaction_start: int (struct tdb_context *) +tdb_transaction_start_nonblock: int (struct tdb_context *) +tdb_traverse: int (struct tdb_context *, tdb_traverse_func, void *) +tdb_traverse_read: int (struct tdb_context *, tdb_traverse_func, void *) +tdb_unlockall: int (struct tdb_context *) +tdb_unlockall_read: int (struct tdb_context *) +tdb_validate_freelist: int (struct tdb_context *, int *) +tdb_wipe_all: int (struct tdb_context *) diff --git a/ctdb/lib/tdb/ABI/tdb-1.2.3.sigs b/ctdb/lib/tdb/ABI/tdb-1.2.3.sigs new file mode 100644 index 00000000000..043790d27e6 --- /dev/null +++ b/ctdb/lib/tdb/ABI/tdb-1.2.3.sigs @@ -0,0 +1,60 @@ +tdb_add_flags: void (struct tdb_context *, unsigned int) +tdb_append: int (struct tdb_context *, TDB_DATA, TDB_DATA) +tdb_chainlock: int (struct tdb_context *, TDB_DATA) +tdb_chainlock_mark: int (struct tdb_context *, TDB_DATA) +tdb_chainlock_nonblock: int (struct tdb_context *, TDB_DATA) +tdb_chainlock_read: int (struct tdb_context *, TDB_DATA) +tdb_chainlock_unmark: int (struct tdb_context *, TDB_DATA) +tdb_chainunlock: int (struct tdb_context *, TDB_DATA) +tdb_chainunlock_read: int (struct tdb_context *, TDB_DATA) +tdb_check: int (struct tdb_context *, int (*)(TDB_DATA, TDB_DATA, void *), void *) +tdb_close: int (struct tdb_context *) +tdb_delete: int (struct tdb_context *, TDB_DATA) +tdb_dump_all: void (struct tdb_context *) +tdb_enable_seqnum: void (struct tdb_context *) +tdb_error: enum TDB_ERROR (struct tdb_context *) +tdb_errorstr: const char *(struct tdb_context *) +tdb_exists: int (struct tdb_context *, TDB_DATA) +tdb_fd: int (struct tdb_context *) +tdb_fetch: TDB_DATA (struct tdb_context *, TDB_DATA) +tdb_firstkey: TDB_DATA (struct tdb_context *) +tdb_freelist_size: int (struct tdb_context *) +tdb_get_flags: int (struct tdb_context *) +tdb_get_logging_private: void *(struct tdb_context *) +tdb_get_seqnum: int (struct tdb_context *) +tdb_hash_size: int (struct tdb_context *) +tdb_increment_seqnum_nonblock: void (struct tdb_context *) +tdb_lockall: int (struct tdb_context *) +tdb_lockall_mark: int (struct tdb_context *) +tdb_lockall_nonblock: int (struct tdb_context *) +tdb_lockall_read: int (struct tdb_context *) +tdb_lockall_read_nonblock: int (struct tdb_context *) +tdb_lockall_unmark: int (struct tdb_context *) +tdb_log_fn: tdb_log_func (struct tdb_context *) +tdb_map_size: size_t (struct tdb_context *) +tdb_name: const char *(struct tdb_context *) +tdb_nextkey: TDB_DATA (struct tdb_context *, TDB_DATA) +tdb_null: dptr = 0xXXXX, dsize = 0 +tdb_open: struct tdb_context *(const char *, int, int, int, mode_t) +tdb_open_ex: struct tdb_context *(const char *, int, int, int, mode_t, const struct tdb_logging_context *, tdb_hash_func) +tdb_parse_record: int (struct tdb_context *, TDB_DATA, int (*)(TDB_DATA, TDB_DATA, void *), void *) +tdb_printfreelist: int (struct tdb_context *) +tdb_remove_flags: void (struct tdb_context *, unsigned int) +tdb_reopen: int (struct tdb_context *) +tdb_reopen_all: int (int) +tdb_repack: int (struct tdb_context *) +tdb_set_logging_function: void (struct tdb_context *, const struct tdb_logging_context *) +tdb_set_max_dead: void (struct tdb_context *, int) +tdb_setalarm_sigptr: void (struct tdb_context *, volatile sig_atomic_t *) +tdb_store: int (struct tdb_context *, TDB_DATA, TDB_DATA, int) +tdb_transaction_cancel: int (struct tdb_context *) +tdb_transaction_commit: int (struct tdb_context *) +tdb_transaction_prepare_commit: int (struct tdb_context *) +tdb_transaction_start: int (struct tdb_context *) +tdb_transaction_start_nonblock: int (struct tdb_context *) +tdb_traverse: int (struct tdb_context *, tdb_traverse_func, void *) +tdb_traverse_read: int (struct tdb_context *, tdb_traverse_func, void *) +tdb_unlockall: int (struct tdb_context *) +tdb_unlockall_read: int (struct tdb_context *) +tdb_validate_freelist: int (struct tdb_context *, int *) +tdb_wipe_all: int (struct tdb_context *) diff --git a/ctdb/lib/tdb/ABI/tdb-1.2.4.sigs b/ctdb/lib/tdb/ABI/tdb-1.2.4.sigs new file mode 100644 index 00000000000..043790d27e6 --- /dev/null +++ b/ctdb/lib/tdb/ABI/tdb-1.2.4.sigs @@ -0,0 +1,60 @@ +tdb_add_flags: void (struct tdb_context *, unsigned int) +tdb_append: int (struct tdb_context *, TDB_DATA, TDB_DATA) +tdb_chainlock: int (struct tdb_context *, TDB_DATA) +tdb_chainlock_mark: int (struct tdb_context *, TDB_DATA) +tdb_chainlock_nonblock: int (struct tdb_context *, TDB_DATA) +tdb_chainlock_read: int (struct tdb_context *, TDB_DATA) +tdb_chainlock_unmark: int (struct tdb_context *, TDB_DATA) +tdb_chainunlock: int (struct tdb_context *, TDB_DATA) +tdb_chainunlock_read: int (struct tdb_context *, TDB_DATA) +tdb_check: int (struct tdb_context *, int (*)(TDB_DATA, TDB_DATA, void *), void *) +tdb_close: int (struct tdb_context *) +tdb_delete: int (struct tdb_context *, TDB_DATA) +tdb_dump_all: void (struct tdb_context *) +tdb_enable_seqnum: void (struct tdb_context *) +tdb_error: enum TDB_ERROR (struct tdb_context *) +tdb_errorstr: const char *(struct tdb_context *) +tdb_exists: int (struct tdb_context *, TDB_DATA) +tdb_fd: int (struct tdb_context *) +tdb_fetch: TDB_DATA (struct tdb_context *, TDB_DATA) +tdb_firstkey: TDB_DATA (struct tdb_context *) +tdb_freelist_size: int (struct tdb_context *) +tdb_get_flags: int (struct tdb_context *) +tdb_get_logging_private: void *(struct tdb_context *) +tdb_get_seqnum: int (struct tdb_context *) +tdb_hash_size: int (struct tdb_context *) +tdb_increment_seqnum_nonblock: void (struct tdb_context *) +tdb_lockall: int (struct tdb_context *) +tdb_lockall_mark: int (struct tdb_context *) +tdb_lockall_nonblock: int (struct tdb_context *) +tdb_lockall_read: int (struct tdb_context *) +tdb_lockall_read_nonblock: int (struct tdb_context *) +tdb_lockall_unmark: int (struct tdb_context *) +tdb_log_fn: tdb_log_func (struct tdb_context *) +tdb_map_size: size_t (struct tdb_context *) +tdb_name: const char *(struct tdb_context *) +tdb_nextkey: TDB_DATA (struct tdb_context *, TDB_DATA) +tdb_null: dptr = 0xXXXX, dsize = 0 +tdb_open: struct tdb_context *(const char *, int, int, int, mode_t) +tdb_open_ex: struct tdb_context *(const char *, int, int, int, mode_t, const struct tdb_logging_context *, tdb_hash_func) +tdb_parse_record: int (struct tdb_context *, TDB_DATA, int (*)(TDB_DATA, TDB_DATA, void *), void *) +tdb_printfreelist: int (struct tdb_context *) +tdb_remove_flags: void (struct tdb_context *, unsigned int) +tdb_reopen: int (struct tdb_context *) +tdb_reopen_all: int (int) +tdb_repack: int (struct tdb_context *) +tdb_set_logging_function: void (struct tdb_context *, const struct tdb_logging_context *) +tdb_set_max_dead: void (struct tdb_context *, int) +tdb_setalarm_sigptr: void (struct tdb_context *, volatile sig_atomic_t *) +tdb_store: int (struct tdb_context *, TDB_DATA, TDB_DATA, int) +tdb_transaction_cancel: int (struct tdb_context *) +tdb_transaction_commit: int (struct tdb_context *) +tdb_transaction_prepare_commit: int (struct tdb_context *) +tdb_transaction_start: int (struct tdb_context *) +tdb_transaction_start_nonblock: int (struct tdb_context *) +tdb_traverse: int (struct tdb_context *, tdb_traverse_func, void *) +tdb_traverse_read: int (struct tdb_context *, tdb_traverse_func, void *) +tdb_unlockall: int (struct tdb_context *) +tdb_unlockall_read: int (struct tdb_context *) +tdb_validate_freelist: int (struct tdb_context *, int *) +tdb_wipe_all: int (struct tdb_context *) diff --git a/ctdb/lib/tdb/ABI/tdb-1.2.5.sigs b/ctdb/lib/tdb/ABI/tdb-1.2.5.sigs new file mode 100644 index 00000000000..1e01f3ba24b --- /dev/null +++ b/ctdb/lib/tdb/ABI/tdb-1.2.5.sigs @@ -0,0 +1,61 @@ +tdb_add_flags: void (struct tdb_context *, unsigned int) +tdb_append: int (struct tdb_context *, TDB_DATA, TDB_DATA) +tdb_chainlock: int (struct tdb_context *, TDB_DATA) +tdb_chainlock_mark: int (struct tdb_context *, TDB_DATA) +tdb_chainlock_nonblock: int (struct tdb_context *, TDB_DATA) +tdb_chainlock_read: int (struct tdb_context *, TDB_DATA) +tdb_chainlock_unmark: int (struct tdb_context *, TDB_DATA) +tdb_chainunlock: int (struct tdb_context *, TDB_DATA) +tdb_chainunlock_read: int (struct tdb_context *, TDB_DATA) +tdb_check: int (struct tdb_context *, int (*)(TDB_DATA, TDB_DATA, void *), void *) +tdb_close: int (struct tdb_context *) +tdb_delete: int (struct tdb_context *, TDB_DATA) +tdb_dump_all: void (struct tdb_context *) +tdb_enable_seqnum: void (struct tdb_context *) +tdb_error: enum TDB_ERROR (struct tdb_context *) +tdb_errorstr: const char *(struct tdb_context *) +tdb_exists: int (struct tdb_context *, TDB_DATA) +tdb_fd: int (struct tdb_context *) +tdb_fetch: TDB_DATA (struct tdb_context *, TDB_DATA) +tdb_firstkey: TDB_DATA (struct tdb_context *) +tdb_freelist_size: int (struct tdb_context *) +tdb_get_flags: int (struct tdb_context *) +tdb_get_logging_private: void *(struct tdb_context *) +tdb_get_seqnum: int (struct tdb_context *) +tdb_hash_size: int (struct tdb_context *) +tdb_increment_seqnum_nonblock: void (struct tdb_context *) +tdb_jenkins_hash: unsigned int (TDB_DATA *) +tdb_lockall: int (struct tdb_context *) +tdb_lockall_mark: int (struct tdb_context *) +tdb_lockall_nonblock: int (struct tdb_context *) +tdb_lockall_read: int (struct tdb_context *) +tdb_lockall_read_nonblock: int (struct tdb_context *) +tdb_lockall_unmark: int (struct tdb_context *) +tdb_log_fn: tdb_log_func (struct tdb_context *) +tdb_map_size: size_t (struct tdb_context *) +tdb_name: const char *(struct tdb_context *) +tdb_nextkey: TDB_DATA (struct tdb_context *, TDB_DATA) +tdb_null: dptr = 0xXXXX, dsize = 0 +tdb_open: struct tdb_context *(const char *, int, int, int, mode_t) +tdb_open_ex: struct tdb_context *(const char *, int, int, int, mode_t, const struct tdb_logging_context *, tdb_hash_func) +tdb_parse_record: int (struct tdb_context *, TDB_DATA, int (*)(TDB_DATA, TDB_DATA, void *), void *) +tdb_printfreelist: int (struct tdb_context *) +tdb_remove_flags: void (struct tdb_context *, unsigned int) +tdb_reopen: int (struct tdb_context *) +tdb_reopen_all: int (int) +tdb_repack: int (struct tdb_context *) +tdb_set_logging_function: void (struct tdb_context *, const struct tdb_logging_context *) +tdb_set_max_dead: void (struct tdb_context *, int) +tdb_setalarm_sigptr: void (struct tdb_context *, volatile sig_atomic_t *) +tdb_store: int (struct tdb_context *, TDB_DATA, TDB_DATA, int) +tdb_transaction_cancel: int (struct tdb_context *) +tdb_transaction_commit: int (struct tdb_context *) +tdb_transaction_prepare_commit: int (struct tdb_context *) +tdb_transaction_start: int (struct tdb_context *) +tdb_transaction_start_nonblock: int (struct tdb_context *) +tdb_traverse: int (struct tdb_context *, tdb_traverse_func, void *) +tdb_traverse_read: int (struct tdb_context *, tdb_traverse_func, void *) +tdb_unlockall: int (struct tdb_context *) +tdb_unlockall_read: int (struct tdb_context *) +tdb_validate_freelist: int (struct tdb_context *, int *) +tdb_wipe_all: int (struct tdb_context *) diff --git a/ctdb/lib/tdb/ABI/tdb-1.2.6.sigs b/ctdb/lib/tdb/ABI/tdb-1.2.6.sigs new file mode 100644 index 00000000000..1e01f3ba24b --- /dev/null +++ b/ctdb/lib/tdb/ABI/tdb-1.2.6.sigs @@ -0,0 +1,61 @@ +tdb_add_flags: void (struct tdb_context *, unsigned int) +tdb_append: int (struct tdb_context *, TDB_DATA, TDB_DATA) +tdb_chainlock: int (struct tdb_context *, TDB_DATA) +tdb_chainlock_mark: int (struct tdb_context *, TDB_DATA) +tdb_chainlock_nonblock: int (struct tdb_context *, TDB_DATA) +tdb_chainlock_read: int (struct tdb_context *, TDB_DATA) +tdb_chainlock_unmark: int (struct tdb_context *, TDB_DATA) +tdb_chainunlock: int (struct tdb_context *, TDB_DATA) +tdb_chainunlock_read: int (struct tdb_context *, TDB_DATA) +tdb_check: int (struct tdb_context *, int (*)(TDB_DATA, TDB_DATA, void *), void *) +tdb_close: int (struct tdb_context *) +tdb_delete: int (struct tdb_context *, TDB_DATA) +tdb_dump_all: void (struct tdb_context *) +tdb_enable_seqnum: void (struct tdb_context *) +tdb_error: enum TDB_ERROR (struct tdb_context *) +tdb_errorstr: const char *(struct tdb_context *) +tdb_exists: int (struct tdb_context *, TDB_DATA) +tdb_fd: int (struct tdb_context *) +tdb_fetch: TDB_DATA (struct tdb_context *, TDB_DATA) +tdb_firstkey: TDB_DATA (struct tdb_context *) +tdb_freelist_size: int (struct tdb_context *) +tdb_get_flags: int (struct tdb_context *) +tdb_get_logging_private: void *(struct tdb_context *) +tdb_get_seqnum: int (struct tdb_context *) +tdb_hash_size: int (struct tdb_context *) +tdb_increment_seqnum_nonblock: void (struct tdb_context *) +tdb_jenkins_hash: unsigned int (TDB_DATA *) +tdb_lockall: int (struct tdb_context *) +tdb_lockall_mark: int (struct tdb_context *) +tdb_lockall_nonblock: int (struct tdb_context *) +tdb_lockall_read: int (struct tdb_context *) +tdb_lockall_read_nonblock: int (struct tdb_context *) +tdb_lockall_unmark: int (struct tdb_context *) +tdb_log_fn: tdb_log_func (struct tdb_context *) +tdb_map_size: size_t (struct tdb_context *) +tdb_name: const char *(struct tdb_context *) +tdb_nextkey: TDB_DATA (struct tdb_context *, TDB_DATA) +tdb_null: dptr = 0xXXXX, dsize = 0 +tdb_open: struct tdb_context *(const char *, int, int, int, mode_t) +tdb_open_ex: struct tdb_context *(const char *, int, int, int, mode_t, const struct tdb_logging_context *, tdb_hash_func) +tdb_parse_record: int (struct tdb_context *, TDB_DATA, int (*)(TDB_DATA, TDB_DATA, void *), void *) +tdb_printfreelist: int (struct tdb_context *) +tdb_remove_flags: void (struct tdb_context *, unsigned int) +tdb_reopen: int (struct tdb_context *) +tdb_reopen_all: int (int) +tdb_repack: int (struct tdb_context *) +tdb_set_logging_function: void (struct tdb_context *, const struct tdb_logging_context *) +tdb_set_max_dead: void (struct tdb_context *, int) +tdb_setalarm_sigptr: void (struct tdb_context *, volatile sig_atomic_t *) +tdb_store: int (struct tdb_context *, TDB_DATA, TDB_DATA, int) +tdb_transaction_cancel: int (struct tdb_context *) +tdb_transaction_commit: int (struct tdb_context *) +tdb_transaction_prepare_commit: int (struct tdb_context *) +tdb_transaction_start: int (struct tdb_context *) +tdb_transaction_start_nonblock: int (struct tdb_context *) +tdb_traverse: int (struct tdb_context *, tdb_traverse_func, void *) +tdb_traverse_read: int (struct tdb_context *, tdb_traverse_func, void *) +tdb_unlockall: int (struct tdb_context *) +tdb_unlockall_read: int (struct tdb_context *) +tdb_validate_freelist: int (struct tdb_context *, int *) +tdb_wipe_all: int (struct tdb_context *) diff --git a/ctdb/lib/tdb/ABI/tdb-1.2.7.sigs b/ctdb/lib/tdb/ABI/tdb-1.2.7.sigs new file mode 100644 index 00000000000..1e01f3ba24b --- /dev/null +++ b/ctdb/lib/tdb/ABI/tdb-1.2.7.sigs @@ -0,0 +1,61 @@ +tdb_add_flags: void (struct tdb_context *, unsigned int) +tdb_append: int (struct tdb_context *, TDB_DATA, TDB_DATA) +tdb_chainlock: int (struct tdb_context *, TDB_DATA) +tdb_chainlock_mark: int (struct tdb_context *, TDB_DATA) +tdb_chainlock_nonblock: int (struct tdb_context *, TDB_DATA) +tdb_chainlock_read: int (struct tdb_context *, TDB_DATA) +tdb_chainlock_unmark: int (struct tdb_context *, TDB_DATA) +tdb_chainunlock: int (struct tdb_context *, TDB_DATA) +tdb_chainunlock_read: int (struct tdb_context *, TDB_DATA) +tdb_check: int (struct tdb_context *, int (*)(TDB_DATA, TDB_DATA, void *), void *) +tdb_close: int (struct tdb_context *) +tdb_delete: int (struct tdb_context *, TDB_DATA) +tdb_dump_all: void (struct tdb_context *) +tdb_enable_seqnum: void (struct tdb_context *) +tdb_error: enum TDB_ERROR (struct tdb_context *) +tdb_errorstr: const char *(struct tdb_context *) +tdb_exists: int (struct tdb_context *, TDB_DATA) +tdb_fd: int (struct tdb_context *) +tdb_fetch: TDB_DATA (struct tdb_context *, TDB_DATA) +tdb_firstkey: TDB_DATA (struct tdb_context *) +tdb_freelist_size: int (struct tdb_context *) +tdb_get_flags: int (struct tdb_context *) +tdb_get_logging_private: void *(struct tdb_context *) +tdb_get_seqnum: int (struct tdb_context *) +tdb_hash_size: int (struct tdb_context *) +tdb_increment_seqnum_nonblock: void (struct tdb_context *) +tdb_jenkins_hash: unsigned int (TDB_DATA *) +tdb_lockall: int (struct tdb_context *) +tdb_lockall_mark: int (struct tdb_context *) +tdb_lockall_nonblock: int (struct tdb_context *) +tdb_lockall_read: int (struct tdb_context *) +tdb_lockall_read_nonblock: int (struct tdb_context *) +tdb_lockall_unmark: int (struct tdb_context *) +tdb_log_fn: tdb_log_func (struct tdb_context *) +tdb_map_size: size_t (struct tdb_context *) +tdb_name: const char *(struct tdb_context *) +tdb_nextkey: TDB_DATA (struct tdb_context *, TDB_DATA) +tdb_null: dptr = 0xXXXX, dsize = 0 +tdb_open: struct tdb_context *(const char *, int, int, int, mode_t) +tdb_open_ex: struct tdb_context *(const char *, int, int, int, mode_t, const struct tdb_logging_context *, tdb_hash_func) +tdb_parse_record: int (struct tdb_context *, TDB_DATA, int (*)(TDB_DATA, TDB_DATA, void *), void *) +tdb_printfreelist: int (struct tdb_context *) +tdb_remove_flags: void (struct tdb_context *, unsigned int) +tdb_reopen: int (struct tdb_context *) +tdb_reopen_all: int (int) +tdb_repack: int (struct tdb_context *) +tdb_set_logging_function: void (struct tdb_context *, const struct tdb_logging_context *) +tdb_set_max_dead: void (struct tdb_context *, int) +tdb_setalarm_sigptr: void (struct tdb_context *, volatile sig_atomic_t *) +tdb_store: int (struct tdb_context *, TDB_DATA, TDB_DATA, int) +tdb_transaction_cancel: int (struct tdb_context *) +tdb_transaction_commit: int (struct tdb_context *) +tdb_transaction_prepare_commit: int (struct tdb_context *) +tdb_transaction_start: int (struct tdb_context *) +tdb_transaction_start_nonblock: int (struct tdb_context *) +tdb_traverse: int (struct tdb_context *, tdb_traverse_func, void *) +tdb_traverse_read: int (struct tdb_context *, tdb_traverse_func, void *) +tdb_unlockall: int (struct tdb_context *) +tdb_unlockall_read: int (struct tdb_context *) +tdb_validate_freelist: int (struct tdb_context *, int *) +tdb_wipe_all: int (struct tdb_context *) diff --git a/ctdb/lib/tdb/ABI/tdb-1.2.8.sigs b/ctdb/lib/tdb/ABI/tdb-1.2.8.sigs new file mode 100644 index 00000000000..1e01f3ba24b --- /dev/null +++ b/ctdb/lib/tdb/ABI/tdb-1.2.8.sigs @@ -0,0 +1,61 @@ +tdb_add_flags: void (struct tdb_context *, unsigned int) +tdb_append: int (struct tdb_context *, TDB_DATA, TDB_DATA) +tdb_chainlock: int (struct tdb_context *, TDB_DATA) +tdb_chainlock_mark: int (struct tdb_context *, TDB_DATA) +tdb_chainlock_nonblock: int (struct tdb_context *, TDB_DATA) +tdb_chainlock_read: int (struct tdb_context *, TDB_DATA) +tdb_chainlock_unmark: int (struct tdb_context *, TDB_DATA) +tdb_chainunlock: int (struct tdb_context *, TDB_DATA) +tdb_chainunlock_read: int (struct tdb_context *, TDB_DATA) +tdb_check: int (struct tdb_context *, int (*)(TDB_DATA, TDB_DATA, void *), void *) +tdb_close: int (struct tdb_context *) +tdb_delete: int (struct tdb_context *, TDB_DATA) +tdb_dump_all: void (struct tdb_context *) +tdb_enable_seqnum: void (struct tdb_context *) +tdb_error: enum TDB_ERROR (struct tdb_context *) +tdb_errorstr: const char *(struct tdb_context *) +tdb_exists: int (struct tdb_context *, TDB_DATA) +tdb_fd: int (struct tdb_context *) +tdb_fetch: TDB_DATA (struct tdb_context *, TDB_DATA) +tdb_firstkey: TDB_DATA (struct tdb_context *) +tdb_freelist_size: int (struct tdb_context *) +tdb_get_flags: int (struct tdb_context *) +tdb_get_logging_private: void *(struct tdb_context *) +tdb_get_seqnum: int (struct tdb_context *) +tdb_hash_size: int (struct tdb_context *) +tdb_increment_seqnum_nonblock: void (struct tdb_context *) +tdb_jenkins_hash: unsigned int (TDB_DATA *) +tdb_lockall: int (struct tdb_context *) +tdb_lockall_mark: int (struct tdb_context *) +tdb_lockall_nonblock: int (struct tdb_context *) +tdb_lockall_read: int (struct tdb_context *) +tdb_lockall_read_nonblock: int (struct tdb_context *) +tdb_lockall_unmark: int (struct tdb_context *) +tdb_log_fn: tdb_log_func (struct tdb_context *) +tdb_map_size: size_t (struct tdb_context *) +tdb_name: const char *(struct tdb_context *) +tdb_nextkey: TDB_DATA (struct tdb_context *, TDB_DATA) +tdb_null: dptr = 0xXXXX, dsize = 0 +tdb_open: struct tdb_context *(const char *, int, int, int, mode_t) +tdb_open_ex: struct tdb_context *(const char *, int, int, int, mode_t, const struct tdb_logging_context *, tdb_hash_func) +tdb_parse_record: int (struct tdb_context *, TDB_DATA, int (*)(TDB_DATA, TDB_DATA, void *), void *) +tdb_printfreelist: int (struct tdb_context *) +tdb_remove_flags: void (struct tdb_context *, unsigned int) +tdb_reopen: int (struct tdb_context *) +tdb_reopen_all: int (int) +tdb_repack: int (struct tdb_context *) +tdb_set_logging_function: void (struct tdb_context *, const struct tdb_logging_context *) +tdb_set_max_dead: void (struct tdb_context *, int) +tdb_setalarm_sigptr: void (struct tdb_context *, volatile sig_atomic_t *) +tdb_store: int (struct tdb_context *, TDB_DATA, TDB_DATA, int) +tdb_transaction_cancel: int (struct tdb_context *) +tdb_transaction_commit: int (struct tdb_context *) +tdb_transaction_prepare_commit: int (struct tdb_context *) +tdb_transaction_start: int (struct tdb_context *) +tdb_transaction_start_nonblock: int (struct tdb_context *) +tdb_traverse: int (struct tdb_context *, tdb_traverse_func, void *) +tdb_traverse_read: int (struct tdb_context *, tdb_traverse_func, void *) +tdb_unlockall: int (struct tdb_context *) +tdb_unlockall_read: int (struct tdb_context *) +tdb_validate_freelist: int (struct tdb_context *, int *) +tdb_wipe_all: int (struct tdb_context *) diff --git a/ctdb/lib/tdb/ABI/tdb-1.2.9.sigs b/ctdb/lib/tdb/ABI/tdb-1.2.9.sigs new file mode 100644 index 00000000000..9e4149b4e5e --- /dev/null +++ b/ctdb/lib/tdb/ABI/tdb-1.2.9.sigs @@ -0,0 +1,62 @@ +tdb_add_flags: void (struct tdb_context *, unsigned int) +tdb_append: int (struct tdb_context *, TDB_DATA, TDB_DATA) +tdb_chainlock: int (struct tdb_context *, TDB_DATA) +tdb_chainlock_mark: int (struct tdb_context *, TDB_DATA) +tdb_chainlock_nonblock: int (struct tdb_context *, TDB_DATA) +tdb_chainlock_read: int (struct tdb_context *, TDB_DATA) +tdb_chainlock_unmark: int (struct tdb_context *, TDB_DATA) +tdb_chainunlock: int (struct tdb_context *, TDB_DATA) +tdb_chainunlock_read: int (struct tdb_context *, TDB_DATA) +tdb_check: int (struct tdb_context *, int (*)(TDB_DATA, TDB_DATA, void *), void *) +tdb_close: int (struct tdb_context *) +tdb_delete: int (struct tdb_context *, TDB_DATA) +tdb_dump_all: void (struct tdb_context *) +tdb_enable_seqnum: void (struct tdb_context *) +tdb_error: enum TDB_ERROR (struct tdb_context *) +tdb_errorstr: const char *(struct tdb_context *) +tdb_exists: int (struct tdb_context *, TDB_DATA) +tdb_fd: int (struct tdb_context *) +tdb_fetch: TDB_DATA (struct tdb_context *, TDB_DATA) +tdb_firstkey: TDB_DATA (struct tdb_context *) +tdb_freelist_size: int (struct tdb_context *) +tdb_get_flags: int (struct tdb_context *) +tdb_get_logging_private: void *(struct tdb_context *) +tdb_get_seqnum: int (struct tdb_context *) +tdb_hash_size: int (struct tdb_context *) +tdb_increment_seqnum_nonblock: void (struct tdb_context *) +tdb_jenkins_hash: unsigned int (TDB_DATA *) +tdb_lockall: int (struct tdb_context *) +tdb_lockall_mark: int (struct tdb_context *) +tdb_lockall_nonblock: int (struct tdb_context *) +tdb_lockall_read: int (struct tdb_context *) +tdb_lockall_read_nonblock: int (struct tdb_context *) +tdb_lockall_unmark: int (struct tdb_context *) +tdb_log_fn: tdb_log_func (struct tdb_context *) +tdb_map_size: size_t (struct tdb_context *) +tdb_name: const char *(struct tdb_context *) +tdb_nextkey: TDB_DATA (struct tdb_context *, TDB_DATA) +tdb_null: dptr = 0xXXXX, dsize = 0 +tdb_open: struct tdb_context *(const char *, int, int, int, mode_t) +tdb_open_ex: struct tdb_context *(const char *, int, int, int, mode_t, const struct tdb_logging_context *, tdb_hash_func) +tdb_parse_record: int (struct tdb_context *, TDB_DATA, int (*)(TDB_DATA, TDB_DATA, void *), void *) +tdb_printfreelist: int (struct tdb_context *) +tdb_remove_flags: void (struct tdb_context *, unsigned int) +tdb_reopen: int (struct tdb_context *) +tdb_reopen_all: int (int) +tdb_repack: int (struct tdb_context *) +tdb_set_logging_function: void (struct tdb_context *, const struct tdb_logging_context *) +tdb_set_max_dead: void (struct tdb_context *, int) +tdb_setalarm_sigptr: void (struct tdb_context *, volatile sig_atomic_t *) +tdb_store: int (struct tdb_context *, TDB_DATA, TDB_DATA, int) +tdb_summary: char *(struct tdb_context *) +tdb_transaction_cancel: int (struct tdb_context *) +tdb_transaction_commit: int (struct tdb_context *) +tdb_transaction_prepare_commit: int (struct tdb_context *) +tdb_transaction_start: int (struct tdb_context *) +tdb_transaction_start_nonblock: int (struct tdb_context *) +tdb_traverse: int (struct tdb_context *, tdb_traverse_func, void *) +tdb_traverse_read: int (struct tdb_context *, tdb_traverse_func, void *) +tdb_unlockall: int (struct tdb_context *) +tdb_unlockall_read: int (struct tdb_context *) +tdb_validate_freelist: int (struct tdb_context *, int *) +tdb_wipe_all: int (struct tdb_context *) diff --git a/ctdb/lib/tdb/common/io.c b/ctdb/lib/tdb/common/io.c index 3131f4fa0af..25968bfef2d 100644 --- a/ctdb/lib/tdb/common/io.c +++ b/ctdb/lib/tdb/common/io.c @@ -63,16 +63,6 @@ static int tdb_oob(struct tdb_context *tdb, tdb_off_t off, tdb_len_t len, return -1; } - if (st.st_size < (size_t)off + len) { - if (!probe) { - /* Ensure ecode is set for log fn. */ - tdb->ecode = TDB_ERR_IO; - TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_oob len %u beyond eof at %u\n", - (int)(off + len), (int)st.st_size)); - } - return -1; - } - /* Beware >4G files! */ if ((tdb_off_t)st.st_size != st.st_size) { /* Ensure ecode is set for log fn. */ @@ -82,13 +72,31 @@ static int tdb_oob(struct tdb_context *tdb, tdb_off_t off, tdb_len_t len, return -1; } - /* Unmap, update size, remap */ + /* Unmap, update size, remap. We do this unconditionally, to handle + * the unusual case where the db is truncated. + * + * This can happen to a child using tdb_reopen_all(true) on a + * TDB_CLEAR_IF_FIRST tdb whose parent crashes: the next + * opener will truncate the database. */ if (tdb_munmap(tdb) == -1) { tdb->ecode = TDB_ERR_IO; return -1; } tdb->map_size = st.st_size; - return tdb_mmap(tdb); + if (tdb_mmap(tdb) != 0) { + return - 1; + } + + if (st.st_size < (size_t)off + len) { + if (!probe) { + /* Ensure ecode is set for log fn. */ + tdb->ecode = TDB_ERR_IO; + TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_oob len %u beyond eof at %u\n", + (int)(off + len), (int)st.st_size)); + } + return -1; + } + return 0; } /* write a lump of data at a specified offset */ diff --git a/ctdb/lib/tdb/common/lock.c b/ctdb/lib/tdb/common/lock.c index 88a52e9dfa4..260fab66819 100644 --- a/ctdb/lib/tdb/common/lock.c +++ b/ctdb/lib/tdb/common/lock.c @@ -562,7 +562,7 @@ int tdb_allrecord_lock(struct tdb_context *tdb, int ltype, /* We cover two kinds of locks: * 1) Normal chain locks. Taken for almost all operations. - * 3) Individual records locks. Taken after normal or free + * 2) Individual records locks. Taken after normal or free * chain locks. * * It is (1) which cause the starvation problem, so we're only diff --git a/ctdb/lib/tdb/common/open.c b/ctdb/lib/tdb/common/open.c index 8836f8442ee..d9f76f08350 100644 --- a/ctdb/lib/tdb/common/open.c +++ b/ctdb/lib/tdb/common/open.c @@ -313,12 +313,36 @@ _PUBLIC_ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int td if ((tdb_flags & TDB_CLEAR_IF_FIRST) && (!tdb->read_only) && (locked = (tdb_nest_lock(tdb, ACTIVE_LOCK, F_WRLCK, TDB_LOCK_NOWAIT|TDB_LOCK_PROBE) == 0))) { - open_flags |= O_CREAT; - if (ftruncate(tdb->fd, 0) == -1) { + int ret; + ret = tdb_brlock(tdb, F_WRLCK, FREELIST_TOP, 0, + TDB_LOCK_WAIT); + if (ret == -1) { TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_open_ex: " - "failed to truncate %s: %s\n", + "tdb_brlock failed for %s: %s\n", name, strerror(errno))); - goto fail; /* errno set by ftruncate */ + goto fail; + } + ret = tdb_new_database(tdb, hash_size); + if (ret == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_open_ex: " + "tdb_new_database failed for %s: %s\n", + name, strerror(errno))); + tdb_unlockall(tdb); + goto fail; + } + ret = tdb_brunlock(tdb, F_WRLCK, FREELIST_TOP, 0); + if (ret == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_open_ex: " + "tdb_unlockall failed for %s: %s\n", + name, strerror(errno))); + goto fail; + } + ret = lseek(tdb->fd, 0, SEEK_SET); + if (ret == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_open_ex: " + "lseek failed for %s: %s\n", + name, strerror(errno))); + goto fail; } } diff --git a/ctdb/lib/tdb/common/rescue.c b/ctdb/lib/tdb/common/rescue.c new file mode 100644 index 00000000000..03ae8d6ae51 --- /dev/null +++ b/ctdb/lib/tdb/common/rescue.c @@ -0,0 +1,349 @@ + /* + Unix SMB/CIFS implementation. + + trivial database library, rescue attempt code. + + Copyright (C) Rusty Russell 2012 + + ** NOTE! The following LGPL license applies to the tdb + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see <http://www.gnu.org/licenses/>. +*/ +#include "tdb_private.h" +#include <assert.h> + + +struct found { + tdb_off_t head; /* 0 -> invalid. */ + struct tdb_record rec; + TDB_DATA key; + bool in_hash; + bool in_free; +}; + +struct found_table { + /* As an ordered array (by head offset). */ + struct found *arr; + unsigned int num, max; +}; + +static bool looks_like_valid_record(struct tdb_context *tdb, + tdb_off_t off, + const struct tdb_record *rec, + TDB_DATA *key) +{ + unsigned int hval; + + if (rec->magic != TDB_MAGIC) + return false; + + if (rec->key_len + rec->data_len > rec->rec_len) + return false; + + if (rec->rec_len % TDB_ALIGNMENT) + return false; + + /* Next pointer must make some sense. */ + if (rec->next > 0 && rec->next < TDB_DATA_START(tdb->header.hash_size)) + return false; + + if (tdb->methods->tdb_oob(tdb, rec->next, sizeof(*rec), 1)) + return false; + + key->dsize = rec->key_len; + key->dptr = tdb_alloc_read(tdb, off + sizeof(*rec), key->dsize); + if (!key->dptr) + return false; + + hval = tdb->hash_fn(key); + if (hval != rec->full_hash) { + free(key->dptr); + return false; + } + + /* Caller frees up key->dptr */ + return true; +} + +static bool add_to_table(struct found_table *found, + tdb_off_t off, + struct tdb_record *rec, + TDB_DATA key) +{ + if (found->num + 1 > found->max) { + struct found *new; + found->max = (found->max ? found->max * 2 : 128); + new = realloc(found->arr, found->max * sizeof(found->arr[0])); + if (!new) + return false; + found->arr = new; + } + + found->arr[found->num].head = off; + found->arr[found->num].rec = *rec; + found->arr[found->num].key = key; + found->arr[found->num].in_hash = false; + found->arr[found->num].in_free = false; + + found->num++; + return true; +} + +static bool walk_record(struct tdb_context *tdb, + const struct found *f, + void (*walk)(TDB_DATA, TDB_DATA, void *private_data), + void *private_data) +{ + TDB_DATA data; + + data.dsize = f->rec.data_len; + data.dptr = tdb_alloc_read(tdb, + f->head + sizeof(f->rec) + f->rec.key_len, + data.dsize); + if (!data.dptr) { + if (tdb->ecode == TDB_ERR_OOM) + return false; + /* I/O errors are expected. */ + return true; + } + + walk(f->key, data, private_data); + free(data.dptr); + return true; +} + +/* First entry which has offset >= this one. */ +static unsigned int find_entry(struct found_table *found, tdb_off_t off) +{ + unsigned int start = 0, end = found->num; + + while (start < end) { + /* We can't overflow here. */ + unsigned int mid = (start + end) / 2; + + if (off < found->arr[mid].head) { + end = mid; + } else if (off > found->arr[mid].head) { + start = mid + 1; + } else { + return mid; + } + } + + assert(start == end); + return end; +} + +static void found_in_hashchain(struct found_table *found, tdb_off_t head) +{ + unsigned int match; + + match = find_entry(found, head); + if (match < found->num && found->arr[match].head == head) { + found->arr[match].in_hash = true; + } +} + +static void mark_free_area(struct found_table *found, tdb_off_t head, + tdb_len_t len) +{ + unsigned int match; + + match = find_entry(found, head); + /* Mark everything within this free entry. */ + while (match < found->num) { + if (found->arr[match].head >= head + len) { + break; + } + found->arr[match].in_free = true; + match++; + } +} + +static int cmp_key(const void *a, const void *b) +{ + const struct found *fa = a, *fb = b; + + if (fa->key.dsize < fb->key.dsize) { + return -1; + } else if (fa->key.dsize > fb->key.dsize) { + return 1; + } + return memcmp(fa->key.dptr, fb->key.dptr, fa->key.dsize); +} + +static bool key_eq(TDB_DATA a, TDB_DATA b) +{ + return a.dsize == b.dsize + && memcmp(a.dptr, b.dptr, a.dsize) == 0; +} + +static void free_table(struct found_table *found) +{ + unsigned int i; + + for (i = 0; i < found->num; i++) { + free(found->arr[i].key.dptr); + } + free(found->arr); +} + +static void logging_suppressed(struct tdb_context *tdb, + enum tdb_debug_level level, const char *fmt, ...) +{ +} + +_PUBLIC_ int tdb_rescue(struct tdb_context *tdb, + void (*walk)(TDB_DATA, TDB_DATA, void *private_data), + void *private_data) +{ + struct found_table found = { NULL, 0, 0 }; + tdb_off_t h, off, i; + tdb_log_func oldlog = tdb->log.log_fn; + struct tdb_record rec; + TDB_DATA key; + bool locked; + + /* Read-only databases use no locking at all: it's best-effort. + * We may have a write lock already, so skip that case too. */ + if (tdb->read_only || tdb->allrecord_lock.count != 0) { + locked = false; + } else { + if (tdb_lockall_read(tdb) == -1) + return -1; + locked = true; + } + + /* Make sure we know true size of the underlying file. */ + tdb->methods->tdb_oob(tdb, tdb->map_size, 1, 1); + + /* Suppress logging, since we anticipate errors. */ + tdb->log.log_fn = logging_suppressed; + + /* Now walk entire db looking for records. */ + for (off = TDB_DATA_START(tdb->header.hash_size); + off < tdb->map_size; + off += TDB_ALIGNMENT) { + if (tdb->methods->tdb_read(tdb, off, &rec, sizeof(rec), + DOCONV()) == -1) + continue; + + if (looks_like_valid_record(tdb, off, &rec, &key)) { + if (!add_to_table(&found, off, &rec, key)) { + goto oom; + } + } + } + + /* Walk hash chains to positive vet. */ + for (h = 0; h < 1+tdb->header.hash_size; h++) { + bool slow_chase = false; + tdb_off_t slow_off = FREELIST_TOP + h*sizeof(tdb_off_t); + + if (tdb_ofs_read(tdb, FREELIST_TOP + h*sizeof(tdb_off_t), + &off) == -1) + continue; + + while (off && off != slow_off) { + if (tdb->methods->tdb_read(tdb, off, &rec, sizeof(rec), + DOCONV()) != 0) { + break; + } + + /* 0 is the free list, rest are hash chains. */ + if (h == 0) { + /* Don't mark garbage as free. */ + if (rec.magic != TDB_FREE_MAGIC) { + break; + } + mark_free_area(&found, off, + sizeof(rec) + rec.rec_len); + } else { + found_in_hashchain(&found, off); + } + + off = rec.next; + + /* Loop detection using second pointer at half-speed */ + if (slow_chase) { + /* First entry happens to be next ptr */ + tdb_ofs_read(tdb, slow_off, &slow_off); + } + slow_chase = !slow_chase; + } + } + + /* Recovery area: must be marked as free, since it often has old + * records in there! */ + if (tdb_ofs_read(tdb, TDB_RECOVERY_HEAD, &off) == 0 && off != 0) { + if (tdb->methods->tdb_read(tdb, off, &rec, sizeof(rec), + DOCONV()) == 0) { + mark_free_area(&found, off, sizeof(rec) + rec.rec_len); + } + } + + /* Now sort by key! */ + qsort(found.arr, found.num, sizeof(found.arr[0]), cmp_key); + + for (i = 0; i < found.num; ) { + unsigned int num, num_in_hash = 0; + + /* How many are identical? */ + for (num = 0; num < found.num - i; num++) { + if (!key_eq(found.arr[i].key, found.arr[i+num].key)) { + break; + } + if (found.arr[i+num].in_hash) { + if (!walk_record(tdb, &found.arr[i+num], + walk, private_data)) + goto oom; + num_in_hash++; + } + } + assert(num); + + /* If none were in the hash, we print any not in free list. */ + if (num_in_hash == 0) { + unsigned int j; + + for (j = i; j < i + num; j++) { + if (!found.arr[j].in_free) { + if (!walk_record(tdb, &found.arr[j], + walk, private_data)) + goto oom; + } + } + } + + i += num; + } + + tdb->log.log_fn = oldlog; + if (locked) { + tdb_unlockall_read(tdb); + } + return 0; + +oom: + tdb->log.log_fn = oldlog; + tdb->ecode = TDB_ERR_OOM; + TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_rescue: failed allocating\n")); + free_table(&found); + if (locked) { + tdb_unlockall_read(tdb); + } + return -1; +} diff --git a/ctdb/lib/tdb/common/transaction.c b/ctdb/lib/tdb/common/transaction.c index c3477eb80ca..f18b4c229eb 100644 --- a/ctdb/lib/tdb/common/transaction.c +++ b/ctdb/lib/tdb/common/transaction.c @@ -82,8 +82,9 @@ intervention. - if TDB_NOSYNC is passed to flags in tdb_open then transactions are - still available, but no transaction recovery area is used and no - fsync/msync calls are made. + still available, but no fsync/msync calls are made. This means we + are still proof against a process dying during transaction commit, + but not against machine reboot. - if TDB_ALLOW_NESTING is passed to flags in tdb open, or added using tdb_add_flags() transaction nesting is enabled. @@ -976,13 +977,11 @@ static int _tdb_transaction_prepare_commit(struct tdb_context *tdb) return -1; } - if (!(tdb->flags & TDB_NOSYNC)) { - /* write the recovery data to the end of the file */ - if (transaction_setup_recovery(tdb, &tdb->transaction->magic_offset) == -1) { - TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_prepare_commit: failed to setup recovery data\n")); - _tdb_transaction_cancel(tdb); - return -1; - } + /* write the recovery data to the end of the file */ + if (transaction_setup_recovery(tdb, &tdb->transaction->magic_offset) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_prepare_commit: failed to setup recovery data\n")); + _tdb_transaction_cancel(tdb); + return -1; } tdb->transaction->prepared = true; diff --git a/ctdb/lib/tdb/doxy.config b/ctdb/lib/tdb/doxy.config new file mode 100644 index 00000000000..f55e9c3c274 --- /dev/null +++ b/ctdb/lib/tdb/doxy.config @@ -0,0 +1,1697 @@ +# Doxyfile 1.7.3 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" "). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = tdb + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = 1.2.9 + +# Using the PROJECT_BRIEF tag one can provide an optional one line description for a project that appears at the top of each page and should give viewer a quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify an logo or icon that is +# included in the documentation. The maximum height of the logo should not +# exceed 55 pixels and the maximum width should not exceed 200 pixels. +# Doxygen will copy the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = docs + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, +# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English +# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, +# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, +# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful if your file system +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = YES + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given extension. +# Doxygen has a built-in mapping, but you can override or extend it using this +# tag. The format is ext=language, where ext is a file extension, and language +# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, +# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make +# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C +# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions +# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also makes the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate getter +# and setter methods for a property. Setting this option to YES (the default) +# will make doxygen replace the get and set methods by a property in the +# documentation. This will only work if the methods are indeed getting or +# setting a simple type. If this is not the case, or you want to show the +# methods anyway, you should set this option to NO. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = NO + +# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to +# determine which symbols to keep in memory and which to flush to disk. +# When the cache is full, less often used symbols will be written to disk. +# For small to medium size projects (<1000 input files) the default value is +# probably good enough. For larger projects a too small cache size can cause +# doxygen to be busy swapping symbols to and from disk most of the time +# causing a significant performance penalty. +# If the system has enough physical memory increasing the cache will improve the +# performance by keeping more symbols in memory. Note that the value works on +# a logarithmic scale so increasing the size by one will roughly double the +# memory usage. The cache size is given by this formula: +# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols + +SYMBOL_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = NO + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespaces are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = YES + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = YES + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen +# will list include files with double quotes in the documentation +# rather than with sharp brackets. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen +# will sort the (brief and detailed) documentation of class members so that +# constructors and destructors are listed first. If set to NO (the default) +# the constructors will appear in the respective orders defined by +# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. +# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO +# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper type resolution of all parameters of a function it will reject a +# match between the prototype and the implementation of a member function even if there is only one candidate or it is obvious which candidate to choose by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen +# will still accept a match between prototype and implementation in such cases. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or macro consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and macros in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = NO + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the +# Folder Tree View (if specified). The default is YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. +# This will remove the Namespaces entry from the Quick Index +# and from the Folder Tree View (if specified). The default is YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command <command> <input-file>, where <command> is the value of +# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. The create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. +# You can optionally specify a file name after the option, if omitted +# DoxygenLayout.xml will be used as the name of the layout file. + +LAYOUT_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# The WARN_NO_PARAMDOC option can be enabled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = include \ + docs + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh +# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py +# *.f90 *.f *.for *.vhd *.vhdl + +FILE_PATTERNS = *.cpp \ + *.cc \ + *.c \ + *.h \ + *.hh \ + *.hpp \ + *.dox + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = */.git/* + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command <filter> <input-file>, where <filter> +# is the value of the INPUT_FILTER tag, and <input-file> is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. +# If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. +# Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. +# The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty or if +# non of the patterns match the file name, INPUT_FILTER is applied. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) +# and it is also possible to disable source filtering for a specific pattern +# using *.ext= (so without naming a filter). This option only has effect when +# FILTER_SOURCE_FILES is enabled. + +FILTER_SOURCE_PATTERNS = + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. +# Otherwise they will link to the documentation. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = NO + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. +# Doxygen will adjust the colors in the stylesheet and background images +# according to this color. Hue is specified as an angle on a colorwheel, +# see http://en.wikipedia.org/wiki/Hue for more information. +# For instance the value 0 represents red, 60 is yellow, 120 is green, +# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. +# The allowed range is 0 to 359. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of +# the colors in the HTML output. For a value of 0 the output will use +# grayscales only. A value of 255 will produce the most vivid colors. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to +# the luminance component of the colors in the HTML output. Values below +# 100 gradually make the output lighter, whereas values above 100 make +# the output darker. The value divided by 100 is the actual gamma applied, +# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, +# and 100 does not change the gamma. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting +# this to NO can help when comparing the output of multiple runs. + +HTML_TIMESTAMP = NO + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. For this to work a browser that supports +# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox +# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). + +HTML_DYNAMIC_SECTIONS = NO + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. +# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# content. + +CHM_INDEX_ENCODING = + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated +# that can be used as input for Qt's qhelpgenerator to generate a +# Qt Compressed Help (.qch) of the generated HTML documentation. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. +# The path specified is relative to the HTML output folder. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace + +QHP_NAMESPACE = + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#virtual-folders + +QHP_VIRTUAL_FOLDER = doc + +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to +# add. For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see +# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters"> +# Qt Help Project / Custom Filters</a>. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's +# filter section matches. +# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes"> +# Qt Help Project / Filter Attributes</a>. + +QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files +# will be generated, which together with the HTML files, form an Eclipse help +# plugin. To install this plugin and make it available under the help contents +# menu in Eclipse, the contents of the directory containing the HTML and XML +# files needs to be copied into the plugins directory of eclipse. The name of +# the directory within the plugins directory should be the same as +# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before +# the help appears. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have +# this name. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [0,1..20]) +# that doxygen will group on one line in the generated HTML documentation. +# Note that a value of 0 will completely suppress the enum values from appearing in the overview section. + +ENUM_VALUES_PER_LINE = 4 + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value is set to YES, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). +# Windows users are probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NONE + +# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, +# and Class Hierarchy pages using a tree view instead of an ordered list. + +USE_INLINE_TREES = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open +# links to external symbols imported via tag files in a separate window. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory +# to force them to be regenerated. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are +# not supported properly for IE 6.0, but are supported on all modern browsers. +# Note that when changing this option you need to delete any form_*.png files +# in the HTML output before the changes have effect. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax +# (see http://www.mathjax.org) which uses client side Javascript for the +# rendering instead of using prerendered bitmaps. Use this if you do not +# have LaTeX installed or if you want to formulas look prettier in the HTML +# output. When enabled you also need to install MathJax separately and +# configure the path to it using the MATHJAX_RELPATH option. + +USE_MATHJAX = NO + +# When MathJax is enabled you need to specify the location relative to the +# HTML output directory using the MATHJAX_RELPATH option. The destination +# directory should contain the MathJax.js script. For instance, if the mathjax +# directory is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the mathjax.org site, so you can quickly see the result without installing +# MathJax, but it is strongly recommended to install a local copy of MathJax +# before deployment. + +MATHJAX_RELPATH = http://www.mathjax.org/mathjax + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box +# for the HTML output. The underlying search engine uses javascript +# and DHTML and should work on any modern browser. Note that when using +# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets +# (GENERATE_DOCSET) there is already a search function so this one should +# typically be disabled. For large projects the javascript based search engine +# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. + +SEARCHENGINE = NO + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a PHP enabled web server instead of at the web client +# using Javascript. Doxygen will generate the search PHP script and index +# file to put on the web server. The advantage of the server +# based approach is that it scales better to large projects and allows +# full text search. The disadvantages are that it is more difficult to setup +# and does not have live searching capabilities. + +SERVER_BASED_SEARCH = NO + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = YES + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. +# Note that when enabling USE_PDFLATEX this option is only used for +# generating bitmaps for formulas in the HTML output, but not in the +# Makefile that is written to the output directory. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +# If LATEX_SOURCE_CODE is set to YES then doxygen will include +# source code with syntax highlighting in the LaTeX output. +# Note that which sources are shown also depends on other settings +# such as SOURCE_BROWSER. + +LATEX_SOURCE_CODE = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = YES + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. +# This is useful +# if you want to understand what is going on. +# On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = YES + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = YES + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = DOXYGEN \ + PRINTF_ATTRIBUTE(x,y)= + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition that overrules the definition found in the source code. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all references to function-like macros +# that are alone on a line, have an all uppercase name, and do not end with a +# semicolon, because these will confuse the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option also works with HAVE_DOT disabled, but it is recommended to +# install and use dot, since it yields more powerful graphs. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is +# allowed to run in parallel. When set to 0 (the default) doxygen will +# base this on the number of processors available in the system. You can set it +# explicitly to a value larger than 0 to get control over the balance +# between CPU load and processing speed. + +DOT_NUM_THREADS = 0 + +# By default doxygen will write a font called Helvetica to the output +# directory and reference it in all dot files that doxygen generates. +# When you want a differently looking font you can specify the font name +# using DOT_FONTNAME. You need to make sure dot is able to find the font, +# which can be done by putting it in a standard location or by setting the +# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory +# containing the font. + +DOT_FONTNAME = FreeSans + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the output directory to look for the +# FreeSans.ttf font (which doxygen will put there itself). If you specify a +# different font using DOT_FONTNAME you can set the path where dot +# can find it using this tag. + +DOT_FONTPATH = + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will generate a graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, svg, gif or svg. +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the +# \mscfile command). + +MSCFILE_DIRS = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). + +DOT_TRANSPARENT = YES + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES diff --git a/ctdb/lib/tdb/include/tdb.h b/ctdb/lib/tdb/include/tdb.h index 5eb95625dae..d19439e7602 100644 --- a/ctdb/lib/tdb/include/tdb.h +++ b/ctdb/lib/tdb/include/tdb.h @@ -814,6 +814,28 @@ int tdb_check(struct tdb_context *tdb, int (*check) (TDB_DATA key, TDB_DATA data, void *private_data), void *private_data); +/** + * @brief Dump all possible records in a corrupt database. + * + * This is the only way to get data out of a database where tdb_check() fails. + * It will call walk() with anything which looks like a database record; this + * may well include invalid, incomplete or duplicate records. + * + * @param[in] tdb The database to check. + * + * @param[in] walk The walk function to use. + * + * @param[in] private_data the private data to pass to the walk function. + * + * @return 0 on success, -1 on error with error code set. + * + * @see tdb_error() + * @see tdb_errorstr() + */ +int tdb_rescue(struct tdb_context *tdb, + void (*walk) (TDB_DATA key, TDB_DATA data, void *private_data), + void *private_data); + /* @} ******************************************************************/ /* Low level locking functions: use with care */ diff --git a/ctdb/lib/tdb/libtdb.m4 b/ctdb/lib/tdb/libtdb.m4 index dde4e0d0e90..6503bf15f44 100644 --- a/ctdb/lib/tdb/libtdb.m4 +++ b/ctdb/lib/tdb/libtdb.m4 @@ -37,7 +37,7 @@ if test x"$INCLUDED_TDB" != x"no" ; then AC_MSG_ERROR([cannot find tdb source in $tdbpaths]) fi TDB_OBJ="common/tdb.o common/dump.o common/transaction.o common/error.o common/traverse.o" - TDB_OBJ="$TDB_OBJ common/freelist.o common/freelistcheck.o common/io.o common/lock.o common/open.o common/check.o common/hash.o" + TDB_OBJ="$TDB_OBJ common/freelist.o common/freelistcheck.o common/io.o common/lock.o common/open.o common/check.o common/hash.o common/summary.o common/rescue.o" AC_SUBST(TDB_OBJ) TDB_LIBS="" diff --git a/ctdb/lib/tdb/manpages/tdbbackup.8.xml b/ctdb/lib/tdb/manpages/tdbbackup.8.xml new file mode 100644 index 00000000000..78fe32eb8ec --- /dev/null +++ b/ctdb/lib/tdb/manpages/tdbbackup.8.xml @@ -0,0 +1,136 @@ +<?xml version="1.0" encoding="iso-8859-1"?> +<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> +<refentry id="tdbbackup.8"> + +<refmeta> + <refentrytitle>tdbbackup</refentrytitle> + <manvolnum>8</manvolnum> + <refmiscinfo class="source">Samba</refmiscinfo> + <refmiscinfo class="manual">System Administration tools</refmiscinfo> + <refmiscinfo class="version">3.6</refmiscinfo> +</refmeta> + + +<refnamediv> + <refname>tdbbackup</refname> + <refpurpose>tool for backing up and for validating the integrity of samba .tdb files</refpurpose> +</refnamediv> + +<refsynopsisdiv> + <cmdsynopsis> + <command>tdbbackup</command> + <arg choice="opt">-s suffix</arg> + <arg choice="opt">-v</arg> + <arg choice="opt">-h</arg> + </cmdsynopsis> +</refsynopsisdiv> + +<refsect1> + <title>DESCRIPTION</title> + + <para>This tool is part of the <citerefentry><refentrytitle>samba</refentrytitle> + <manvolnum>1</manvolnum></citerefentry> suite.</para> + + <para><command>tdbbackup</command> is a tool that may be used to backup samba .tdb + files. This tool may also be used to verify the integrity of the .tdb files prior + to samba startup or during normal operation. If it finds file damage and it finds + a prior backup the backup file will be restored. + </para> +</refsect1> + + +<refsect1> + <title>OPTIONS</title> + + <variablelist> + + <varlistentry> + <term>-h</term> + <listitem><para> + Get help information. + </para></listitem> + </varlistentry> + + <varlistentry> + <term>-s suffix</term> + <listitem><para> + The <command>-s</command> option allows the adminisistrator to specify a file + backup extension. This way it is possible to keep a history of tdb backup + files by using a new suffix for each backup. + </para> </listitem> + </varlistentry> + + <varlistentry> + <term>-v</term> + <listitem><para> + The <command>-v</command> will check the database for damages (currupt data) + which if detected causes the backup to be restored. + </para></listitem> + </varlistentry> + + </variablelist> +</refsect1> + + +<refsect1> + <title>COMMANDS</title> + + <para><emphasis>GENERAL INFORMATION</emphasis></para> + + <para> + The <command>tdbbackup</command> utility can safely be run at any time. It was designed so + that it can be used at any time to validate the integrity of tdb files, even during Samba + operation. Typical usage for the command will be: + </para> + + <para>tdbbackup [-s suffix] *.tdb</para> + + <para> + Before restarting samba the following command may be run to validate .tdb files: + </para> + + <para>tdbbackup -v [-s suffix] *.tdb</para> + + <para> + Samba .tdb files are stored in various locations, be sure to run backup all + .tdb file on the system. Important files includes: + </para> + + <itemizedlist> + <listitem><para> + <command>secrets.tdb</command> - usual location is in the /usr/local/samba/private + directory, or on some systems in /etc/samba. + </para></listitem> + + <listitem><para> + <command>passdb.tdb</command> - usual location is in the /usr/local/samba/private + directory, or on some systems in /etc/samba. + </para></listitem> + + <listitem><para> + <command>*.tdb</command> located in the /usr/local/samba/var directory or on some + systems in the /var/cache or /var/lib/samba directories. + </para></listitem> + </itemizedlist> + +</refsect1> + +<refsect1> + <title>VERSION</title> + + <para>This man page is correct for version 3 of the Samba suite.</para> +</refsect1> + +<refsect1> + <title>AUTHOR</title> + + <para> + The original Samba software and related utilities were created by Andrew Tridgell. + Samba is now developed by the Samba Team as an Open Source project similar to the way + the Linux kernel is developed. + </para> + + <para>The tdbbackup man page was written by John H Terpstra.</para> +</refsect1> + +</refentry> diff --git a/ctdb/lib/tdb/manpages/tdbdump.8.xml b/ctdb/lib/tdb/manpages/tdbdump.8.xml new file mode 100644 index 00000000000..34201932434 --- /dev/null +++ b/ctdb/lib/tdb/manpages/tdbdump.8.xml @@ -0,0 +1,92 @@ +<?xml version="1.0" encoding="iso-8859-1"?> +<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> +<refentry id="tdbdump.8"> + +<refmeta> + <refentrytitle>tdbdump</refentrytitle> + <manvolnum>8</manvolnum> + <refmiscinfo class="source">Samba</refmiscinfo> + <refmiscinfo class="manual">System Administration tools</refmiscinfo> + <refmiscinfo class="version">3.6</refmiscinfo> +</refmeta> + + +<refnamediv> + <refname>tdbdump</refname> + <refpurpose>tool for printing the contents of a TDB file</refpurpose> +</refnamediv> + +<refsynopsisdiv> + <cmdsynopsis> + <command>tdbdump</command> + <arg choice="opt">-k <replaceable>keyname</replaceable></arg> + <arg choice="opt">-e</arg> + <arg choice="opt">-h</arg> + <arg choice="req">filename</arg> + </cmdsynopsis> +</refsynopsisdiv> + +<refsect1> + <title>DESCRIPTION</title> + + <para>This tool is part of the <citerefentry><refentrytitle>samba</refentrytitle> + <manvolnum>1</manvolnum></citerefentry> suite.</para> + + <para><command>tdbdump</command> is a very simple utility that 'dumps' the + contents of a TDB (Trivial DataBase) file to standard output in a + human-readable format. + </para> + + <para>This tool can be used when debugging problems with TDB files. It is + intended for those who are somewhat familiar with Samba internals. + </para> +</refsect1> + +<refsect1> + <title>OPTIONS</title> + + <variablelist> + + <varlistentry> + <term>-h</term> + <listitem><para> + Get help information. + </para></listitem> + </varlistentry> + + <varlistentry> + <term>-k <replaceable>keyname</replaceable></term> + <listitem><para> + The <command>-k</command> option restricts dumping to a single key, if found. + </para> </listitem> + </varlistentry> + + <varlistentry> + <term>-e</term> + <listitem><para> + The <command>-e</command> tries to dump out from a corrupt database. Naturally, such a dump is unreliable, at best. + </para></listitem> + </varlistentry> + + </variablelist> +</refsect1> + +<refsect1> + <title>VERSION</title> + + <para>This man page is correct for version 3 of the Samba suite.</para> +</refsect1> + +<refsect1> + <title>AUTHOR</title> + + <para> + The original Samba software and related utilities were created by Andrew Tridgell. + Samba is now developed by the Samba Team as an Open Source project similar to the way + the Linux kernel is developed. + </para> + + <para>The tdbdump man page was written by Jelmer Vernooij.</para> +</refsect1> + +</refentry> diff --git a/ctdb/lib/tdb/manpages/tdbrestore.8.xml b/ctdb/lib/tdb/manpages/tdbrestore.8.xml new file mode 100644 index 00000000000..64c0ba2dac1 --- /dev/null +++ b/ctdb/lib/tdb/manpages/tdbrestore.8.xml @@ -0,0 +1,66 @@ +<?xml version="1.0" encoding="iso-8859-1"?> +<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> +<refentry id="tdbrestore.8"> + +<refmeta> + <refentrytitle>tdbrestore</refentrytitle> + <manvolnum>8</manvolnum> + <refmiscinfo class="source">Samba</refmiscinfo> + <refmiscinfo class="manual">System Administration tools</refmiscinfo> + <refmiscinfo class="version">3.6</refmiscinfo> +</refmeta> + + +<refnamediv> + <refname>tdbrestore</refname> + <refpurpose>tool for creating a TDB file out of a tdbdump output</refpurpose> +</refnamediv> + +<refsynopsisdiv> + <cmdsynopsis> + <command>tdbrestore</command> + <arg choice="req">tdbfilename</arg> + </cmdsynopsis> +</refsynopsisdiv> + +<refsect1> + <title>DESCRIPTION</title> + + <para>This tool is part of the <citerefentry><refentrytitle>samba</refentrytitle> + <manvolnum>1</manvolnum></citerefentry> suite.</para> + + <para><command>tdbrestore</command> is a very simple utility that 'restores' the + contents of dump file into TDB (Trivial DataBase) file. The dump file is obtained from the tdbdump + command. + </para> + + <para>This tool wait on the standard input for the content of the dump and will write the tdb in the tdbfilename + parameter. + </para> + <para>This tool can be used for unpacking the content of tdb as backup mean. + </para> +</refsect1> + + +<refsect1> + <title>VERSION</title> + + <para>This man page is correct for version 3 of the Samba suite.</para> +</refsect1> + +<refsect1> + <title>AUTHOR</title> + + <para> + The original Samba software and related utilities were created by Andrew Tridgell. + Samba is now developed by the Samba Team as an Open Source project similar to the way + the Linux kernel is developed. + + This tool was initially written by Volker Lendecke based on an + idea by Simon McVittie. + </para> + + <para>The tdbrestore man page was written by Matthieu Patou.</para> +</refsect1> + +</refentry> diff --git a/ctdb/lib/tdb/manpages/tdbtool.8.xml b/ctdb/lib/tdb/manpages/tdbtool.8.xml new file mode 100644 index 00000000000..9f96db277db --- /dev/null +++ b/ctdb/lib/tdb/manpages/tdbtool.8.xml @@ -0,0 +1,235 @@ +<?xml version="1.0" encoding="iso-8859-1"?> +<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> +<refentry id="tdbtool.8"> + +<refmeta> + <refentrytitle>tdbtool</refentrytitle> + <manvolnum>8</manvolnum> + <refmiscinfo class="source">Samba</refmiscinfo> + <refmiscinfo class="manual">System Administration tools</refmiscinfo> + <refmiscinfo class="version">3.6</refmiscinfo> +</refmeta> + + +<refnamediv> + <refname>tdbtool</refname> + <refpurpose>manipulate the contents TDB files</refpurpose> +</refnamediv> + +<refsynopsisdiv> + + <cmdsynopsis> + <command>tdbtool</command> + </cmdsynopsis> + + <cmdsynopsis> + <command>tdbtool</command> + <arg choice="plain"> + <replaceable>TDBFILE</replaceable> + </arg> + <arg rep="repeat" choice="opt"> + <replaceable>COMMANDS</replaceable> + </arg> + </cmdsynopsis> + +</refsynopsisdiv> + +<refsect1> + <title>DESCRIPTION</title> + + <para>This tool is part of the + <citerefentry><refentrytitle>samba</refentrytitle> + <manvolnum>1</manvolnum></citerefentry> suite.</para> + + <para><command>tdbtool</command> a tool for displaying and + altering the contents of Samba TDB (Trivial DataBase) files. Each + of the commands listed below can be entered interactively or + provided on the command line.</para> + +</refsect1> + + +<refsect1> + <title>COMMANDS</title> + + <variablelist> + + <varlistentry> + <term><option>create</option> + <replaceable>TDBFILE</replaceable></term> + <listitem><para>Create a new database named + <replaceable>TDBFILE</replaceable>. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option>open</option> + <replaceable>TDBFILE</replaceable></term> + <listitem><para>Open an existing database named + <replaceable>TDBFILE</replaceable>. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option>erase</option></term> + <listitem><para>Erase the current database. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option>dump</option></term> + <listitem><para>Dump the current database as strings. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option>cdump</option></term> + <listitem><para>Dump the current database as connection records. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option>keys</option></term> + <listitem><para>Dump the current database keys as strings. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option>hexkeys</option></term> + <listitem><para>Dump the current database keys as hex values. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option>info</option></term> + <listitem><para>Print summary information about the + current database. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option>insert</option> + <replaceable>KEY</replaceable> + <replaceable>DATA</replaceable> + </term> + <listitem><para>Insert a record into the + current database. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option>move</option> + <replaceable>KEY</replaceable> + <replaceable>TDBFILE</replaceable> + </term> + <listitem><para>Move a record from the + current database into <replaceable>TDBFILE</replaceable>. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option>store</option> + <replaceable>KEY</replaceable> + <replaceable>DATA</replaceable> + </term> + <listitem><para>Store (replace) a record in the + current database. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option>show</option> + <replaceable>KEY</replaceable> + </term> + <listitem><para>Show a record by key. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option>delete</option> + <replaceable>KEY</replaceable> + </term> + <listitem><para>Delete a record by key. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option>list</option> + </term> + <listitem><para>Print the current database hash table and free list. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option>free</option> + </term> + <listitem><para>Print the current database and free list. + </para></listitem> + </varlistentry> + + <varlistentry> + <term><option>!</option> + <replaceable>COMMAND</replaceable> + </term> + <listitem><para>Execute the given system command. + </para></listitem> + </varlistentry> + + <varlistentry> + <term> + <option>first</option> + </term> + <listitem><para>Print the first record in the current database. + </para></listitem> + </varlistentry> + + <varlistentry> + <term> + <option>next</option> + </term> + <listitem><para>Print the next record in the current database. + </para></listitem> + </varlistentry> + + <varlistentry> + <term> + <option>check</option> + </term> + <listitem><para>Check the integrity of the current database. + </para></listitem> + </varlistentry> + + <varlistentry> + <term> + <option>quit</option> + </term> + <listitem><para>Exit <command>tdbtool</command>. + </para></listitem> + </varlistentry> + + </variablelist> +</refsect1> + +<refsect1> + <title>CAVEATS</title> + <para>The contents of the Samba TDB files are private + to the implementation and should not be altered with + <command>tdbtool</command>. + </para> +</refsect1> + +<refsect1> + <title>VERSION</title> + <para>This man page is correct for version 3.0.25 of the Samba suite.</para> +</refsect1> + +<refsect1> + <title>AUTHOR</title> + + <para> The original Samba software and related utilities were + created by Andrew Tridgell. Samba is now developed by the + Samba Team as an Open Source project similar to the way the + Linux kernel is developed.</para> +</refsect1> + +</refentry> diff --git a/ctdb/lib/tdb/pytdb.c b/ctdb/lib/tdb/pytdb.c index 48927522f3a..ae0e6f808d7 100644 --- a/ctdb/lib/tdb/pytdb.c +++ b/ctdb/lib/tdb/pytdb.c @@ -75,6 +75,20 @@ static PyObject *PyString_FromTDB_DATA(TDB_DATA data) return NULL; \ } +#define PyErr_TDB_RAISE_IF_CLOSED(self) \ + if (self->closed) { \ + PyErr_SetObject(PyExc_RuntimeError, \ + Py_BuildValue("(i,s)", TDB_ERR_IO, "Database is already closed")); \ + return NULL; \ + } + +#define PyErr_TDB_RAISE_RETURN_MINUS_1_IF_CLOSED(self) \ + if (self->closed) { \ + PyErr_SetObject(PyExc_RuntimeError, \ + Py_BuildValue("(i,s)", TDB_ERR_IO, "Database is already closed")); \ + return -1; \ + } + static PyObject *py_tdb_open(PyTypeObject *type, PyObject *args, PyObject *kwargs) { char *name = NULL; @@ -109,56 +123,74 @@ static PyObject *py_tdb_open(PyTypeObject *type, PyObject *args, PyObject *kwarg static PyObject *obj_transaction_cancel(PyTdbObject *self) { - int ret = tdb_transaction_cancel(self->ctx); + int ret; + + PyErr_TDB_RAISE_IF_CLOSED(self); + + ret = tdb_transaction_cancel(self->ctx); PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx); Py_RETURN_NONE; } static PyObject *obj_transaction_commit(PyTdbObject *self) { - int ret = tdb_transaction_commit(self->ctx); + int ret; + PyErr_TDB_RAISE_IF_CLOSED(self); + ret = tdb_transaction_commit(self->ctx); PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx); Py_RETURN_NONE; } static PyObject *obj_transaction_prepare_commit(PyTdbObject *self) { - int ret = tdb_transaction_prepare_commit(self->ctx); + int ret; + PyErr_TDB_RAISE_IF_CLOSED(self); + ret = tdb_transaction_prepare_commit(self->ctx); PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx); Py_RETURN_NONE; } static PyObject *obj_transaction_start(PyTdbObject *self) { - int ret = tdb_transaction_start(self->ctx); + int ret; + PyErr_TDB_RAISE_IF_CLOSED(self); + ret = tdb_transaction_start(self->ctx); PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx); Py_RETURN_NONE; } static PyObject *obj_reopen(PyTdbObject *self) { - int ret = tdb_reopen(self->ctx); + int ret; + PyErr_TDB_RAISE_IF_CLOSED(self); + ret = tdb_reopen(self->ctx); PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx); Py_RETURN_NONE; } static PyObject *obj_lockall(PyTdbObject *self) { - int ret = tdb_lockall(self->ctx); + int ret; + PyErr_TDB_RAISE_IF_CLOSED(self); + ret = tdb_lockall(self->ctx); PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx); Py_RETURN_NONE; } static PyObject *obj_unlockall(PyTdbObject *self) { - int ret = tdb_unlockall(self->ctx); + int ret; + PyErr_TDB_RAISE_IF_CLOSED(self); + ret = tdb_unlockall(self->ctx); PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx); Py_RETURN_NONE; } static PyObject *obj_lockall_read(PyTdbObject *self) { - int ret = tdb_lockall_read(self->ctx); + int ret; + PyErr_TDB_RAISE_IF_CLOSED(self); + ret = tdb_lockall_read(self->ctx); PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx); Py_RETURN_NONE; } @@ -185,10 +217,15 @@ static PyObject *obj_get(PyTdbObject *self, PyObject *args) { TDB_DATA key; PyObject *py_key; + + PyErr_TDB_RAISE_IF_CLOSED(self); + if (!PyArg_ParseTuple(args, "O", &py_key)) return NULL; key = PyString_AsTDB_DATA(py_key); + if (!key.dptr) + return NULL; return PyString_FromTDB_DATA(tdb_fetch(self->ctx, key)); } @@ -198,11 +235,18 @@ static PyObject *obj_append(PyTdbObject *self, PyObject *args) TDB_DATA key, data; PyObject *py_key, *py_data; int ret; + + PyErr_TDB_RAISE_IF_CLOSED(self); + if (!PyArg_ParseTuple(args, "OO", &py_key, &py_data)) return NULL; key = PyString_AsTDB_DATA(py_key); + if (!key.dptr) + return NULL; data = PyString_AsTDB_DATA(py_data); + if (!data.dptr) + return NULL; ret = tdb_append(self->ctx, key, data); PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx); @@ -211,6 +255,8 @@ static PyObject *obj_append(PyTdbObject *self, PyObject *args) static PyObject *obj_firstkey(PyTdbObject *self) { + PyErr_TDB_RAISE_IF_CLOSED(self); + return PyString_FromTDB_DATA(tdb_firstkey(self->ctx)); } @@ -218,10 +264,14 @@ static PyObject *obj_nextkey(PyTdbObject *self, PyObject *args) { TDB_DATA key; PyObject *py_key; + PyErr_TDB_RAISE_IF_CLOSED(self); + if (!PyArg_ParseTuple(args, "O", &py_key)) return NULL; key = PyString_AsTDB_DATA(py_key); + if (!key.dptr) + return NULL; return PyString_FromTDB_DATA(tdb_nextkey(self->ctx, key)); } @@ -231,10 +281,14 @@ static PyObject *obj_delete(PyTdbObject *self, PyObject *args) TDB_DATA key; PyObject *py_key; int ret; + PyErr_TDB_RAISE_IF_CLOSED(self); + if (!PyArg_ParseTuple(args, "O", &py_key)) return NULL; key = PyString_AsTDB_DATA(py_key); + if (!key.dptr) + return NULL; ret = tdb_delete(self->ctx, key); PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx); Py_RETURN_NONE; @@ -245,10 +299,14 @@ static PyObject *obj_has_key(PyTdbObject *self, PyObject *args) TDB_DATA key; int ret; PyObject *py_key; + PyErr_TDB_RAISE_IF_CLOSED(self); + if (!PyArg_ParseTuple(args, "O", &py_key)) return NULL; key = PyString_AsTDB_DATA(py_key); + if (!key.dptr) + return NULL; ret = tdb_exists(self->ctx, key); if (ret != TDB_ERR_NOEXIST) { PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx); @@ -264,11 +322,17 @@ static PyObject *obj_store(PyTdbObject *self, PyObject *args) int flag = TDB_REPLACE; PyObject *py_key, *py_value; + PyErr_TDB_RAISE_IF_CLOSED(self); + if (!PyArg_ParseTuple(args, "OO|i", &py_key, &py_value, &flag)) return NULL; key = PyString_AsTDB_DATA(py_key); + if (!key.dptr) + return NULL; value = PyString_AsTDB_DATA(py_value); + if (!value.dptr) + return NULL; ret = tdb_store(self->ctx, key, value, flag); PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx); @@ -279,6 +343,8 @@ static PyObject *obj_add_flags(PyTdbObject *self, PyObject *args) { unsigned flags; + PyErr_TDB_RAISE_IF_CLOSED(self); + if (!PyArg_ParseTuple(args, "I", &flags)) return NULL; @@ -290,6 +356,8 @@ static PyObject *obj_remove_flags(PyTdbObject *self, PyObject *args) { unsigned flags; + PyErr_TDB_RAISE_IF_CLOSED(self); + if (!PyArg_ParseTuple(args, "I", &flags)) return NULL; @@ -334,6 +402,8 @@ static PyObject *tdb_object_iter(PyTdbObject *self) { PyTdbIteratorObject *ret; + PyErr_TDB_RAISE_IF_CLOSED(self); + ret = PyObject_New(PyTdbIteratorObject, &PyTdbIterator); if (!ret) return NULL; @@ -345,26 +415,32 @@ static PyObject *tdb_object_iter(PyTdbObject *self) static PyObject *obj_clear(PyTdbObject *self) { - int ret = tdb_wipe_all(self->ctx); + int ret; + PyErr_TDB_RAISE_IF_CLOSED(self); + ret = tdb_wipe_all(self->ctx); PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx); Py_RETURN_NONE; } static PyObject *obj_repack(PyTdbObject *self) { - int ret = tdb_repack(self->ctx); + int ret; + PyErr_TDB_RAISE_IF_CLOSED(self); + ret = tdb_repack(self->ctx); PyErr_TDB_ERROR_IS_ERR_RAISE(ret, self->ctx); Py_RETURN_NONE; } static PyObject *obj_enable_seqnum(PyTdbObject *self) { + PyErr_TDB_RAISE_IF_CLOSED(self); tdb_enable_seqnum(self->ctx); Py_RETURN_NONE; } static PyObject *obj_increment_seqnum_nonblock(PyTdbObject *self) { + PyErr_TDB_RAISE_IF_CLOSED(self); tdb_increment_seqnum_nonblock(self->ctx); Py_RETURN_NONE; } @@ -418,11 +494,13 @@ static PyMethodDef tdb_object_methods[] = { static PyObject *obj_get_hash_size(PyTdbObject *self, void *closure) { + PyErr_TDB_RAISE_IF_CLOSED(self); return PyInt_FromLong(tdb_hash_size(self->ctx)); } static int obj_set_max_dead(PyTdbObject *self, PyObject *max_dead, void *closure) { + PyErr_TDB_RAISE_RETURN_MINUS_1_IF_CLOSED(self); if (!PyInt_Check(max_dead)) return -1; tdb_set_max_dead(self->ctx, PyInt_AsLong(max_dead)); @@ -431,26 +509,31 @@ static int obj_set_max_dead(PyTdbObject *self, PyObject *max_dead, void *closure static PyObject *obj_get_map_size(PyTdbObject *self, void *closure) { + PyErr_TDB_RAISE_IF_CLOSED(self); return PyInt_FromLong(tdb_map_size(self->ctx)); } static PyObject *obj_get_freelist_size(PyTdbObject *self, void *closure) { + PyErr_TDB_RAISE_IF_CLOSED(self); return PyInt_FromLong(tdb_freelist_size(self->ctx)); } static PyObject *obj_get_flags(PyTdbObject *self, void *closure) { + PyErr_TDB_RAISE_IF_CLOSED(self); return PyInt_FromLong(tdb_get_flags(self->ctx)); } static PyObject *obj_get_filename(PyTdbObject *self, void *closure) { + PyErr_TDB_RAISE_IF_CLOSED(self); return PyString_FromString(tdb_name(self->ctx)); } static PyObject *obj_get_seqnum(PyTdbObject *self, void *closure) { + PyErr_TDB_RAISE_IF_CLOSED(self); return PyInt_FromLong(tdb_get_seqnum(self->ctx)); } @@ -468,6 +551,7 @@ static PyGetSetDef tdb_object_getsetters[] = { static PyObject *tdb_object_repr(PyTdbObject *self) { + PyErr_TDB_RAISE_IF_CLOSED(self); if (tdb_get_flags(self->ctx) & TDB_INTERNAL) { return PyString_FromString("Tdb(<internal>)"); } else { @@ -485,6 +569,7 @@ static void tdb_object_dealloc(PyTdbObject *self) static PyObject *obj_getitem(PyTdbObject *self, PyObject *key) { TDB_DATA tkey, val; + PyErr_TDB_RAISE_IF_CLOSED(self); if (!PyString_Check(key)) { PyErr_SetString(PyExc_TypeError, "Expected string as key"); return NULL; @@ -506,6 +591,7 @@ static int obj_setitem(PyTdbObject *self, PyObject *key, PyObject *value) { TDB_DATA tkey, tval; int ret; + PyErr_TDB_RAISE_RETURN_MINUS_1_IF_CLOSED(self); if (!PyString_Check(key)) { PyErr_SetString(PyExc_TypeError, "Expected string as key"); return -1; diff --git a/ctdb/lib/tdb/python/tests/simple.py b/ctdb/lib/tdb/python/tests/simple.py index 2877092fe31..7e295a8c952 100644 --- a/ctdb/lib/tdb/python/tests/simple.py +++ b/ctdb/lib/tdb/python/tests/simple.py @@ -13,8 +13,8 @@ import os, tempfile class OpenTdbTests(TestCase): - def test_nonexistant_read(self): - self.assertRaises(IOError, tdb.Tdb, "/some/nonexistant/file", 0, + def test_nonexistent_read(self): + self.assertRaises(IOError, tdb.Tdb, "/some/nonexistent/file", 0, tdb.DEFAULT, os.O_RDWR) class CloseTdbTests(TestCase): @@ -33,6 +33,11 @@ class CloseTdbTests(TestCase): self.tdb.close() self.tdb.close() + # Check that further operations do not crash python + self.assertRaises(RuntimeError, lambda: self.tdb.transaction_start()) + + self.assertRaises(RuntimeError, lambda: self.tdb["bar"]) + class InternalTdbTests(TestCase): diff --git a/ctdb/lib/tdb/tdb.pc.in b/ctdb/lib/tdb/tdb.pc.in index 6f8f553736c..b78419ea784 100644 --- a/ctdb/lib/tdb/tdb.pc.in +++ b/ctdb/lib/tdb/tdb.pc.in @@ -6,6 +6,6 @@ includedir=@includedir@ Name: tdb Description: A trivial database Version: @PACKAGE_VERSION@ -Libs: -L${libdir} -ltdb +Libs: @LIB_RPATH@ -L${libdir} -ltdb Cflags: -I${includedir} URL: http://tdb.samba.org/ diff --git a/ctdb/lib/tdb/test/external-agent.c b/ctdb/lib/tdb/test/external-agent.c new file mode 100644 index 00000000000..8140e70ead0 --- /dev/null +++ b/ctdb/lib/tdb/test/external-agent.c @@ -0,0 +1,198 @@ +#include "external-agent.h" +#include "lock-tracking.h" +#include "logging.h" +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> +#include <fcntl.h> +#include <stdlib.h> +#include <limits.h> +#include <string.h> +#include <errno.h> +#include "../common/tdb_private.h" +#include "tap-interface.h" +#include <stdio.h> +#include <stdarg.h> + +static struct tdb_context *tdb; + +static enum agent_return do_operation(enum operation op, const char *name) +{ + TDB_DATA k; + enum agent_return ret; + TDB_DATA data; + + if (op != OPEN && op != OPEN_WITH_CLEAR_IF_FIRST && !tdb) { + diag("external: No tdb open!"); + return OTHER_FAILURE; + } + + k.dptr = (void *)name; + k.dsize = strlen(name); + + locking_would_block = 0; + switch (op) { + case OPEN: + if (tdb) { + diag("Already have tdb %s open", tdb_name(tdb)); + return OTHER_FAILURE; + } + tdb = tdb_open_ex(name, 0, TDB_DEFAULT, O_RDWR, 0, + &taplogctx, NULL); + if (!tdb) { + if (!locking_would_block) + diag("Opening tdb gave %s", strerror(errno)); + ret = OTHER_FAILURE; + } else + ret = SUCCESS; + break; + case OPEN_WITH_CLEAR_IF_FIRST: + if (tdb) + return OTHER_FAILURE; + tdb = tdb_open_ex(name, 0, TDB_CLEAR_IF_FIRST, O_RDWR, 0, + &taplogctx, NULL); + ret = tdb ? SUCCESS : OTHER_FAILURE; + break; + case TRANSACTION_START: + ret = tdb_transaction_start(tdb) == 0 ? SUCCESS : OTHER_FAILURE; + break; + case FETCH: + data = tdb_fetch(tdb, k); + if (data.dptr == NULL) { + if (tdb_error(tdb) == TDB_ERR_NOEXIST) + ret = FAILED; + else + ret = OTHER_FAILURE; + } else if (data.dsize != k.dsize + || memcmp(data.dptr, k.dptr, k.dsize) != 0) { + ret = OTHER_FAILURE; + } else { + ret = SUCCESS; + } + free(data.dptr); + break; + case STORE: + ret = tdb_store(tdb, k, k, 0) == 0 ? SUCCESS : OTHER_FAILURE; + break; + case TRANSACTION_COMMIT: + ret = tdb_transaction_commit(tdb)==0 ? SUCCESS : OTHER_FAILURE; + break; + case CHECK: + ret = tdb_check(tdb, NULL, NULL) == 0 ? SUCCESS : OTHER_FAILURE; + break; + case NEEDS_RECOVERY: + ret = tdb_needs_recovery(tdb) ? SUCCESS : FAILED; + break; + case CLOSE: + ret = tdb_close(tdb) == 0 ? SUCCESS : OTHER_FAILURE; + tdb = NULL; + break; + default: + ret = OTHER_FAILURE; + } + + if (locking_would_block) + ret = WOULD_HAVE_BLOCKED; + + return ret; +} + +struct agent { + int cmdfd, responsefd; +}; + +/* Do this before doing any tdb stuff. Return handle, or NULL. */ +struct agent *prepare_external_agent(void) +{ + int pid, ret; + int command[2], response[2]; + char name[1+PATH_MAX]; + + if (pipe(command) != 0 || pipe(response) != 0) { + fprintf(stderr, "pipe failed: %s\n", strerror(errno)); + exit(1); + } + + pid = fork(); + if (pid < 0) { + fprintf(stderr, "fork failed: %s\n", strerror(errno)); + exit(1); + } + + if (pid != 0) { + struct agent *agent = malloc(sizeof(*agent)); + + close(command[0]); + close(response[1]); + agent->cmdfd = command[1]; + agent->responsefd = response[0]; + return agent; + } + + close(command[1]); + close(response[0]); + + /* We want to fail, not block. */ + nonblocking_locks = true; + log_prefix = "external: "; + while ((ret = read(command[0], name, sizeof(name))) > 0) { + enum agent_return result; + + result = do_operation(name[0], name+1); + if (write(response[1], &result, sizeof(result)) + != sizeof(result)) + abort(); + } + exit(0); +} + +/* Ask the external agent to try to do an operation. */ +enum agent_return external_agent_operation(struct agent *agent, + enum operation op, + const char *name) +{ + enum agent_return res; + unsigned int len; + char *string; + + if (!name) + name = ""; + len = 1 + strlen(name) + 1; + string = malloc(len); + + string[0] = op; + strcpy(string+1, name); + + if (write(agent->cmdfd, string, len) != len + || read(agent->responsefd, &res, sizeof(res)) != sizeof(res)) + res = AGENT_DIED; + + free(string); + return res; +} + +const char *agent_return_name(enum agent_return ret) +{ + return ret == SUCCESS ? "SUCCESS" + : ret == WOULD_HAVE_BLOCKED ? "WOULD_HAVE_BLOCKED" + : ret == AGENT_DIED ? "AGENT_DIED" + : ret == FAILED ? "FAILED" + : ret == OTHER_FAILURE ? "OTHER_FAILURE" + : "**INVALID**"; +} + +const char *operation_name(enum operation op) +{ + switch (op) { + case OPEN: return "OPEN"; + case OPEN_WITH_CLEAR_IF_FIRST: return "OPEN_WITH_CLEAR_IF_FIRST"; + case TRANSACTION_START: return "TRANSACTION_START"; + case FETCH: return "FETCH"; + case STORE: return "STORE"; + case TRANSACTION_COMMIT: return "TRANSACTION_COMMIT"; + case CHECK: return "CHECK"; + case NEEDS_RECOVERY: return "NEEDS_RECOVERY"; + case CLOSE: return "CLOSE"; + } + return "**INVALID**"; +} diff --git a/ctdb/lib/tdb/test/external-agent.h b/ctdb/lib/tdb/test/external-agent.h new file mode 100644 index 00000000000..dffdca962f6 --- /dev/null +++ b/ctdb/lib/tdb/test/external-agent.h @@ -0,0 +1,41 @@ +#ifndef TDB_TEST_EXTERNAL_AGENT_H +#define TDB_TEST_EXTERNAL_AGENT_H + +/* For locking tests, we need a different process to try things at + * various times. */ +enum operation { + OPEN, + OPEN_WITH_CLEAR_IF_FIRST, + TRANSACTION_START, + FETCH, + STORE, + TRANSACTION_COMMIT, + CHECK, + NEEDS_RECOVERY, + CLOSE, +}; + +/* Do this before doing any tdb stuff. Return handle, or -1. */ +struct agent *prepare_external_agent(void); + +enum agent_return { + SUCCESS, + WOULD_HAVE_BLOCKED, + AGENT_DIED, + FAILED, /* For fetch, or NEEDS_RECOVERY */ + OTHER_FAILURE, +}; + +/* Ask the external agent to try to do an operation. + * name == tdb name for OPEN/OPEN_WITH_CLEAR_IF_FIRST, + * record name for FETCH/STORE (store stores name as data too) + */ +enum agent_return external_agent_operation(struct agent *handle, + enum operation op, + const char *name); + +/* Mapping enum -> string. */ +const char *agent_return_name(enum agent_return ret); +const char *operation_name(enum operation op); + +#endif /* TDB_TEST_EXTERNAL_AGENT_H */ diff --git a/ctdb/lib/tdb/test/jenkins-be-hash.tdb b/ctdb/lib/tdb/test/jenkins-be-hash.tdb Binary files differnew file mode 100644 index 00000000000..b6528404148 --- /dev/null +++ b/ctdb/lib/tdb/test/jenkins-be-hash.tdb diff --git a/ctdb/lib/tdb/test/jenkins-le-hash.tdb b/ctdb/lib/tdb/test/jenkins-le-hash.tdb Binary files differnew file mode 100644 index 00000000000..007e0a33684 --- /dev/null +++ b/ctdb/lib/tdb/test/jenkins-le-hash.tdb diff --git a/ctdb/lib/tdb/test/lock-tracking.c b/ctdb/lib/tdb/test/lock-tracking.c new file mode 100644 index 00000000000..90a07f8ef16 --- /dev/null +++ b/ctdb/lib/tdb/test/lock-tracking.c @@ -0,0 +1,146 @@ +/* We save the locks so we can reaquire them. */ +#include "../common/tdb_private.h" +#include <unistd.h> +#include <fcntl.h> +#include <stdarg.h> +#include <stdlib.h> +#include "tap-interface.h" +#include "lock-tracking.h" + +struct testlock { + struct testlock *next; + unsigned int off; + unsigned int len; + int type; +}; +static struct testlock *testlocks; +int locking_errors = 0; +bool suppress_lockcheck = false; +bool nonblocking_locks; +int locking_would_block = 0; +void (*unlock_callback)(int fd); + +int fcntl_with_lockcheck(int fd, int cmd, ... /* arg */ ) +{ + va_list ap; + int ret, arg3; + struct flock *fl; + bool may_block = false; + + if (cmd != F_SETLK && cmd != F_SETLKW) { + /* This may be totally bogus, but we don't know in general. */ + va_start(ap, cmd); + arg3 = va_arg(ap, int); + va_end(ap); + + return fcntl(fd, cmd, arg3); + } + + va_start(ap, cmd); + fl = va_arg(ap, struct flock *); + va_end(ap); + + if (cmd == F_SETLKW && nonblocking_locks) { + cmd = F_SETLK; + may_block = true; + } + ret = fcntl(fd, cmd, fl); + + /* Detect when we failed, but might have been OK if we waited. */ + if (may_block && ret == -1 && (errno == EAGAIN || errno == EACCES)) { + locking_would_block++; + } + + if (fl->l_type == F_UNLCK) { + struct testlock **l; + struct testlock *old = NULL; + + for (l = &testlocks; *l; l = &(*l)->next) { + if ((*l)->off == fl->l_start + && (*l)->len == fl->l_len) { + if (ret == 0) { + old = *l; + *l = (*l)->next; + free(old); + } + break; + } + } + if (!old && !suppress_lockcheck) { + diag("Unknown unlock %u@%u - %i", + (int)fl->l_len, (int)fl->l_start, ret); + locking_errors++; + } + } else { + struct testlock *new, *i; + unsigned int fl_end = fl->l_start + fl->l_len; + if (fl->l_len == 0) + fl_end = (unsigned int)-1; + + /* Check for overlaps: we shouldn't do this. */ + for (i = testlocks; i; i = i->next) { + unsigned int i_end = i->off + i->len; + if (i->len == 0) + i_end = (unsigned int)-1; + + if (fl->l_start >= i->off && fl->l_start < i_end) + break; + if (fl_end >= i->off && fl_end < i_end) + break; + + /* tdb_allrecord_lock does this, handle adjacent: */ + if (fl->l_start == i_end && fl->l_type == i->type) { + if (ret == 0) { + i->len = fl->l_len + ? i->len + fl->l_len + : 0; + } + goto done; + } + } + if (i) { + /* Special case: upgrade of allrecord lock. */ + if (i->type == F_RDLCK && fl->l_type == F_WRLCK + && i->off == FREELIST_TOP + && fl->l_start == FREELIST_TOP + && i->len == 0 + && fl->l_len == 0) { + if (ret == 0) + i->type = F_WRLCK; + goto done; + } + if (!suppress_lockcheck) { + diag("%s testlock %u@%u overlaps %u@%u", + fl->l_type == F_WRLCK ? "write" : "read", + (int)fl->l_len, (int)fl->l_start, + i->len, (int)i->off); + locking_errors++; + } + } + + if (ret == 0) { + new = malloc(sizeof *new); + new->off = fl->l_start; + new->len = fl->l_len; + new->type = fl->l_type; + new->next = testlocks; + testlocks = new; + } + } +done: + if (ret == 0 && fl->l_type == F_UNLCK && unlock_callback) + unlock_callback(fd); + return ret; +} + +unsigned int forget_locking(void) +{ + unsigned int num = 0; + while (testlocks) { + struct testlock *next = testlocks->next; + free(testlocks); + testlocks = next; + num++; + } + return num; +} diff --git a/ctdb/lib/tdb/test/lock-tracking.h b/ctdb/lib/tdb/test/lock-tracking.h new file mode 100644 index 00000000000..f2c9c44653b --- /dev/null +++ b/ctdb/lib/tdb/test/lock-tracking.h @@ -0,0 +1,25 @@ +#ifndef LOCK_TRACKING_H +#define LOCK_TRACKING_H +#include <stdbool.h> + +/* Set this if you want a callback after fnctl unlock. */ +extern void (*unlock_callback)(int fd); + +/* Replacement fcntl. */ +int fcntl_with_lockcheck(int fd, int cmd, ... /* arg */ ); + +/* Discard locking info: returns number of locks outstanding. */ +unsigned int forget_locking(void); + +/* Number of errors in locking. */ +extern int locking_errors; + +/* Suppress lock checking. */ +extern bool suppress_lockcheck; + +/* Make all locks non-blocking. */ +extern bool nonblocking_locks; + +/* Number of times we failed a lock because we made it non-blocking. */ +extern int locking_would_block; +#endif /* LOCK_TRACKING_H */ diff --git a/ctdb/lib/tdb/test/logging.c b/ctdb/lib/tdb/test/logging.c new file mode 100644 index 00000000000..dfab4868d25 --- /dev/null +++ b/ctdb/lib/tdb/test/logging.c @@ -0,0 +1,33 @@ +#include "logging.h" +#include "tap-interface.h" +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +bool suppress_logging = false; +const char *log_prefix = ""; + +/* Turn log messages into tap diag messages. */ +static void taplog(struct tdb_context *tdb, + enum tdb_debug_level level, + const char *fmt, ...) +{ + va_list ap; + char line[200]; + + if (suppress_logging) + return; + + va_start(ap, fmt); + vsprintf(line, fmt, ap); + va_end(ap); + + /* Strip trailing \n: diag adds it. */ + if (line[0] && line[strlen(line)-1] == '\n') + diag("%s%.*s", log_prefix, (unsigned)strlen(line)-1, line); + else + diag("%s%s", log_prefix, line); +} + +struct tdb_logging_context taplogctx = { taplog, NULL }; diff --git a/ctdb/lib/tdb/test/logging.h b/ctdb/lib/tdb/test/logging.h new file mode 100644 index 00000000000..89e77b21080 --- /dev/null +++ b/ctdb/lib/tdb/test/logging.h @@ -0,0 +1,11 @@ +#ifndef TDB_TEST_LOGGING_H +#define TDB_TEST_LOGGING_H +#include "replace.h" +#include "../include/tdb.h" +#include <stdbool.h> + +extern bool suppress_logging; +extern const char *log_prefix; +extern struct tdb_logging_context taplogctx; + +#endif /* TDB_TEST_LOGGING_H */ diff --git a/ctdb/lib/tdb/test/old-nohash-be.tdb b/ctdb/lib/tdb/test/old-nohash-be.tdb Binary files differnew file mode 100644 index 00000000000..1c49116c1d9 --- /dev/null +++ b/ctdb/lib/tdb/test/old-nohash-be.tdb diff --git a/ctdb/lib/tdb/test/old-nohash-le.tdb b/ctdb/lib/tdb/test/old-nohash-le.tdb Binary files differnew file mode 100644 index 00000000000..0655072d882 --- /dev/null +++ b/ctdb/lib/tdb/test/old-nohash-le.tdb diff --git a/ctdb/lib/tdb/test/run-3G-file.c b/ctdb/lib/tdb/test/run-3G-file.c new file mode 100644 index 00000000000..3ee9de15ed7 --- /dev/null +++ b/ctdb/lib/tdb/test/run-3G-file.c @@ -0,0 +1,144 @@ +#include "../common/tdb_private.h" +#include "../common/io.c" +#include "../common/tdb.c" +#include "../common/lock.c" +#include "../common/freelist.c" +#include "../common/traverse.c" +#include "../common/transaction.c" +#include "../common/error.c" +#include "../common/open.c" +#include "../common/check.c" +#include "../common/hash.c" +#include "tap-interface.h" +#include <stdlib.h> +#include "logging.h" + +static int tdb_expand_file_sparse(struct tdb_context *tdb, + tdb_off_t size, + tdb_off_t addition) +{ + if (tdb->read_only || tdb->traverse_read) { + tdb->ecode = TDB_ERR_RDONLY; + return -1; + } + + if (ftruncate(tdb->fd, size+addition) == -1) { + char b = 0; + ssize_t written = pwrite(tdb->fd, &b, 1, (size+addition) - 1); + if (written == 0) { + /* try once more, potentially revealing errno */ + written = pwrite(tdb->fd, &b, 1, (size+addition) - 1); + } + if (written == 0) { + /* again - give up, guessing errno */ + errno = ENOSPC; + } + if (written != 1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "expand_file to %d failed (%s)\n", + size+addition, strerror(errno))); + return -1; + } + } + + return 0; +} + +static const struct tdb_methods large_io_methods = { + tdb_read, + tdb_write, + tdb_next_hash_chain, + tdb_oob, + tdb_expand_file_sparse +}; + +static int test_traverse(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data, + void *_data) +{ + TDB_DATA *expect = _data; + ok1(key.dsize == strlen("hi")); + ok1(memcmp(key.dptr, "hi", strlen("hi")) == 0); + ok1(data.dsize == expect->dsize); + ok1(memcmp(data.dptr, expect->dptr, data.dsize) == 0); + return 0; +} + +int main(int argc, char *argv[]) +{ + struct tdb_context *tdb; + TDB_DATA key, orig_data, data; + uint32_t hash; + tdb_off_t rec_ptr; + struct tdb_record rec; + int ret; + + plan_tests(24); + tdb = tdb_open_ex("run-36-file.tdb", 1024, TDB_CLEAR_IF_FIRST, + O_CREAT|O_TRUNC|O_RDWR, 0600, &taplogctx, NULL); + + ok1(tdb); + tdb->methods = &large_io_methods; + + key.dsize = strlen("hi"); + key.dptr = (void *)"hi"; + orig_data.dsize = strlen("world"); + orig_data.dptr = (void *)"world"; + + /* Enlarge the file (internally multiplies by 2). */ + ret = tdb_expand(tdb, 1500000000); +#ifdef HAVE_INCOHERENT_MMAP + /* This can fail due to mmap failure on 32 bit systems. */ + if (ret == -1) { + /* These should now fail. */ + ok1(tdb_store(tdb, key, orig_data, TDB_INSERT) == -1); + data = tdb_fetch(tdb, key); + ok1(data.dptr == NULL); + ok1(tdb_traverse(tdb, test_traverse, &orig_data) == -1); + ok1(tdb_delete(tdb, key) == -1); + ok1(tdb_traverse(tdb, test_traverse, NULL) == -1); + /* Skip the rest... */ + for (ret = 0; ret < 24 - 6; ret++) + ok1(1); + tdb_close(tdb); + return exit_status(); + } +#endif + ok1(ret == 0); + + /* Put an entry in, and check it. */ + ok1(tdb_store(tdb, key, orig_data, TDB_INSERT) == 0); + + data = tdb_fetch(tdb, key); + ok1(data.dsize == strlen("world")); + ok1(memcmp(data.dptr, "world", strlen("world")) == 0); + free(data.dptr); + + /* That currently fills at the end, make sure that's true. */ + hash = tdb->hash_fn(&key); + rec_ptr = tdb_find_lock_hash(tdb, key, hash, F_RDLCK, &rec); + ok1(rec_ptr); + ok1(rec_ptr > 2U*1024*1024*1024); + tdb_unlock(tdb, BUCKET(rec.full_hash), F_RDLCK); + + /* Traverse must work. */ + ok1(tdb_traverse(tdb, test_traverse, &orig_data) == 1); + + /* Delete should work. */ + ok1(tdb_delete(tdb, key) == 0); + + ok1(tdb_traverse(tdb, test_traverse, NULL) == 0); + + /* Transactions should work. */ + ok1(tdb_transaction_start(tdb) == 0); + ok1(tdb_store(tdb, key, orig_data, TDB_INSERT) == 0); + + data = tdb_fetch(tdb, key); + ok1(data.dsize == strlen("world")); + ok1(memcmp(data.dptr, "world", strlen("world")) == 0); + free(data.dptr); + ok1(tdb_transaction_commit(tdb) == 0); + + ok1(tdb_traverse(tdb, test_traverse, &orig_data) == 1); + tdb_close(tdb); + + return exit_status(); +} diff --git a/ctdb/lib/tdb/test/run-bad-tdb-header.c b/ctdb/lib/tdb/test/run-bad-tdb-header.c new file mode 100644 index 00000000000..b00fb8934a4 --- /dev/null +++ b/ctdb/lib/tdb/test/run-bad-tdb-header.c @@ -0,0 +1,58 @@ +#include "../common/tdb_private.h" +#include "../common/io.c" +#include "../common/tdb.c" +#include "../common/lock.c" +#include "../common/freelist.c" +#include "../common/traverse.c" +#include "../common/transaction.c" +#include "../common/error.c" +#include "../common/open.c" +#include "../common/check.c" +#include "../common/hash.c" +#include "tap-interface.h" +#include <stdlib.h> +#include "logging.h" + +int main(int argc, char *argv[]) +{ + struct tdb_context *tdb; + struct tdb_header hdr; + int fd; + + plan_tests(11); + /* Can open fine if complete crap, as long as O_CREAT. */ + fd = open("run-bad-tdb-header.tdb", O_RDWR|O_CREAT|O_TRUNC, 0600); + ok1(fd >= 0); + ok1(write(fd, "hello world", 11) == 11); + close(fd); + tdb = tdb_open_ex("run-bad-tdb-header.tdb", 1024, 0, O_RDWR, 0, + &taplogctx, NULL); + ok1(!tdb); + tdb = tdb_open_ex("run-bad-tdb-header.tdb", 1024, 0, O_CREAT|O_RDWR, + 0600, &taplogctx, NULL); + ok1(tdb); + tdb_close(tdb); + + /* Now, with wrong version it should *not* overwrite. */ + fd = open("run-bad-tdb-header.tdb", O_RDWR); + ok1(fd >= 0); + ok1(read(fd, &hdr, sizeof(hdr)) == sizeof(hdr)); + ok1(hdr.version == TDB_VERSION); + hdr.version++; + lseek(fd, 0, SEEK_SET); + ok1(write(fd, &hdr, sizeof(hdr)) == sizeof(hdr)); + close(fd); + + tdb = tdb_open_ex("run-bad-tdb-header.tdb", 1024, 0, O_RDWR|O_CREAT, + 0600, &taplogctx, NULL); + ok1(errno == EIO); + ok1(!tdb); + + /* With truncate, will be fine. */ + tdb = tdb_open_ex("run-bad-tdb-header.tdb", 1024, 0, + O_RDWR|O_CREAT|O_TRUNC, 0600, &taplogctx, NULL); + ok1(tdb); + tdb_close(tdb); + + return exit_status(); +} diff --git a/ctdb/lib/tdb/test/run-check.c b/ctdb/lib/tdb/test/run-check.c new file mode 100644 index 00000000000..05f7aecd083 --- /dev/null +++ b/ctdb/lib/tdb/test/run-check.c @@ -0,0 +1,64 @@ +#include "../common/tdb_private.h" +#include "../common/io.c" +#include "../common/tdb.c" +#include "../common/lock.c" +#include "../common/freelist.c" +#include "../common/traverse.c" +#include "../common/transaction.c" +#include "../common/error.c" +#include "../common/open.c" +#include "../common/check.c" +#include "../common/hash.c" +#include "tap-interface.h" +#include <stdlib.h> +#include "logging.h" + +int main(int argc, char *argv[]) +{ + struct tdb_context *tdb; + TDB_DATA key, data; + + plan_tests(13); + tdb = tdb_open_ex("run-check.tdb", 1, TDB_CLEAR_IF_FIRST, + O_CREAT|O_TRUNC|O_RDWR, 0600, &taplogctx, NULL); + + ok1(tdb); + ok1(tdb_check(tdb, NULL, NULL) == 0); + + key.dsize = strlen("hi"); + key.dptr = (void *)"hi"; + data.dsize = strlen("world"); + data.dptr = (void *)"world"; + + ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0); + ok1(tdb_check(tdb, NULL, NULL) == 0); + tdb_close(tdb); + + tdb = tdb_open_ex("run-check.tdb", 1024, 0, O_RDWR, 0, + &taplogctx, NULL); + ok1(tdb); + ok1(tdb_check(tdb, NULL, NULL) == 0); + tdb_close(tdb); + + tdb = tdb_open_ex("test/tdb.corrupt", 1024, 0, O_RDWR, 0, + &taplogctx, NULL); + ok1(tdb); + ok1(tdb_check(tdb, NULL, NULL) == -1); + ok1(tdb_error(tdb) == TDB_ERR_CORRUPT); + tdb_close(tdb); + + /* Big and little endian should work! */ + tdb = tdb_open_ex("test/old-nohash-le.tdb", 1024, 0, O_RDWR, 0, + &taplogctx, NULL); + ok1(tdb); + ok1(tdb_check(tdb, NULL, NULL) == 0); + tdb_close(tdb); + + tdb = tdb_open_ex("test/old-nohash-be.tdb", 1024, 0, O_RDWR, 0, + &taplogctx, NULL); + ok1(tdb); + ok1(tdb_check(tdb, NULL, NULL) == 0); + tdb_close(tdb); + + return exit_status(); +} diff --git a/ctdb/lib/tdb/test/run-corrupt.c b/ctdb/lib/tdb/test/run-corrupt.c new file mode 100644 index 00000000000..1a3c7691832 --- /dev/null +++ b/ctdb/lib/tdb/test/run-corrupt.c @@ -0,0 +1,131 @@ +#include "../common/tdb_private.h" +#include "../common/io.c" +#include "../common/tdb.c" +#include "../common/lock.c" +#include "../common/freelist.c" +#include "../common/traverse.c" +#include "../common/transaction.c" +#include "../common/error.c" +#include "../common/open.c" +#include "../common/check.c" +#include "../common/hash.c" +#include "tap-interface.h" +#include <stdlib.h> +#include "logging.h" + +static int check(TDB_DATA key, TDB_DATA data, void *private) +{ + unsigned int *sizes = private; + + if (key.dsize > strlen("hello")) + return -1; + if (memcmp(key.dptr, "hello", key.dsize) != 0) + return -1; + + if (data.dsize != strlen("world")) + return -1; + if (memcmp(data.dptr, "world", data.dsize) != 0) + return -1; + + sizes[0] += key.dsize; + sizes[1] += data.dsize; + return 0; +} + +static void tdb_flip_bit(struct tdb_context *tdb, unsigned int bit) +{ + unsigned int off = bit / CHAR_BIT; + unsigned char mask = (1 << (bit % CHAR_BIT)); + + if (tdb->map_ptr) + ((unsigned char *)tdb->map_ptr)[off] ^= mask; + else { + unsigned char c; + if (pread(tdb->fd, &c, 1, off) != 1) { + fprintf(stderr, "pread: %s\n", strerror(errno)); + exit(1); + } + c ^= mask; + if (pwrite(tdb->fd, &c, 1, off) != 1) { + fprintf(stderr, "pwrite: %s\n", strerror(errno)); + exit(1); + } + } +} + +static void check_test(struct tdb_context *tdb) +{ + TDB_DATA key, data; + unsigned int i, verifiable, corrupt, sizes[2], dsize, ksize; + + ok1(tdb_check(tdb, NULL, NULL) == 0); + + key.dptr = (void *)"hello"; + data.dsize = strlen("world"); + data.dptr = (void *)"world"; + + /* Key and data size respectively. */ + dsize = ksize = 0; + + /* 5 keys in hash size 2 means we'll have multichains. */ + for (key.dsize = 1; key.dsize <= 5; key.dsize++) { + ksize += key.dsize; + dsize += data.dsize; + if (tdb_store(tdb, key, data, TDB_INSERT) != 0) + abort(); + } + + /* This is how many bytes we expect to be verifiable. */ + /* From the file header. */ + verifiable = strlen(TDB_MAGIC_FOOD) + 1 + + 2 * sizeof(uint32_t) + 2 * sizeof(tdb_off_t) + + 2 * sizeof(uint32_t); + /* From the free list chain and hash chains. */ + verifiable += 3 * sizeof(tdb_off_t); + /* From the record headers & tailer */ + verifiable += 5 * (sizeof(struct tdb_record) + sizeof(uint32_t)); + /* The free block: we ignore datalen, keylen, full_hash. */ + verifiable += sizeof(struct tdb_record) - 3*sizeof(uint32_t) + + sizeof(uint32_t); + /* Our check function verifies the key and data. */ + verifiable += ksize + dsize; + + /* Flip one bit at a time, make sure it detects verifiable bytes. */ + for (i = 0, corrupt = 0; i < tdb->map_size * CHAR_BIT; i++) { + tdb_flip_bit(tdb, i); + memset(sizes, 0, sizeof(sizes)); + if (tdb_check(tdb, check, sizes) != 0) + corrupt++; + else if (sizes[0] != ksize || sizes[1] != dsize) + corrupt++; + tdb_flip_bit(tdb, i); + } + ok(corrupt == verifiable * CHAR_BIT, "corrupt %u should be %u", + corrupt, verifiable * CHAR_BIT); +} + +int main(int argc, char *argv[]) +{ + struct tdb_context *tdb; + + plan_tests(4); + /* This should use mmap. */ + tdb = tdb_open_ex("run-corrupt.tdb", 2, TDB_CLEAR_IF_FIRST, + O_CREAT|O_TRUNC|O_RDWR, 0600, &taplogctx, NULL); + + if (!tdb) + abort(); + check_test(tdb); + tdb_close(tdb); + + /* This should not. */ + tdb = tdb_open_ex("run-corrupt.tdb", 2, TDB_CLEAR_IF_FIRST|TDB_NOMMAP, + O_CREAT|O_TRUNC|O_RDWR, 0600, &taplogctx, NULL); + + if (!tdb) + abort(); + check_test(tdb); + tdb_close(tdb); + + return exit_status(); +} diff --git a/ctdb/lib/tdb/test/run-die-during-transaction.c b/ctdb/lib/tdb/test/run-die-during-transaction.c new file mode 100644 index 00000000000..6e3a70d4ae6 --- /dev/null +++ b/ctdb/lib/tdb/test/run-die-during-transaction.c @@ -0,0 +1,231 @@ +#include "../common/tdb_private.h" +#include "lock-tracking.h" +static ssize_t pwrite_check(int fd, const void *buf, size_t count, off_t offset); +static ssize_t write_check(int fd, const void *buf, size_t count); +static int ftruncate_check(int fd, off_t length); + +#define pwrite pwrite_check +#define write write_check +#define fcntl fcntl_with_lockcheck +#define ftruncate ftruncate_check + +#include "../common/io.c" +#include "../common/tdb.c" +#include "../common/lock.c" +#include "../common/freelist.c" +#include "../common/traverse.c" +#include "../common/transaction.c" +#include "../common/error.c" +#include "../common/open.c" +#include "../common/check.c" +#include "../common/hash.c" +#include "tap-interface.h" +#include <stdlib.h> +#include <stdbool.h> +#include <stdarg.h> +#include <setjmp.h> +#include "external-agent.h" +#include "logging.h" + +#undef write +#undef pwrite +#undef fcntl +#undef ftruncate + +static bool in_transaction; +static int target, current; +static jmp_buf jmpbuf; +#define TEST_DBNAME "run-die-during-transaction.tdb" +#define KEY_STRING "helloworld" + +static void maybe_die(int fd) +{ + if (in_transaction && current++ == target) { + longjmp(jmpbuf, 1); + } +} + +static ssize_t pwrite_check(int fd, + const void *buf, size_t count, off_t offset) +{ + ssize_t ret; + + maybe_die(fd); + + ret = pwrite(fd, buf, count, offset); + if (ret != count) + return ret; + + maybe_die(fd); + return ret; +} + +static ssize_t write_check(int fd, const void *buf, size_t count) +{ + ssize_t ret; + + maybe_die(fd); + + ret = write(fd, buf, count); + if (ret != count) + return ret; + + maybe_die(fd); + return ret; +} + +static int ftruncate_check(int fd, off_t length) +{ + int ret; + + maybe_die(fd); + + ret = ftruncate(fd, length); + + maybe_die(fd); + return ret; +} + +static bool test_death(enum operation op, struct agent *agent) +{ + struct tdb_context *tdb = NULL; + TDB_DATA key; + enum agent_return ret; + int needed_recovery = 0; + + current = target = 0; +reset: + unlink(TEST_DBNAME); + tdb = tdb_open_ex(TEST_DBNAME, 1024, TDB_NOMMAP, + O_CREAT|O_TRUNC|O_RDWR, 0600, &taplogctx, NULL); + + if (setjmp(jmpbuf) != 0) { + /* We're partway through. Simulate our death. */ + close(tdb->fd); + forget_locking(); + in_transaction = false; + + ret = external_agent_operation(agent, NEEDS_RECOVERY, ""); + if (ret == SUCCESS) + needed_recovery++; + else if (ret != FAILED) { + diag("Step %u agent NEEDS_RECOVERY = %s", current, + agent_return_name(ret)); + return false; + } + + ret = external_agent_operation(agent, op, KEY_STRING); + if (ret != SUCCESS) { + diag("Step %u op %s failed = %s", current, + operation_name(op), + agent_return_name(ret)); + return false; + } + + ret = external_agent_operation(agent, NEEDS_RECOVERY, ""); + if (ret != FAILED) { + diag("Still needs recovery after step %u = %s", + current, agent_return_name(ret)); + return false; + } + + ret = external_agent_operation(agent, CHECK, ""); + if (ret != SUCCESS) { + diag("Step %u check failed = %s", current, + agent_return_name(ret)); + return false; + } + + ret = external_agent_operation(agent, CLOSE, ""); + if (ret != SUCCESS) { + diag("Step %u close failed = %s", current, + agent_return_name(ret)); + return false; + } + + /* Suppress logging as this tries to use closed fd. */ + suppress_logging = true; + suppress_lockcheck = true; + tdb_close(tdb); + suppress_logging = false; + suppress_lockcheck = false; + target++; + current = 0; + goto reset; + } + + /* Put key for agent to fetch. */ + key.dsize = strlen(KEY_STRING); + key.dptr = (void *)KEY_STRING; + if (tdb_store(tdb, key, key, TDB_INSERT) != 0) + return false; + + /* This is the key we insert in transaction. */ + key.dsize--; + + ret = external_agent_operation(agent, OPEN, TEST_DBNAME); + if (ret != SUCCESS) { + fprintf(stderr, "Agent failed to open: %s\n", + agent_return_name(ret)); + exit(1); + } + + ret = external_agent_operation(agent, FETCH, KEY_STRING); + if (ret != SUCCESS) { + fprintf(stderr, "Agent failed find key: %s\n", + agent_return_name(ret)); + exit(1); + } + + in_transaction = true; + if (tdb_transaction_start(tdb) != 0) + return false; + + if (tdb_store(tdb, key, key, TDB_INSERT) != 0) + return false; + + if (tdb_transaction_commit(tdb) != 0) + return false; + + in_transaction = false; + + /* We made it! */ + diag("Completed %u runs", current); + tdb_close(tdb); + ret = external_agent_operation(agent, CLOSE, ""); + if (ret != SUCCESS) { + diag("Step %u close failed = %s", current, + agent_return_name(ret)); + return false; + } + +#ifdef HAVE_INCOHERENT_MMAP + /* This means we always mmap, which makes this test a noop. */ + ok1(1); +#else + ok1(needed_recovery); +#endif + ok1(locking_errors == 0); + ok1(forget_locking() == 0); + locking_errors = 0; + return true; +} + +int main(int argc, char *argv[]) +{ + enum operation ops[] = { FETCH, STORE, TRANSACTION_START }; + struct agent *agent; + int i; + + plan_tests(12); + unlock_callback = maybe_die; + + agent = prepare_external_agent(); + + for (i = 0; i < sizeof(ops)/sizeof(ops[0]); i++) { + diag("Testing %s after death", operation_name(ops[i])); + ok1(test_death(ops[i], agent)); + } + + return exit_status(); +} diff --git a/ctdb/lib/tdb/test/run-endian.c b/ctdb/lib/tdb/test/run-endian.c new file mode 100644 index 00000000000..b19ffd373f1 --- /dev/null +++ b/ctdb/lib/tdb/test/run-endian.c @@ -0,0 +1,63 @@ +#include "../common/tdb_private.h" +#include "../common/io.c" +#include "../common/tdb.c" +#include "../common/lock.c" +#include "../common/freelist.c" +#include "../common/traverse.c" +#include "../common/transaction.c" +#include "../common/error.c" +#include "../common/open.c" +#include "../common/check.c" +#include "../common/hash.c" +#include "tap-interface.h" +#include <stdlib.h> +#include "logging.h" + +int main(int argc, char *argv[]) +{ + struct tdb_context *tdb; + TDB_DATA key, data; + + plan_tests(13); + tdb = tdb_open_ex("run-endian.tdb", 1024, + TDB_CLEAR_IF_FIRST|TDB_CONVERT, + O_CREAT|O_TRUNC|O_RDWR, 0600, &taplogctx, NULL); + + ok1(tdb); + key.dsize = strlen("hi"); + key.dptr = (void *)"hi"; + data.dsize = strlen("world"); + data.dptr = (void *)"world"; + + ok1(tdb_store(tdb, key, data, TDB_MODIFY) < 0); + ok1(tdb_error(tdb) == TDB_ERR_NOEXIST); + ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0); + ok1(tdb_store(tdb, key, data, TDB_INSERT) < 0); + ok1(tdb_error(tdb) == TDB_ERR_EXISTS); + ok1(tdb_store(tdb, key, data, TDB_MODIFY) == 0); + + data = tdb_fetch(tdb, key); + ok1(data.dsize == strlen("world")); + ok1(memcmp(data.dptr, "world", strlen("world")) == 0); + free(data.dptr); + + key.dsize++; + data = tdb_fetch(tdb, key); + ok1(data.dptr == NULL); + tdb_close(tdb); + + /* Reopen: should read it */ + tdb = tdb_open_ex("run-endian.tdb", 1024, 0, O_RDWR, 0, + &taplogctx, NULL); + ok1(tdb); + + key.dsize = strlen("hi"); + key.dptr = (void *)"hi"; + data = tdb_fetch(tdb, key); + ok1(data.dsize == strlen("world")); + ok1(memcmp(data.dptr, "world", strlen("world")) == 0); + free(data.dptr); + tdb_close(tdb); + + return exit_status(); +} diff --git a/ctdb/lib/tdb/test/run-incompatible.c b/ctdb/lib/tdb/test/run-incompatible.c new file mode 100644 index 00000000000..628927c8868 --- /dev/null +++ b/ctdb/lib/tdb/test/run-incompatible.c @@ -0,0 +1,185 @@ +#include "../common/tdb_private.h" +#include "../common/io.c" +#include "../common/tdb.c" +#include "../common/lock.c" +#include "../common/freelist.c" +#include "../common/traverse.c" +#include "../common/transaction.c" +#include "../common/error.c" +#include "../common/open.c" +#include "../common/check.c" +#include "../common/hash.c" +#include "tap-interface.h" +#include <stdlib.h> + +static unsigned int tdb_dumb_hash(TDB_DATA *key) +{ + return key->dsize; +} + +static void log_fn(struct tdb_context *tdb, enum tdb_debug_level level, const char *fmt, ...) +{ + unsigned int *count = tdb_get_logging_private(tdb); + if (strstr(fmt, "hash")) + (*count)++; +} + +static unsigned int hdr_rwlocks(const char *fname) +{ + struct tdb_header hdr; + + int fd = open(fname, O_RDONLY); + if (fd == -1) + return -1; + + if (read(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) + return -1; + + close(fd); + return hdr.rwlocks; +} + +int main(int argc, char *argv[]) +{ + struct tdb_context *tdb; + unsigned int log_count, flags; + TDB_DATA d, r; + struct tdb_logging_context log_ctx = { log_fn, &log_count }; + + plan_tests(38 * 2); + + for (flags = 0; flags <= TDB_CONVERT; flags += TDB_CONVERT) { + unsigned int rwmagic = TDB_HASH_RWLOCK_MAGIC; + + if (flags & TDB_CONVERT) + tdb_convert(&rwmagic, sizeof(rwmagic)); + + /* Create an old-style hash. */ + log_count = 0; + tdb = tdb_open_ex("run-incompatible.tdb", 0, flags, + O_CREAT|O_RDWR|O_TRUNC, 0600, &log_ctx, + NULL); + ok1(tdb); + ok1(log_count == 0); + d.dptr = (void *)"Hello"; + d.dsize = 5; + ok1(tdb_store(tdb, d, d, TDB_INSERT) == 0); + tdb_close(tdb); + + /* Should not have marked rwlocks field. */ + ok1(hdr_rwlocks("run-incompatible.tdb") == 0); + + /* We can still open any old-style with incompat flag. */ + log_count = 0; + tdb = tdb_open_ex("run-incompatible.tdb", 0, + TDB_INCOMPATIBLE_HASH, + O_RDWR, 0600, &log_ctx, NULL); + ok1(tdb); + ok1(log_count == 0); + r = tdb_fetch(tdb, d); + ok1(r.dsize == 5); + free(r.dptr); + ok1(tdb_check(tdb, NULL, NULL) == 0); + tdb_close(tdb); + + log_count = 0; + tdb = tdb_open_ex("test/jenkins-le-hash.tdb", 0, 0, O_RDONLY, + 0, &log_ctx, tdb_jenkins_hash); + ok1(tdb); + ok1(log_count == 0); + ok1(tdb_check(tdb, NULL, NULL) == 0); + tdb_close(tdb); + + log_count = 0; + tdb = tdb_open_ex("test/jenkins-be-hash.tdb", 0, 0, O_RDONLY, + 0, &log_ctx, tdb_jenkins_hash); + ok1(tdb); + ok1(log_count == 0); + ok1(tdb_check(tdb, NULL, NULL) == 0); + tdb_close(tdb); + + /* OK, now create with incompatible flag, default hash. */ + log_count = 0; + tdb = tdb_open_ex("run-incompatible.tdb", 0, + flags|TDB_INCOMPATIBLE_HASH, + O_CREAT|O_RDWR|O_TRUNC, 0600, &log_ctx, + NULL); + ok1(tdb); + ok1(log_count == 0); + d.dptr = (void *)"Hello"; + d.dsize = 5; + ok1(tdb_store(tdb, d, d, TDB_INSERT) == 0); + tdb_close(tdb); + + /* Should have marked rwlocks field. */ + ok1(hdr_rwlocks("run-incompatible.tdb") == rwmagic); + + /* Cannot open with old hash. */ + log_count = 0; + tdb = tdb_open_ex("run-incompatible.tdb", 0, 0, + O_RDWR, 0600, &log_ctx, tdb_old_hash); + ok1(!tdb); + ok1(log_count == 1); + + /* Can open with jenkins hash. */ + log_count = 0; + tdb = tdb_open_ex("run-incompatible.tdb", 0, 0, + O_RDWR, 0600, &log_ctx, tdb_jenkins_hash); + ok1(tdb); + ok1(log_count == 0); + r = tdb_fetch(tdb, d); + ok1(r.dsize == 5); + free(r.dptr); + ok1(tdb_check(tdb, NULL, NULL) == 0); + tdb_close(tdb); + + /* Can open by letting it figure it out itself. */ + log_count = 0; + tdb = tdb_open_ex("run-incompatible.tdb", 0, 0, + O_RDWR, 0600, &log_ctx, NULL); + ok1(tdb); + ok1(log_count == 0); + r = tdb_fetch(tdb, d); + ok1(r.dsize == 5); + free(r.dptr); + ok1(tdb_check(tdb, NULL, NULL) == 0); + tdb_close(tdb); + + /* We can also use incompatible hash with other hashes. */ + log_count = 0; + tdb = tdb_open_ex("run-incompatible.tdb", 0, + flags|TDB_INCOMPATIBLE_HASH, + O_CREAT|O_RDWR|O_TRUNC, 0600, &log_ctx, + tdb_dumb_hash); + ok1(tdb); + ok1(log_count == 0); + d.dptr = (void *)"Hello"; + d.dsize = 5; + ok1(tdb_store(tdb, d, d, TDB_INSERT) == 0); + tdb_close(tdb); + + /* Should have marked rwlocks field. */ + ok1(hdr_rwlocks("run-incompatible.tdb") == rwmagic); + + /* It should not open if we don't specify. */ + log_count = 0; + tdb = tdb_open_ex("run-incompatible.tdb", 0, 0, O_RDWR, 0, + &log_ctx, NULL); + ok1(!tdb); + ok1(log_count == 1); + + /* Should reopen with correct hash. */ + log_count = 0; + tdb = tdb_open_ex("run-incompatible.tdb", 0, 0, O_RDWR, 0, + &log_ctx, tdb_dumb_hash); + ok1(tdb); + ok1(log_count == 0); + r = tdb_fetch(tdb, d); + ok1(r.dsize == 5); + free(r.dptr); + ok1(tdb_check(tdb, NULL, NULL) == 0); + tdb_close(tdb); + } + + return exit_status(); +} diff --git a/ctdb/lib/tdb/test/run-nested-transactions.c b/ctdb/lib/tdb/test/run-nested-transactions.c new file mode 100644 index 00000000000..8c84bcac836 --- /dev/null +++ b/ctdb/lib/tdb/test/run-nested-transactions.c @@ -0,0 +1,78 @@ +#include "../common/tdb_private.h" +#include "../common/io.c" +#include "../common/tdb.c" +#include "../common/lock.c" +#include "../common/freelist.c" +#include "../common/traverse.c" +#include "../common/transaction.c" +#include "../common/error.c" +#include "../common/open.c" +#include "../common/check.c" +#include "../common/hash.c" +#include "tap-interface.h" +#include <stdlib.h> +#include <stdbool.h> +#include "logging.h" + +int main(int argc, char *argv[]) +{ + struct tdb_context *tdb; + TDB_DATA key, data; + + plan_tests(27); + key.dsize = strlen("hi"); + key.dptr = (void *)"hi"; + + tdb = tdb_open_ex("run-nested-transactions.tdb", + 1024, TDB_CLEAR_IF_FIRST|TDB_DISALLOW_NESTING, + O_CREAT|O_TRUNC|O_RDWR, 0600, &taplogctx, NULL); + ok1(tdb); + + /* Nesting disallowed. */ + ok1(tdb_transaction_start(tdb) == 0); + data.dptr = (void *)"world"; + data.dsize = strlen("world"); + ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0); + data = tdb_fetch(tdb, key); + ok1(data.dsize == strlen("world")); + ok1(memcmp(data.dptr, "world", strlen("world")) == 0); + free(data.dptr); + ok1(tdb_transaction_start(tdb) != 0); + ok1(tdb_error(tdb) == TDB_ERR_NESTING); + + data = tdb_fetch(tdb, key); + ok1(data.dsize == strlen("world")); + ok1(memcmp(data.dptr, "world", strlen("world")) == 0); + free(data.dptr); + ok1(tdb_transaction_commit(tdb) == 0); + data = tdb_fetch(tdb, key); + ok1(data.dsize == strlen("world")); + ok1(memcmp(data.dptr, "world", strlen("world")) == 0); + free(data.dptr); + tdb_close(tdb); + + /* Nesting allowed by default */ + tdb = tdb_open_ex("run-nested-transactions.tdb", + 1024, TDB_DEFAULT, O_RDWR, 0, &taplogctx, NULL); + ok1(tdb); + + ok1(tdb_transaction_start(tdb) == 0); + ok1(tdb_transaction_start(tdb) == 0); + ok1(tdb_delete(tdb, key) == 0); + ok1(tdb_transaction_commit(tdb) == 0); + ok1(!tdb_exists(tdb, key)); + ok1(tdb_transaction_cancel(tdb) == 0); + /* Surprise! Kills inner "committed" transaction. */ + ok1(tdb_exists(tdb, key)); + + ok1(tdb_transaction_start(tdb) == 0); + ok1(tdb_transaction_start(tdb) == 0); + ok1(tdb_delete(tdb, key) == 0); + ok1(tdb_transaction_commit(tdb) == 0); + ok1(!tdb_exists(tdb, key)); + ok1(tdb_transaction_commit(tdb) == 0); + ok1(!tdb_exists(tdb, key)); + tdb_close(tdb); + + return exit_status(); +} diff --git a/ctdb/lib/tdb/test/run-nested-traverse.c b/ctdb/lib/tdb/test/run-nested-traverse.c new file mode 100644 index 00000000000..37d57c01258 --- /dev/null +++ b/ctdb/lib/tdb/test/run-nested-traverse.c @@ -0,0 +1,87 @@ +#include "../common/tdb_private.h" +#include "lock-tracking.h" +#define fcntl fcntl_with_lockcheck +#include "../common/io.c" +#include "../common/tdb.c" +#include "../common/lock.c" +#include "../common/freelist.c" +#include "../common/traverse.c" +#include "../common/transaction.c" +#include "../common/error.c" +#include "../common/open.c" +#include "../common/check.c" +#include "../common/hash.c" +#include "tap-interface.h" +#undef fcntl +#include <stdlib.h> +#include <stdbool.h> +#include "external-agent.h" +#include "logging.h" + +static struct agent *agent; + +static bool correct_key(TDB_DATA key) +{ + return key.dsize == strlen("hi") + && memcmp(key.dptr, "hi", key.dsize) == 0; +} + +static bool correct_data(TDB_DATA data) +{ + return data.dsize == strlen("world") + && memcmp(data.dptr, "world", data.dsize) == 0; +} + +static int traverse2(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data, + void *p) +{ + ok1(correct_key(key)); + ok1(correct_data(data)); + return 0; +} + +static int traverse1(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data, + void *p) +{ + ok1(correct_key(key)); + ok1(correct_data(data)); + ok1(external_agent_operation(agent, TRANSACTION_START, tdb_name(tdb)) + == WOULD_HAVE_BLOCKED); + tdb_traverse(tdb, traverse2, NULL); + + /* That should *not* release the transaction lock! */ + ok1(external_agent_operation(agent, TRANSACTION_START, tdb_name(tdb)) + == WOULD_HAVE_BLOCKED); + return 0; +} + +int main(int argc, char *argv[]) +{ + struct tdb_context *tdb; + TDB_DATA key, data; + + plan_tests(17); + agent = prepare_external_agent(); + + tdb = tdb_open_ex("run-nested-traverse.tdb", 1024, TDB_CLEAR_IF_FIRST, + O_CREAT|O_TRUNC|O_RDWR, 0600, &taplogctx, NULL); + ok1(tdb); + + ok1(external_agent_operation(agent, OPEN, tdb_name(tdb)) == SUCCESS); + ok1(external_agent_operation(agent, TRANSACTION_START, tdb_name(tdb)) + == SUCCESS); + ok1(external_agent_operation(agent, TRANSACTION_COMMIT, tdb_name(tdb)) + == SUCCESS); + + key.dsize = strlen("hi"); + key.dptr = (void *)"hi"; + data.dptr = (void *)"world"; + data.dsize = strlen("world"); + + ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0); + tdb_traverse(tdb, traverse1, NULL); + tdb_traverse_read(tdb, traverse1, NULL); + tdb_close(tdb); + + return exit_status(); +} diff --git a/ctdb/lib/tdb/test/run-no-lock-during-traverse.c b/ctdb/lib/tdb/test/run-no-lock-during-traverse.c new file mode 100644 index 00000000000..0a72282eb6d --- /dev/null +++ b/ctdb/lib/tdb/test/run-no-lock-during-traverse.c @@ -0,0 +1,113 @@ +#include "../common/tdb_private.h" +#include "lock-tracking.h" + +#define fcntl fcntl_with_lockcheck + +#include "../common/io.c" +#include "../common/tdb.c" +#include "../common/lock.c" +#include "../common/freelist.c" +#include "../common/traverse.c" +#include "../common/transaction.c" +#include "../common/error.c" +#include "../common/open.c" +#include "../common/check.c" +#include "../common/hash.c" +#include "tap-interface.h" +#include <stdlib.h> +#include "logging.h" + +#undef fcntl + +#define NUM_ENTRIES 10 + +static bool prepare_entries(struct tdb_context *tdb) +{ + unsigned int i; + TDB_DATA key, data; + + for (i = 0; i < NUM_ENTRIES; i++) { + key.dsize = sizeof(i); + key.dptr = (void *)&i; + data.dsize = strlen("world"); + data.dptr = (void *)"world"; + + if (tdb_store(tdb, key, data, 0) != 0) + return false; + } + return true; +} + +static void delete_entries(struct tdb_context *tdb) +{ + unsigned int i; + TDB_DATA key; + + for (i = 0; i < NUM_ENTRIES; i++) { + key.dsize = sizeof(i); + key.dptr = (void *)&i; + + ok1(tdb_delete(tdb, key) == 0); + } +} + +/* We don't know how many times this will run. */ +static int delete_other(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data, + void *private_data) +{ + unsigned int i; + memcpy(&i, key.dptr, 4); + i = (i + 1) % NUM_ENTRIES; + key.dptr = (void *)&i; + if (tdb_delete(tdb, key) != 0) + (*(int *)private_data)++; + return 0; +} + +static int delete_self(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data, + void *private_data) +{ + ok1(tdb_delete(tdb, key) == 0); + return 0; +} + +int main(int argc, char *argv[]) +{ + struct tdb_context *tdb; + int errors = 0; + + plan_tests(41); + tdb = tdb_open_ex("run-no-lock-during-traverse.tdb", + 1024, TDB_CLEAR_IF_FIRST, O_CREAT|O_TRUNC|O_RDWR, + 0600, &taplogctx, NULL); + + ok1(tdb); + ok1(prepare_entries(tdb)); + ok1(locking_errors == 0); + ok1(tdb_lockall(tdb) == 0); + ok1(locking_errors == 0); + tdb_traverse(tdb, delete_other, &errors); + ok1(errors == 0); + ok1(locking_errors == 0); + ok1(tdb_unlockall(tdb) == 0); + + ok1(prepare_entries(tdb)); + ok1(locking_errors == 0); + ok1(tdb_lockall(tdb) == 0); + ok1(locking_errors == 0); + tdb_traverse(tdb, delete_self, NULL); + ok1(locking_errors == 0); + ok1(tdb_unlockall(tdb) == 0); + + ok1(prepare_entries(tdb)); + ok1(locking_errors == 0); + ok1(tdb_lockall(tdb) == 0); + ok1(locking_errors == 0); + delete_entries(tdb); + ok1(locking_errors == 0); + ok1(tdb_unlockall(tdb) == 0); + + ok1(tdb_close(tdb) == 0); + + return exit_status(); +} diff --git a/ctdb/lib/tdb/test/run-oldhash.c b/ctdb/lib/tdb/test/run-oldhash.c new file mode 100644 index 00000000000..535336cb473 --- /dev/null +++ b/ctdb/lib/tdb/test/run-oldhash.c @@ -0,0 +1,49 @@ +#include "../common/tdb_private.h" +#include "../common/io.c" +#include "../common/tdb.c" +#include "../common/lock.c" +#include "../common/freelist.c" +#include "../common/traverse.c" +#include "../common/transaction.c" +#include "../common/error.c" +#include "../common/open.c" +#include "../common/check.c" +#include "../common/hash.c" +#include "tap-interface.h" +#include <stdlib.h> +#include "logging.h" + +int main(int argc, char *argv[]) +{ + struct tdb_context *tdb; + + plan_tests(8); + + /* Old format (with zeroes in the hash magic fields) should + * open with any hash (since we don't know what hash they used). */ + tdb = tdb_open_ex("test/old-nohash-le.tdb", 0, 0, O_RDWR, 0, + &taplogctx, NULL); + ok1(tdb); + ok1(tdb_check(tdb, NULL, NULL) == 0); + tdb_close(tdb); + + tdb = tdb_open_ex("test/old-nohash-be.tdb", 0, 0, O_RDWR, 0, + &taplogctx, NULL); + ok1(tdb); + ok1(tdb_check(tdb, NULL, NULL) == 0); + tdb_close(tdb); + + tdb = tdb_open_ex("test/old-nohash-le.tdb", 0, 0, O_RDWR, 0, + &taplogctx, tdb_jenkins_hash); + ok1(tdb); + ok1(tdb_check(tdb, NULL, NULL) == 0); + tdb_close(tdb); + + tdb = tdb_open_ex("test/old-nohash-be.tdb", 0, 0, O_RDWR, 0, + &taplogctx, tdb_jenkins_hash); + ok1(tdb); + ok1(tdb_check(tdb, NULL, NULL) == 0); + tdb_close(tdb); + + return exit_status(); +} diff --git a/ctdb/lib/tdb/test/run-open-during-transaction.c b/ctdb/lib/tdb/test/run-open-during-transaction.c new file mode 100644 index 00000000000..a825e6269d7 --- /dev/null +++ b/ctdb/lib/tdb/test/run-open-during-transaction.c @@ -0,0 +1,181 @@ +#include "../common/tdb_private.h" +#include "lock-tracking.h" + +static ssize_t pwrite_check(int fd, const void *buf, size_t count, off_t offset); +static ssize_t write_check(int fd, const void *buf, size_t count); +static int ftruncate_check(int fd, off_t length); + +#define pwrite pwrite_check +#define write write_check +#define fcntl fcntl_with_lockcheck +#define ftruncate ftruncate_check + +#include "../common/io.c" +#include "../common/tdb.c" +#include "../common/lock.c" +#include "../common/freelist.c" +#include "../common/traverse.c" +#include "../common/transaction.c" +#include "../common/error.c" +#include "../common/open.c" +#include "../common/check.c" +#include "../common/hash.c" +#include "tap-interface.h" +#include <stdlib.h> +#include <stdbool.h> +#include <stdarg.h> +#include "external-agent.h" +#include "logging.h" + +static struct agent *agent; +static bool opened; +static int errors = 0; +static bool clear_if_first; +#define TEST_DBNAME "run-open-during-transaction.tdb" + +#undef write +#undef pwrite +#undef fcntl +#undef ftruncate + +static bool is_same(const char *snapshot, const char *latest, off_t len) +{ + unsigned i; + + for (i = 0; i < len; i++) { + if (snapshot[i] != latest[i]) + return false; + } + return true; +} + +static bool compare_file(int fd, const char *snapshot, off_t snapshot_len) +{ + char *contents; + bool same; + + /* over-length read serves as length check. */ + contents = malloc(snapshot_len+1); + same = pread(fd, contents, snapshot_len+1, 0) == snapshot_len + && is_same(snapshot, contents, snapshot_len); + free(contents); + return same; +} + +static void check_file_intact(int fd) +{ + enum agent_return ret; + struct stat st; + char *contents; + + fstat(fd, &st); + contents = malloc(st.st_size); + if (pread(fd, contents, st.st_size, 0) != st.st_size) { + diag("Read fail"); + errors++; + return; + } + + /* Ask agent to open file. */ + ret = external_agent_operation(agent, clear_if_first ? + OPEN_WITH_CLEAR_IF_FIRST : + OPEN, + TEST_DBNAME); + + /* It's OK to open it, but it must not have changed! */ + if (!compare_file(fd, contents, st.st_size)) { + diag("Agent changed file after opening %s", + agent_return_name(ret)); + errors++; + } + + if (ret == SUCCESS) { + ret = external_agent_operation(agent, CLOSE, NULL); + if (ret != SUCCESS) { + diag("Agent failed to close tdb: %s", + agent_return_name(ret)); + errors++; + } + } else if (ret != WOULD_HAVE_BLOCKED) { + diag("Agent opening file gave %s", + agent_return_name(ret)); + errors++; + } + + free(contents); +} + +static void after_unlock(int fd) +{ + if (opened) + check_file_intact(fd); +} + +static ssize_t pwrite_check(int fd, + const void *buf, size_t count, off_t offset) +{ + if (opened) + check_file_intact(fd); + + return pwrite(fd, buf, count, offset); +} + +static ssize_t write_check(int fd, const void *buf, size_t count) +{ + if (opened) + check_file_intact(fd); + + return write(fd, buf, count); +} + +static int ftruncate_check(int fd, off_t length) +{ + if (opened) + check_file_intact(fd); + + return ftruncate(fd, length); + +} + +int main(int argc, char *argv[]) +{ + const int flags[] = { TDB_DEFAULT, + TDB_CLEAR_IF_FIRST, + TDB_NOMMAP, + TDB_CLEAR_IF_FIRST | TDB_NOMMAP }; + int i; + struct tdb_context *tdb; + TDB_DATA key, data; + + plan_tests(20); + agent = prepare_external_agent(); + + unlock_callback = after_unlock; + for (i = 0; i < sizeof(flags)/sizeof(flags[0]); i++) { + clear_if_first = (flags[i] & TDB_CLEAR_IF_FIRST); + diag("Test with %s and %s\n", + clear_if_first ? "CLEAR" : "DEFAULT", + (flags[i] & TDB_NOMMAP) ? "no mmap" : "mmap"); + unlink(TEST_DBNAME); + tdb = tdb_open_ex(TEST_DBNAME, 1024, flags[i], + O_CREAT|O_TRUNC|O_RDWR, 0600, + &taplogctx, NULL); + ok1(tdb); + + opened = true; + ok1(tdb_transaction_start(tdb) == 0); + key.dsize = strlen("hi"); + key.dptr = (void *)"hi"; + data.dptr = (void *)"world"; + data.dsize = strlen("world"); + + ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0); + ok1(tdb_transaction_commit(tdb) == 0); + ok(!errors, "We had %u open errors", errors); + + opened = false; + tdb_close(tdb); + } + + return exit_status(); +} diff --git a/ctdb/lib/tdb/test/run-readonly-check.c b/ctdb/lib/tdb/test/run-readonly-check.c new file mode 100644 index 00000000000..fdd9507cdad --- /dev/null +++ b/ctdb/lib/tdb/test/run-readonly-check.c @@ -0,0 +1,52 @@ +/* We should be able to tdb_check a O_RDONLY tdb, and we were previously allowed + * to tdb_check() inside a transaction (though that's paranoia!). */ +#include "../common/tdb_private.h" +#include "../common/io.c" +#include "../common/tdb.c" +#include "../common/lock.c" +#include "../common/freelist.c" +#include "../common/traverse.c" +#include "../common/transaction.c" +#include "../common/error.c" +#include "../common/open.c" +#include "../common/check.c" +#include "../common/hash.c" +#include "tap-interface.h" +#include <stdlib.h> +#include "logging.h" + +int main(int argc, char *argv[]) +{ + struct tdb_context *tdb; + TDB_DATA key, data; + + plan_tests(11); + tdb = tdb_open_ex("run-readonly-check.tdb", 1024, + TDB_DEFAULT, + O_CREAT|O_TRUNC|O_RDWR, 0600, &taplogctx, NULL); + + ok1(tdb); + key.dsize = strlen("hi"); + key.dptr = (void *)"hi"; + data.dsize = strlen("world"); + data.dptr = (void *)"world"; + + ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0); + ok1(tdb_check(tdb, NULL, NULL) == 0); + + /* We are also allowed to do a check inside a transaction. */ + ok1(tdb_transaction_start(tdb) == 0); + ok1(tdb_check(tdb, NULL, NULL) == 0); + ok1(tdb_close(tdb) == 0); + + tdb = tdb_open_ex("run-readonly-check.tdb", 1024, + TDB_DEFAULT, O_RDONLY, 0, &taplogctx, NULL); + + ok1(tdb); + ok1(tdb_store(tdb, key, data, TDB_MODIFY) == -1); + ok1(tdb_error(tdb) == TDB_ERR_RDONLY); + ok1(tdb_check(tdb, NULL, NULL) == 0); + ok1(tdb_close(tdb) == 0); + + return exit_status(); +} diff --git a/ctdb/lib/tdb/test/run-rescue-find_entry.c b/ctdb/lib/tdb/test/run-rescue-find_entry.c new file mode 100644 index 00000000000..25f4f1c05f4 --- /dev/null +++ b/ctdb/lib/tdb/test/run-rescue-find_entry.c @@ -0,0 +1,50 @@ +#include "../common/tdb_private.h" +#include "../common/io.c" +#include "../common/tdb.c" +#include "../common/lock.c" +#include "../common/freelist.c" +#include "../common/traverse.c" +#include "../common/transaction.c" +#include "../common/error.c" +#include "../common/open.c" +#include "../common/check.c" +#include "../common/hash.c" +#include "../common/rescue.c" +#include "tap-interface.h" +#include <stdlib.h> +#include "logging.h" + +#define NUM 20 + +/* Binary searches are deceptively simple: easy to screw up! */ +int main(int argc, char *argv[]) +{ + unsigned int i, j, n; + struct found f[NUM+1]; + struct found_table table; + + /* Set up array for searching. */ + for (i = 0; i < NUM+1; i++) { + f[i].head = i * 3; + } + table.arr = f; + + for (i = 0; i < NUM; i++) { + table.num = i; + for (j = 0; j < (i + 2) * 3; j++) { + n = find_entry(&table, j); + ok1(n <= i); + + /* If we were searching for something too large... */ + if (j > i*3) + ok1(n == i); + else { + /* It must give us something after j */ + ok1(f[n].head >= j); + ok1(n == 0 || f[n-1].head < j); + } + } + } + + return exit_status(); +} diff --git a/ctdb/lib/tdb/test/run-rescue.c b/ctdb/lib/tdb/test/run-rescue.c new file mode 100644 index 00000000000..a26c493972a --- /dev/null +++ b/ctdb/lib/tdb/test/run-rescue.c @@ -0,0 +1,126 @@ +#include "../common/tdb_private.h" +#include "../common/io.c" +#include "../common/tdb.c" +#include "../common/lock.c" +#include "../common/freelist.c" +#include "../common/traverse.c" +#include "../common/transaction.c" +#include "../common/error.c" +#include "../common/open.c" +#include "../common/check.c" +#include "../common/hash.c" +#include "../common/rescue.c" +#include "tap-interface.h" +#include <stdlib.h> +#include "logging.h" + +struct walk_data { + TDB_DATA key; + TDB_DATA data; + bool fail; + unsigned count; +}; + +static inline bool tdb_deq(TDB_DATA a, TDB_DATA b) +{ + return a.dsize == b.dsize && memcmp(a.dptr, b.dptr, a.dsize) == 0; +} + +static inline TDB_DATA tdb_mkdata(const void *p, size_t len) +{ + TDB_DATA d; + d.dptr = (void *)p; + d.dsize = len; + return d; +} + +static void walk(TDB_DATA key, TDB_DATA data, void *_wd) +{ + struct walk_data *wd = _wd; + + if (!tdb_deq(key, wd->key)) { + wd->fail = true; + } + + if (!tdb_deq(data, wd->data)) { + wd->fail = true; + } + wd->count++; +} + +static void count_records(TDB_DATA key, TDB_DATA data, void *_wd) +{ + struct walk_data *wd = _wd; + + if (!tdb_deq(key, wd->key) || !tdb_deq(data, wd->data)) + diag("%.*s::%.*s\n", + (int)key.dsize, key.dptr, (int)data.dsize, data.dptr); + wd->count++; +} + +static void log_fn(struct tdb_context *tdb, enum tdb_debug_level level, const char *fmt, ...) +{ + unsigned int *count = tdb_get_logging_private(tdb); + (*count)++; +} + +int main(int argc, char *argv[]) +{ + struct tdb_context *tdb; + struct walk_data wd; + unsigned int i, size, log_count = 0; + struct tdb_logging_context log_ctx = { log_fn, &log_count }; + + plan_tests(8); + tdb = tdb_open_ex("run-rescue.tdb", 1, TDB_CLEAR_IF_FIRST, + O_CREAT|O_TRUNC|O_RDWR, 0600, &log_ctx, NULL); + + wd.key.dsize = strlen("hi"); + wd.key.dptr = (void *)"hi"; + wd.data.dsize = strlen("world"); + wd.data.dptr = (void *)"world"; + wd.count = 0; + wd.fail = false; + + ok1(tdb_store(tdb, wd.key, wd.data, TDB_INSERT) == 0); + + ok1(tdb_rescue(tdb, walk, &wd) == 0); + ok1(!wd.fail); + ok1(wd.count == 1); + + /* Corrupt the database, walk should either get it or not. */ + size = tdb->map_size; + for (i = sizeof(struct tdb_header); i < size; i++) { + char c; + if (tdb->methods->tdb_read(tdb, i, &c, 1, false) != 0) + fail("Reading offset %i", i); + if (tdb->methods->tdb_write(tdb, i, "X", 1) != 0) + fail("Writing X at offset %i", i); + + wd.count = 0; + if (tdb_rescue(tdb, count_records, &wd) != 0) { + wd.fail = true; + break; + } + /* Could be 0 or 1. */ + if (wd.count > 1) { + wd.fail = true; + break; + } + if (tdb->methods->tdb_write(tdb, i, &c, 1) != 0) + fail("Restoring offset %i", i); + } + ok1(log_count == 0); + ok1(!wd.fail); + tdb_close(tdb); + + /* Now try our known-corrupt db. */ + tdb = tdb_open_ex("test/tdb.corrupt", 1024, 0, O_RDWR, 0, + &taplogctx, NULL); + wd.count = 0; + ok1(tdb_rescue(tdb, count_records, &wd) == 0); + ok1(wd.count == 1627); + tdb_close(tdb); + + return exit_status(); +} diff --git a/ctdb/lib/tdb/test/run-rwlock-check.c b/ctdb/lib/tdb/test/run-rwlock-check.c new file mode 100644 index 00000000000..8b8072db1e6 --- /dev/null +++ b/ctdb/lib/tdb/test/run-rwlock-check.c @@ -0,0 +1,45 @@ +#include "../common/tdb_private.h" +#include "../common/io.c" +#include "../common/tdb.c" +#include "../common/lock.c" +#include "../common/freelist.c" +#include "../common/traverse.c" +#include "../common/transaction.c" +#include "../common/error.c" +#include "../common/open.c" +#include "../common/check.c" +#include "../common/hash.c" +#include "tap-interface.h" +#include <stdlib.h> + +static void log_fn(struct tdb_context *tdb, enum tdb_debug_level level, const char *fmt, ...) +{ + unsigned int *count = tdb_get_logging_private(tdb); + if (strstr(fmt, "spinlocks")) + (*count)++; +} + +/* The code should barf on TDBs created with rwlocks. */ +int main(int argc, char *argv[]) +{ + struct tdb_context *tdb; + unsigned int log_count; + struct tdb_logging_context log_ctx = { log_fn, &log_count }; + + plan_tests(4); + + /* We should fail to open rwlock-using tdbs of either endian. */ + log_count = 0; + tdb = tdb_open_ex("test/rwlock-le.tdb", 0, 0, O_RDWR, 0, + &log_ctx, NULL); + ok1(!tdb); + ok1(log_count == 1); + + log_count = 0; + tdb = tdb_open_ex("test/rwlock-be.tdb", 0, 0, O_RDWR, 0, + &log_ctx, NULL); + ok1(!tdb); + ok1(log_count == 1); + + return exit_status(); +} diff --git a/ctdb/lib/tdb/test/run-summary.c b/ctdb/lib/tdb/test/run-summary.c new file mode 100644 index 00000000000..22312843e79 --- /dev/null +++ b/ctdb/lib/tdb/test/run-summary.c @@ -0,0 +1,64 @@ +#include "../common/tdb_private.h" +#include "../common/io.c" +#include "../common/tdb.c" +#include "../common/lock.c" +#include "../common/freelist.c" +#include "../common/traverse.c" +#include "../common/transaction.c" +#include "../common/error.c" +#include "../common/open.c" +#include "../common/check.c" +#include "../common/hash.c" +#include "../common/summary.c" +#include "tap-interface.h" +#include <stdlib.h> + +int main(int argc, char *argv[]) +{ + unsigned int i, j; + struct tdb_context *tdb; + int flags[] = { TDB_INTERNAL, TDB_DEFAULT, TDB_NOMMAP, + TDB_INTERNAL|TDB_CONVERT, TDB_CONVERT, + TDB_NOMMAP|TDB_CONVERT }; + TDB_DATA key = { (unsigned char *)&j, sizeof(j) }; + TDB_DATA data = { (unsigned char *)&j, sizeof(j) }; + char *summary; + + plan_tests(sizeof(flags) / sizeof(flags[0]) * 14); + for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) { + tdb = tdb_open("run-summary.tdb", 131, flags[i], + O_RDWR|O_CREAT|O_TRUNC, 0600); + ok1(tdb); + if (!tdb) + continue; + + /* Put some stuff in there. */ + for (j = 0; j < 500; j++) { + /* Make sure padding varies to we get some graphs! */ + data.dsize = j % (sizeof(j) + 1); + if (tdb_store(tdb, key, data, TDB_REPLACE) != 0) + fail("Storing in tdb"); + } + + summary = tdb_summary(tdb); + diag("%s", summary); + ok1(strstr(summary, "Size of file/data: ")); + ok1(strstr(summary, "Number of records: 500\n")); + ok1(strstr(summary, "Smallest/average/largest keys: 4/4/4\n")); + ok1(strstr(summary, "Smallest/average/largest data: 0/2/4\n")); + ok1(strstr(summary, "Smallest/average/largest padding: ")); + ok1(strstr(summary, "Number of dead records: 0\n")); + ok1(strstr(summary, "Number of free records: 1\n")); + ok1(strstr(summary, "Smallest/average/largest free records: ")); + ok1(strstr(summary, "Number of hash chains: 131\n")); + ok1(strstr(summary, "Smallest/average/largest hash chains: ")); + ok1(strstr(summary, "Number of uncoalesced records: 0\n")); + ok1(strstr(summary, "Smallest/average/largest uncoalesced runs: 0/0/0\n")); + ok1(strstr(summary, "Percentage keys/data/padding/free/dead/rechdrs&tailers/hashes: ")); + + free(summary); + tdb_close(tdb); + } + + return exit_status(); +} diff --git a/ctdb/lib/tdb/test/run-transaction-expand.c b/ctdb/lib/tdb/test/run-transaction-expand.c new file mode 100644 index 00000000000..1271d92b331 --- /dev/null +++ b/ctdb/lib/tdb/test/run-transaction-expand.c @@ -0,0 +1,119 @@ +#include "../common/tdb_private.h" + +/* Speed up the tests, but do the actual sync tests. */ +static unsigned int sync_counts = 0; +static inline int fake_fsync(int fd) +{ + sync_counts++; + return 0; +} +#define fsync fake_fsync + +#ifdef MS_SYNC +static inline int fake_msync(void *addr, size_t length, int flags) +{ + sync_counts++; + return 0; +} +#define msync fake_msync +#endif + +#ifdef HAVE_FDATASYNC +static inline int fake_fdatasync(int fd) +{ + sync_counts++; + return 0; +} +#define fdatasync fake_fdatasync +#endif + +#include "../common/io.c" +#include "../common/tdb.c" +#include "../common/lock.c" +#include "../common/freelist.c" +#include "../common/traverse.c" +#include "../common/transaction.c" +#include "../common/error.c" +#include "../common/open.c" +#include "../common/check.c" +#include "../common/hash.c" +#include "tap-interface.h" +#include <stdlib.h> +#include "logging.h" + +static void write_record(struct tdb_context *tdb, size_t extra_len, + TDB_DATA *data) +{ + TDB_DATA key; + key.dsize = strlen("hi"); + key.dptr = (void *)"hi"; + + data->dsize += extra_len; + tdb_transaction_start(tdb); + tdb_store(tdb, key, *data, TDB_REPLACE); + tdb_transaction_commit(tdb); +} + +int main(int argc, char *argv[]) +{ + struct tdb_context *tdb; + size_t i; + TDB_DATA data; + struct tdb_record rec; + tdb_off_t off; + + /* Do *not* suppress sync for this test; we do it ourselves. */ + unsetenv("TDB_NO_FSYNC"); + + plan_tests(5); + tdb = tdb_open_ex("run-transaction-expand.tdb", + 1024, TDB_CLEAR_IF_FIRST, + O_CREAT|O_TRUNC|O_RDWR, 0600, &taplogctx, NULL); + ok1(tdb); + + data.dsize = 0; + data.dptr = calloc(1000, getpagesize()); + + /* Simulate a slowly growing record. */ + for (i = 0; i < 1000; i++) + write_record(tdb, getpagesize(), &data); + + tdb_ofs_read(tdb, TDB_RECOVERY_HEAD, &off); + tdb_read(tdb, off, &rec, sizeof(rec), DOCONV()); + diag("TDB size = %zu, recovery = %llu-%llu", + (size_t)tdb->map_size, (unsigned long long)off, (unsigned long long)(off + sizeof(rec) + rec.rec_len)); + + /* We should only be about 5 times larger than largest record. */ + ok1(tdb->map_size < 6 * i * getpagesize()); + tdb_close(tdb); + + tdb = tdb_open_ex("run-transaction-expand.tdb", + 1024, TDB_CLEAR_IF_FIRST, + O_CREAT|O_TRUNC|O_RDWR, 0600, &taplogctx, NULL); + ok1(tdb); + + data.dsize = 0; + + /* Simulate a slowly growing record, repacking to keep + * recovery area at end. */ + for (i = 0; i < 1000; i++) { + write_record(tdb, getpagesize(), &data); + if (i % 10 == 0) + tdb_repack(tdb); + } + + tdb_ofs_read(tdb, TDB_RECOVERY_HEAD, &off); + tdb_read(tdb, off, &rec, sizeof(rec), DOCONV()); + diag("TDB size = %zu, recovery = %llu-%llu", + (size_t)tdb->map_size, (unsigned long long)off, (unsigned long long)(off + sizeof(rec) + rec.rec_len)); + + /* We should only be about 4 times larger than largest record. */ + ok1(tdb->map_size < 5 * i * getpagesize()); + + /* We should have synchronized multiple times. */ + ok1(sync_counts); + tdb_close(tdb); + free(data.dptr); + + return exit_status(); +} diff --git a/ctdb/lib/tdb/test/run-traverse-in-transaction.c b/ctdb/lib/tdb/test/run-traverse-in-transaction.c new file mode 100644 index 00000000000..bcdc3545cd5 --- /dev/null +++ b/ctdb/lib/tdb/test/run-traverse-in-transaction.c @@ -0,0 +1,86 @@ +#include "lock-tracking.h" +#include "../common/tdb_private.h" +#define fcntl fcntl_with_lockcheck +#include "../common/io.c" +#include "../common/tdb.c" +#include "../common/lock.c" +#include "../common/freelist.c" +#include "../common/traverse.c" +#include "../common/transaction.c" +#include "../common/error.c" +#include "../common/open.c" +#include "../common/check.c" +#include "../common/hash.c" +#include "tap-interface.h" +#undef fcntl_with_lockcheck +#include <stdlib.h> +#include <stdbool.h> +#include "external-agent.h" +#include "logging.h" + +static struct agent *agent; + +static bool correct_key(TDB_DATA key) +{ + return key.dsize == strlen("hi") + && memcmp(key.dptr, "hi", key.dsize) == 0; +} + +static bool correct_data(TDB_DATA data) +{ + return data.dsize == strlen("world") + && memcmp(data.dptr, "world", data.dsize) == 0; +} + +static int traverse(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data, + void *p) +{ + ok1(correct_key(key)); + ok1(correct_data(data)); + return 0; +} + +int main(int argc, char *argv[]) +{ + struct tdb_context *tdb; + TDB_DATA key, data; + + plan_tests(13); + agent = prepare_external_agent(); + + tdb = tdb_open_ex("run-traverse-in-transaction.tdb", + 1024, TDB_CLEAR_IF_FIRST, O_CREAT|O_TRUNC|O_RDWR, + 0600, &taplogctx, NULL); + ok1(tdb); + + key.dsize = strlen("hi"); + key.dptr = (void *)"hi"; + data.dptr = (void *)"world"; + data.dsize = strlen("world"); + + ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0); + + ok1(external_agent_operation(agent, OPEN, tdb_name(tdb)) == SUCCESS); + + ok1(tdb_transaction_start(tdb) == 0); + ok1(external_agent_operation(agent, TRANSACTION_START, tdb_name(tdb)) + == WOULD_HAVE_BLOCKED); + tdb_traverse(tdb, traverse, NULL); + + /* That should *not* release the transaction lock! */ + ok1(external_agent_operation(agent, TRANSACTION_START, tdb_name(tdb)) + == WOULD_HAVE_BLOCKED); + tdb_traverse_read(tdb, traverse, NULL); + + /* That should *not* release the transaction lock! */ + ok1(external_agent_operation(agent, TRANSACTION_START, tdb_name(tdb)) + == WOULD_HAVE_BLOCKED); + ok1(tdb_transaction_commit(tdb) == 0); + /* Now we should be fine. */ + ok1(external_agent_operation(agent, TRANSACTION_START, tdb_name(tdb)) + == SUCCESS); + + tdb_close(tdb); + + return exit_status(); +} diff --git a/ctdb/lib/tdb/test/run-wronghash-fail.c b/ctdb/lib/tdb/test/run-wronghash-fail.c new file mode 100644 index 00000000000..74bbc30ba6f --- /dev/null +++ b/ctdb/lib/tdb/test/run-wronghash-fail.c @@ -0,0 +1,120 @@ +#include "../common/tdb_private.h" +#include "../common/io.c" +#include "../common/tdb.c" +#include "../common/lock.c" +#include "../common/freelist.c" +#include "../common/traverse.c" +#include "../common/transaction.c" +#include "../common/error.c" +#include "../common/open.c" +#include "../common/check.c" +#include "../common/hash.c" +#include "tap-interface.h" +#include <stdlib.h> + +static void log_fn(struct tdb_context *tdb, enum tdb_debug_level level, const char *fmt, ...) +{ + unsigned int *count = tdb_get_logging_private(tdb); + if (strstr(fmt, "hash")) + (*count)++; +} + +int main(int argc, char *argv[]) +{ + struct tdb_context *tdb; + unsigned int log_count; + TDB_DATA d; + struct tdb_logging_context log_ctx = { log_fn, &log_count }; + + plan_tests(28); + + /* Create with default hash. */ + log_count = 0; + tdb = tdb_open_ex("run-wronghash-fail.tdb", 0, 0, + O_CREAT|O_RDWR|O_TRUNC, 0600, &log_ctx, NULL); + ok1(tdb); + ok1(log_count == 0); + d.dptr = (void *)"Hello"; + d.dsize = 5; + ok1(tdb_store(tdb, d, d, TDB_INSERT) == 0); + tdb_close(tdb); + + /* Fail to open with different hash. */ + tdb = tdb_open_ex("run-wronghash-fail.tdb", 0, 0, O_RDWR, 0, + &log_ctx, tdb_jenkins_hash); + ok1(!tdb); + ok1(log_count == 1); + + /* Create with different hash. */ + log_count = 0; + tdb = tdb_open_ex("run-wronghash-fail.tdb", 0, 0, + O_CREAT|O_RDWR|O_TRUNC, + 0600, &log_ctx, tdb_jenkins_hash); + ok1(tdb); + ok1(log_count == 0); + tdb_close(tdb); + + /* Endian should be no problem. */ + log_count = 0; + tdb = tdb_open_ex("test/jenkins-le-hash.tdb", 0, 0, O_RDWR, 0, + &log_ctx, tdb_old_hash); + ok1(!tdb); + ok1(log_count == 1); + + log_count = 0; + tdb = tdb_open_ex("test/jenkins-be-hash.tdb", 0, 0, O_RDWR, 0, + &log_ctx, tdb_old_hash); + ok1(!tdb); + ok1(log_count == 1); + + log_count = 0; + /* Fail to open with old default hash. */ + tdb = tdb_open_ex("run-wronghash-fail.tdb", 0, 0, O_RDWR, 0, + &log_ctx, tdb_old_hash); + ok1(!tdb); + ok1(log_count == 1); + + log_count = 0; + tdb = tdb_open_ex("test/jenkins-le-hash.tdb", 0, 0, O_RDONLY, + 0, &log_ctx, tdb_jenkins_hash); + ok1(tdb); + ok1(log_count == 0); + ok1(tdb_check(tdb, NULL, NULL) == 0); + tdb_close(tdb); + + log_count = 0; + tdb = tdb_open_ex("test/jenkins-be-hash.tdb", 0, 0, O_RDONLY, + 0, &log_ctx, tdb_jenkins_hash); + ok1(tdb); + ok1(log_count == 0); + ok1(tdb_check(tdb, NULL, NULL) == 0); + tdb_close(tdb); + + /* It should open with jenkins hash if we don't specify. */ + log_count = 0; + tdb = tdb_open_ex("test/jenkins-le-hash.tdb", 0, 0, O_RDWR, 0, + &log_ctx, NULL); + ok1(tdb); + ok1(log_count == 0); + ok1(tdb_check(tdb, NULL, NULL) == 0); + tdb_close(tdb); + + log_count = 0; + tdb = tdb_open_ex("test/jenkins-be-hash.tdb", 0, 0, O_RDWR, 0, + &log_ctx, NULL); + ok1(tdb); + ok1(log_count == 0); + ok1(tdb_check(tdb, NULL, NULL) == 0); + tdb_close(tdb); + + log_count = 0; + tdb = tdb_open_ex("run-wronghash-fail.tdb", 0, 0, O_RDONLY, + 0, &log_ctx, NULL); + ok1(tdb); + ok1(log_count == 0); + ok1(tdb_check(tdb, NULL, NULL) == 0); + tdb_close(tdb); + + + return exit_status(); +} diff --git a/ctdb/lib/tdb/test/run-zero-append.c b/ctdb/lib/tdb/test/run-zero-append.c new file mode 100644 index 00000000000..36bf6990f53 --- /dev/null +++ b/ctdb/lib/tdb/test/run-zero-append.c @@ -0,0 +1,40 @@ +#include "../common/tdb_private.h" +#include "../common/io.c" +#include "../common/tdb.c" +#include "../common/lock.c" +#include "../common/freelist.c" +#include "../common/traverse.c" +#include "../common/transaction.c" +#include "../common/error.c" +#include "../common/open.c" +#include "../common/check.c" +#include "../common/hash.c" +#include "tap-interface.h" +#include <stdlib.h> +#include "logging.h" + +int main(int argc, char *argv[]) +{ + struct tdb_context *tdb; + TDB_DATA key, data; + + plan_tests(4); + tdb = tdb_open_ex(NULL, 1024, TDB_INTERNAL, O_CREAT|O_TRUNC|O_RDWR, + 0600, &taplogctx, NULL); + ok1(tdb); + + /* Tickle bug on appending zero length buffer to zero length buffer. */ + key.dsize = strlen("hi"); + key.dptr = (void *)"hi"; + data.dptr = (void *)"world"; + data.dsize = 0; + + ok1(tdb_append(tdb, key, data) == 0); + ok1(tdb_append(tdb, key, data) == 0); + data = tdb_fetch(tdb, key); + ok1(data.dsize == 0); + tdb_close(tdb); + free(data.dptr); + + return exit_status(); +} diff --git a/ctdb/lib/tdb/test/run.c b/ctdb/lib/tdb/test/run.c new file mode 100644 index 00000000000..f49e85055e5 --- /dev/null +++ b/ctdb/lib/tdb/test/run.c @@ -0,0 +1,49 @@ +#include "../common/tdb_private.h" +#include "../common/io.c" +#include "../common/tdb.c" +#include "../common/lock.c" +#include "../common/freelist.c" +#include "../common/traverse.c" +#include "../common/transaction.c" +#include "../common/error.c" +#include "../common/open.c" +#include "../common/check.c" +#include "../common/hash.c" +#include "tap-interface.h" +#include <stdlib.h> +#include "logging.h" + +int main(int argc, char *argv[]) +{ + struct tdb_context *tdb; + TDB_DATA key, data; + + plan_tests(10); + tdb = tdb_open_ex("run.tdb", 1024, TDB_CLEAR_IF_FIRST, + O_CREAT|O_TRUNC|O_RDWR, 0600, &taplogctx, NULL); + + ok1(tdb); + key.dsize = strlen("hi"); + key.dptr = (void *)"hi"; + data.dsize = strlen("world"); + data.dptr = (void *)"world"; + + ok1(tdb_store(tdb, key, data, TDB_MODIFY) < 0); + ok1(tdb_error(tdb) == TDB_ERR_NOEXIST); + ok1(tdb_store(tdb, key, data, TDB_INSERT) == 0); + ok1(tdb_store(tdb, key, data, TDB_INSERT) < 0); + ok1(tdb_error(tdb) == TDB_ERR_EXISTS); + ok1(tdb_store(tdb, key, data, TDB_MODIFY) == 0); + + data = tdb_fetch(tdb, key); + ok1(data.dsize == strlen("world")); + ok1(memcmp(data.dptr, "world", strlen("world")) == 0); + free(data.dptr); + + key.dsize++; + data = tdb_fetch(tdb, key); + ok1(data.dptr == NULL); + tdb_close(tdb); + + return exit_status(); +} diff --git a/ctdb/lib/tdb/test/rwlock-be.tdb b/ctdb/lib/tdb/test/rwlock-be.tdb Binary files differnew file mode 100644 index 00000000000..45b5f09a1b6 --- /dev/null +++ b/ctdb/lib/tdb/test/rwlock-be.tdb diff --git a/ctdb/lib/tdb/test/rwlock-le.tdb b/ctdb/lib/tdb/test/rwlock-le.tdb Binary files differnew file mode 100644 index 00000000000..45b5f09a1b6 --- /dev/null +++ b/ctdb/lib/tdb/test/rwlock-le.tdb diff --git a/ctdb/lib/tdb/test/tap-interface.h b/ctdb/lib/tdb/test/tap-interface.h new file mode 100644 index 00000000000..d9ed6e84ea5 --- /dev/null +++ b/ctdb/lib/tdb/test/tap-interface.h @@ -0,0 +1,39 @@ +/* + Unix SMB/CIFS implementation. + Simplistic implementation of tap interface. + + Copyright (C) Rusty Russell 2012 + + ** NOTE! The following LGPL license applies to the talloc + ** library. This does NOT imply that all of Samba is released + ** under the LGPL + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see <http://www.gnu.org/licenses/>. +*/ +#include <stdio.h> + +#ifndef __location__ +#define __TAP_STRING_LINE1__(s) #s +#define __TAP_STRING_LINE2__(s) __TAP_STRING_LINE1__(s) +#define __TAP_STRING_LINE3__ __TAP_STRING_LINE2__(__LINE__) +#define __location__ __FILE__ ":" __TAP_STRING_LINE3__ +#endif + +#define plan_tests(num) +#define ok(e, ...) do { if (e) { (void)printf("."); } else { fprintf(stderr, __VA_ARGS__); exit(1); } } while(0) +#define ok1(e) ok((e), "%s:%s", __location__, #e) +#define pass(...) printf(".") +#define fail(...) do { fprintf(stderr, __VA_ARGS__); exit(1); } while(0) +#define diag printf +#define exit_status() 0 diff --git a/ctdb/lib/tdb/test/tap-to-subunit.h b/ctdb/lib/tdb/test/tap-to-subunit.h new file mode 100644 index 00000000000..a5cf74fb046 --- /dev/null +++ b/ctdb/lib/tdb/test/tap-to-subunit.h @@ -0,0 +1,155 @@ +#ifndef TAP_TO_SUBUNIT_H +#define TAP_TO_SUBUNIT_H +/* + * tap-style wrapper for subunit. + * + * Copyright (c) 2011 Rusty Russell + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include "replace.h" + +/** + * plan_tests - announce the number of tests you plan to run + * @tests: the number of tests + * + * This should be the first call in your test program: it allows tracing + * of failures which mean that not all tests are run. + * + * If you don't know how many tests will actually be run, assume all of them + * and use skip() if you don't actually run some tests. + * + * Example: + * plan_tests(13); + */ +void plan_tests(unsigned int tests); + +/** + * ok1 - Simple conditional test + * @e: the expression which we expect to be true. + * + * This is the simplest kind of test: if the expression is true, the + * test passes. The name of the test which is printed will simply be + * file name, line number, and the expression itself. + * + * Example: + * ok1(somefunc() == 1); + */ +# define ok1(e) ((e) ? \ + _gen_result(1, __func__, __FILE__, __LINE__, "%s", #e) : \ + _gen_result(0, __func__, __FILE__, __LINE__, "%s", #e)) + +/** + * ok - Conditional test with a name + * @e: the expression which we expect to be true. + * @...: the printf-style name of the test. + * + * If the expression is true, the test passes. The name of the test will be + * the filename, line number, and the printf-style string. This can be clearer + * than simply the expression itself. + * + * Example: + * ok1(somefunc() == 1); + * ok(somefunc() == 0, "Second somefunc() should fail"); + */ +# define ok(e, ...) ((e) ? \ + _gen_result(1, __func__, __FILE__, __LINE__, \ + __VA_ARGS__) : \ + _gen_result(0, __func__, __FILE__, __LINE__, \ + __VA_ARGS__)) + +/** + * pass - Note that a test passed + * @...: the printf-style name of the test. + * + * For complicated code paths, it can be easiest to simply call pass() in one + * branch and fail() in another. + * + * Example: + * int x = somefunc(); + * if (x > 0) + * pass("somefunc() returned a valid value"); + * else + * fail("somefunc() returned an invalid value"); + */ +# define pass(...) ok(1, __VA_ARGS__) + +/** + * fail - Note that a test failed + * @...: the printf-style name of the test. + * + * For complicated code paths, it can be easiest to simply call pass() in one + * branch and fail() in another. + */ +# define fail(...) ok(0, __VA_ARGS__) + +unsigned int _gen_result(int, const char *, const char *, unsigned int, + const char *, ...) PRINTF_ATTRIBUTE(5, 6); + +/** + * diag - print a diagnostic message (use instead of printf/fprintf) + * @fmt: the format of the printf-style message + * + * diag ensures that the output will not be considered to be a test + * result by the TAP test harness. It will append '\n' for you. + * + * Example: + * diag("Now running complex tests"); + */ +void diag(const char *fmt, ...) PRINTF_ATTRIBUTE(1, 2); + +/** + * skip - print a diagnostic message (use instead of printf/fprintf) + * @n: number of tests you're skipping. + * @fmt: the format of the reason you're skipping the tests. + * + * Sometimes tests cannot be run because the test system lacks some feature: + * you should explicitly document that you're skipping tests using skip(). + * + * From the Test::More documentation: + * If it's something the user might not be able to do, use SKIP. This + * includes optional modules that aren't installed, running under an OS that + * doesn't have some feature (like fork() or symlinks), or maybe you need an + * Internet connection and one isn't available. + * + * Example: + * #ifdef HAVE_SOME_FEATURE + * ok1(somefunc()); + * #else + * skip(1, "Don't have SOME_FEATURE"); + * #endif + */ +void skip(unsigned int n, const char *fmt, ...) PRINTF_ATTRIBUTE(2, 3); + +/** + * exit_status - the value that main should return. + * + * For maximum compatibility your test program should return a particular exit + * code (ie. 0 if all tests were run, and every test which was expected to + * succeed succeeded). + * + * Example: + * exit(exit_status()); + */ +int exit_status(void); +#endif /* CCAN_TAP_H */ diff --git a/ctdb/lib/tdb/test/tdb.corrupt b/ctdb/lib/tdb/test/tdb.corrupt Binary files differnew file mode 100644 index 00000000000..83d66774543 --- /dev/null +++ b/ctdb/lib/tdb/test/tdb.corrupt diff --git a/ctdb/lib/tdb/tools/tdbdump.c b/ctdb/lib/tdb/tools/tdbdump.c index 027fda3d424..bcd395fd5af 100644 --- a/ctdb/lib/tdb/tools/tdbdump.c +++ b/ctdb/lib/tdb/tools/tdbdump.c @@ -51,19 +51,69 @@ static int traverse_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void *stat return 0; } -static int dump_tdb(const char *fname, const char *keyname) +static void log_stderr(struct tdb_context *tdb, enum tdb_debug_level level, + const char *fmt, ...) +{ + va_list ap; + char *ptr = NULL; + int debuglevel = 0; + int ret; + const char *name = tdb_name(tdb); + const char *prefix = ""; + + if (!name) + name = "unnamed"; + + switch (level) { + case TDB_DEBUG_ERROR: + prefix = "ERROR: "; + break; + case TDB_DEBUG_WARNING: + prefix = "WARNING: "; + break; + case TDB_DEBUG_TRACE: + return; + + default: + case TDB_DEBUG_FATAL: + prefix = "FATAL: "; + break; + } + + va_start(ap, fmt); + fprintf(stderr, "tdb(%s): %s", name, prefix); + vfprintf(stderr, fmt, ap); + va_end(ap); +} + +static void emergency_walk(TDB_DATA key, TDB_DATA dbuf, void *keyname) +{ + if (keyname) { + if (key.dsize != strlen(keyname)) + return; + if (memcmp(key.dptr, keyname, key.dsize) != 0) + return; + } + traverse_fn(NULL, key, dbuf, NULL); +} + +static int dump_tdb(const char *fname, const char *keyname, bool emergency) { TDB_CONTEXT *tdb; TDB_DATA key, value; + struct tdb_logging_context logfn = { log_stderr }; - tdb = tdb_open(fname, 0, 0, O_RDONLY, 0); + tdb = tdb_open_ex(fname, 0, 0, O_RDONLY, 0, &logfn, NULL); if (!tdb) { printf("Failed to open %s\n", fname); return 1; } + if (emergency) { + return tdb_rescue(tdb, emergency_walk, keyname) == 0; + } if (!keyname) { - tdb_traverse(tdb, traverse_fn, NULL); + return tdb_traverse(tdb, traverse_fn, NULL) == -1 ? 1 : 0; } else { key.dptr = discard_const_p(uint8_t, keyname); key.dsize = strlen(keyname); @@ -84,11 +134,13 @@ static void usage( void) printf( "Usage: tdbdump [options] <filename>\n\n"); printf( " -h this help message\n"); printf( " -k keyname dumps value of keyname\n"); + printf( " -e emergency dump, for corrupt databases\n"); } int main(int argc, char *argv[]) { char *fname, *keyname=NULL; + bool emergency = false; int c; if (argc < 2) { @@ -96,7 +148,7 @@ static void usage( void) exit(1); } - while ((c = getopt( argc, argv, "hk:")) != -1) { + while ((c = getopt( argc, argv, "hk:e")) != -1) { switch (c) { case 'h': usage(); @@ -104,6 +156,9 @@ static void usage( void) case 'k': keyname = optarg; break; + case 'e': + emergency = true; + break; default: usage(); exit( 1); @@ -112,5 +167,5 @@ static void usage( void) fname = argv[optind]; - return dump_tdb(fname, keyname); + return dump_tdb(fname, keyname, emergency); } diff --git a/ctdb/lib/tdb/tools/tdbrestore.c b/ctdb/lib/tdb/tools/tdbrestore.c index 8106cf9b06a..f65b36fa12e 100644 --- a/ctdb/lib/tdb/tools/tdbrestore.c +++ b/ctdb/lib/tdb/tools/tdbrestore.c @@ -25,8 +25,6 @@ #include "system/wait.h" #include "tdb.h" -#define debug_fprintf(file, fmt, ...) do {/*nothing*/} while (0) - static int read_linehead(FILE *f) { int i, c; diff --git a/ctdb/lib/tdb/wscript b/ctdb/lib/tdb/wscript new file mode 100644 index 00000000000..9d309a0325c --- /dev/null +++ b/ctdb/lib/tdb/wscript @@ -0,0 +1,224 @@ +#!/usr/bin/env python + +APPNAME = 'tdb' +VERSION = '1.2.11' + +blddir = 'bin' + +import sys, os + +# find the buildtools directory +srcdir = '.' +while not os.path.exists(srcdir+'/buildtools') and len(srcdir.split('/')) < 5: + srcdir = '../' + srcdir +sys.path.insert(0, srcdir + '/buildtools/wafsamba') + +import wafsamba, samba_dist, Options, Logs + +samba_dist.DIST_DIRS('lib/tdb:. lib/replace:lib/replace buildtools:buildtools') + +def set_options(opt): + opt.BUILTIN_DEFAULT('replace') + opt.PRIVATE_EXTENSION_DEFAULT('tdb', noextension='tdb') + opt.RECURSE('lib/replace') + if opt.IN_LAUNCH_DIR(): + opt.add_option('--disable-python', + help=("disable the pytdb module"), + action="store_true", dest='disable_python', default=False) + + +def configure(conf): + conf.RECURSE('lib/replace') + + conf.env.standalone_tdb = conf.IN_LAUNCH_DIR() + conf.env.building_tdb = True + + if not conf.env.standalone_tdb: + if conf.CHECK_BUNDLED_SYSTEM_PKG('tdb', minversion=VERSION, + implied_deps='replace'): + conf.define('USING_SYSTEM_TDB', 1) + conf.env.building_tdb = False + if conf.CHECK_BUNDLED_SYSTEM_PYTHON('pytdb', 'tdb', minversion=VERSION): + conf.define('USING_SYSTEM_PYTDB', 1) + + conf.env.disable_python = getattr(Options.options, 'disable_python', False) + + conf.CHECK_XSLTPROC_MANPAGES() + + if not conf.env.disable_python: + # also disable if we don't have the python libs installed + conf.find_program('python', var='PYTHON') + conf.check_tool('python') + conf.check_python_version((2,4,2)) + conf.SAMBA_CHECK_PYTHON_HEADERS(mandatory=False) + if not conf.env.HAVE_PYTHON_H: + Logs.warn('Disabling pytdb as python devel libs not found') + conf.env.disable_python = True + + conf.SAMBA_CONFIG_H() + + conf.SAMBA_CHECK_UNDEFINED_SYMBOL_FLAGS() + +def build(bld): + bld.RECURSE('lib/replace') + + COMMON_SRC = bld.SUBDIR('common', + '''check.c error.c tdb.c traverse.c + freelistcheck.c lock.c dump.c freelist.c + io.c open.c transaction.c hash.c summary.c rescue.c''') + + if bld.env.standalone_tdb: + bld.env.PKGCONFIGDIR = '${LIBDIR}/pkgconfig' + private_library = False + else: + private_library = True + + if not bld.CONFIG_SET('USING_SYSTEM_TDB'): + bld.SAMBA_LIBRARY('tdb', + COMMON_SRC, + deps='replace', + includes='include', + abi_directory='ABI', + abi_match='tdb_*', + hide_symbols=True, + vnum=VERSION, + public_headers='include/tdb.h', + public_headers_install=not private_library, + pc_files='tdb.pc', + private_library=private_library) + + bld.SAMBA_BINARY('tdbtorture', + 'tools/tdbtorture.c', + 'tdb', + install=False) + + bld.SAMBA_BINARY('tdbrestore', + 'tools/tdbrestore.c', + 'tdb', manpages='manpages/tdbrestore.8') + + bld.SAMBA_BINARY('tdbdump', + 'tools/tdbdump.c', + 'tdb', manpages='manpages/tdbdump.8') + + bld.SAMBA_BINARY('tdbbackup', + 'tools/tdbbackup.c', + 'tdb', + manpages='manpages/tdbbackup.8') + + bld.SAMBA_BINARY('tdbtool', + 'tools/tdbtool.c', + 'tdb', manpages='manpages/tdbtool.8') + + # FIXME: This hardcoded list is stupid, stupid, stupid. + bld.SAMBA_SUBSYSTEM('tdb-test-helpers', + 'test/external-agent.c test/lock-tracking.c test/logging.c', + 'replace', + includes='include') + + bld.SAMBA_BINARY('tdb1-run-3G-file', 'test/run-3G-file.c', + 'replace tdb-test-helpers', includes='include', install=False) + bld.SAMBA_BINARY('tdb1-run-bad-tdb-header', 'test/run-bad-tdb-header.c', + 'replace tdb-test-helpers', includes='include', install=False) + bld.SAMBA_BINARY('tdb1-run', 'test/run.c', + 'replace tdb-test-helpers', includes='include', install=False) + bld.SAMBA_BINARY('tdb1-run-check', 'test/run-check.c', + 'replace tdb-test-helpers', includes='include', install=False) + bld.SAMBA_BINARY('tdb1-run-corrupt', 'test/run-corrupt.c', + 'replace tdb-test-helpers', includes='include', install=False) + bld.SAMBA_BINARY('tdb1-run-die-during-transaction', 'test/run-die-during-transaction.c', + 'replace tdb-test-helpers', includes='include', install=False) + bld.SAMBA_BINARY('tdb1-run-endian', 'test/run-endian.c', + 'replace tdb-test-helpers', includes='include', install=False) + bld.SAMBA_BINARY('tdb1-run-incompatible', 'test/run-incompatible.c', + 'replace tdb-test-helpers', includes='include', install=False) + bld.SAMBA_BINARY('tdb1-run-nested-transactions', 'test/run-nested-transactions.c', + 'replace tdb-test-helpers', includes='include', install=False) + bld.SAMBA_BINARY('tdb1-run-nested-traverse', 'test/run-nested-traverse.c', + 'replace tdb-test-helpers', includes='include', install=False) + bld.SAMBA_BINARY('tdb1-run-no-lock-during-traverse', 'test/run-no-lock-during-traverse.c', + 'replace tdb-test-helpers', includes='include', install=False) + bld.SAMBA_BINARY('tdb1-run-oldhash', 'test/run-oldhash.c', + 'replace tdb-test-helpers', includes='include', install=False) + bld.SAMBA_BINARY('tdb1-run-open-during-transaction', 'test/run-open-during-transaction.c', + 'replace tdb-test-helpers', includes='include', install=False) + bld.SAMBA_BINARY('tdb1-run-readonly-check', 'test/run-readonly-check.c', + 'replace tdb-test-helpers', includes='include', install=False) + bld.SAMBA_BINARY('tdb1-run-rescue', 'test/run-rescue.c', + 'replace tdb-test-helpers', includes='include', install=False) + bld.SAMBA_BINARY('tdb1-run-rescue-find_entry', 'test/run-rescue-find_entry.c', + 'replace tdb-test-helpers', includes='include', install=False) + bld.SAMBA_BINARY('tdb1-run-rwlock-check', 'test/run-rwlock-check.c', + 'replace tdb-test-helpers', includes='include', install=False) + bld.SAMBA_BINARY('tdb1-run-summary', 'test/run-summary.c', + 'replace tdb-test-helpers', includes='include', install=False) + bld.SAMBA_BINARY('tdb1-run-transaction-expand', 'test/run-transaction-expand.c', + 'replace tdb-test-helpers', includes='include', install=False) + bld.SAMBA_BINARY('tdb1-run-traverse-in-transaction', 'test/run-traverse-in-transaction.c', + 'replace tdb-test-helpers', includes='include', install=False) + bld.SAMBA_BINARY('tdb1-run-wronghash-fail', 'test/run-wronghash-fail.c', + 'replace tdb-test-helpers', includes='include', install=False) + bld.SAMBA_BINARY('tdb1-run-zero-append', 'test/run-zero-append.c', + 'replace tdb-test-helpers', includes='include', install=False) + + if not bld.CONFIG_SET('USING_SYSTEM_PYTDB'): + bld.SAMBA_PYTHON('pytdb', + 'pytdb.c', + deps='tdb', + enabled=not bld.env.disable_python, + realname='tdb.so', + cflags='-DPACKAGE_VERSION=\"%s\"' % VERSION) + +def testonly(ctx): + '''run tdb testsuite''' + import Utils, samba_utils, shutil + ecode = 0 + + test_prefix = "%s/st" % (Utils.g_module.blddir) + shutil.rmtree(test_prefix, ignore_errors=True) + os.makedirs(test_prefix) + os.environ['TEST_DATA_PREFIX'] = test_prefix + + env = samba_utils.LOAD_ENVIRONMENT() + # FIXME: This is horrible :( + if env.building_tdb: + # Create scratch directory for tests. + testdir = os.path.join(test_prefix, 'tdb-tests') + samba_utils.mkdir_p(testdir) + # Symlink back to source dir so it can find tests in test/ + link = os.path.join(testdir, 'test') + if not os.path.exists(link): + os.symlink(os.path.abspath(os.path.join(env.cwd, 'test')), link) + + for f in 'tdb1-run-3G-file', 'tdb1-run-bad-tdb-header', 'tdb1-run', 'tdb1-run-check', 'tdb1-run-corrupt', 'tdb1-run-die-during-transaction', 'tdb1-run-endian', 'tdb1-run-incompatible', 'tdb1-run-nested-transactions', 'tdb1-run-nested-traverse', 'tdb1-run-no-lock-during-traverse', 'tdb1-run-oldhash', 'tdb1-run-open-during-transaction', 'tdb1-run-readonly-check', 'tdb1-run-rescue', 'tdb1-run-rescue-find_entry', 'tdb1-run-rwlock-check', 'tdb1-run-summary', 'tdb1-run-transaction-expand', 'tdb1-run-traverse-in-transaction', 'tdb1-run-wronghash-fail', 'tdb1-run-zero-append': + cmd = "cd " + testdir + " && " + os.path.abspath(os.path.join(Utils.g_module.blddir, f)) + " > test-output 2>&1" + print("..." + f) + ret = samba_utils.RUN_COMMAND(cmd) + if ret != 0: + print("%s failed:" % f) + samba_utils.RUN_COMMAND("cat " + os.path.join(testdir, 'test-output')) + ecode = ret + break + + if ecode == 0: + cmd = os.path.join(Utils.g_module.blddir, 'tdbtorture') + ret = samba_utils.RUN_COMMAND(cmd) + print("testsuite returned %d" % ret) + if ret != 0: + ecode = ret + sys.exit(ecode) + +# WAF doesn't build the unit tests for this, maybe because they don't link with tdb? +# This forces it +def test(ctx): + import Scripting + Scripting.commands.append('build') + Scripting.commands.append('testonly') + +def dist(): + '''makes a tarball for distribution''' + samba_dist.dist() + +def reconfigure(ctx): + '''reconfigure if config scripts have changed''' + import samba_utils + samba_utils.reconfigure(ctx) |