static struct tdb_context *tdb_open_cloexec( const char *name, int hash_size, int tdb_flags, int open_flags, mode_t mode) { /* Mimics pa_open_cloexec() */ struct tdb_context *c; #ifdef O_NOCTTY open_flags |= O_NOCTTY; #endif #ifdef O_CLOEXEC errno = 0; if ((c = tdb_open(name, hash_size, tdb_flags, open_flags | O_CLOEXEC, mode))) goto finish; if (errno != EINVAL) return NULL; #endif errno = 0; if (!(c = tdb_open(name, hash_size, tdb_flags, open_flags, mode))) return NULL; finish: pa_make_fd_cloexec(tdb_fd(c)); return c; }
/* wrapped connection to a tdb database. The caller should _not_ free this as it is not a talloc structure (as tdb does not use talloc yet). It will auto-close when the caller frees the mem_ctx that is passed to this call */ struct tdb_context *ltdb_wrap_open(TALLOC_CTX *mem_ctx, const char *path, int hash_size, int tdb_flags, int open_flags, mode_t mode, struct ldb_context *ldb) { struct ltdb_wrap *w; struct stat st; struct tdb_logging_context log_ctx; log_ctx.log_fn = ltdb_log_fn; log_ctx.log_private = ldb; if (stat(path, &st) == 0) { for (w=tdb_list;w;w=w->next) { if (st.st_dev == w->device && st.st_ino == w->inode) { if (!talloc_reference(mem_ctx, w)) { return NULL; } return w->tdb; } } } w = talloc(mem_ctx, struct ltdb_wrap); if (w == NULL) { return NULL; } w->tdb = tdb_open_ex(path, hash_size, tdb_flags, open_flags, mode, &log_ctx, NULL); if (w->tdb == NULL) { talloc_free(w); return NULL; } if (fstat(tdb_fd(w->tdb), &st) != 0) { tdb_close(w->tdb); talloc_free(w); return NULL; } w->device = st.st_dev; w->inode = st.st_ino; talloc_set_destructor(w, ltdb_wrap_destructor); DLIST_ADD(tdb_list, w); return w->tdb; }
int main(int argc, char *argv[]) { unsigned int i; struct tdb_context *tdb; int flags[] = { TDB_INTERNAL, TDB_DEFAULT, TDB_NOMMAP, TDB_INTERNAL|TDB_CONVERT, TDB_CONVERT, TDB_NOMMAP|TDB_CONVERT }; plan_tests(sizeof(flags) / sizeof(flags[0]) * 3); for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) { tdb = tdb_open("run-new_database.tdb", flags[i], O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr); if (!ok1(tdb)) continue; if (flags[i] & TDB_INTERNAL) ok1(tdb_fd(tdb) == -1); else ok1(tdb_fd(tdb) > 2); tdb_close(tdb); ok1(tap_log_messages == 0); } return exit_status(); }
/* carefully backup a tdb, validating the contents and only doing the backup if its OK this function is also used for restore */ static int backup_tdb(const char *old_name, const char *new_name, int hash_size, int nolock) { TDB_CONTEXT *tdb; TDB_CONTEXT *tdb_new; char *tmp_name; struct stat st; int count1, count2; tmp_name = add_suffix(new_name, ".tmp"); /* stat the old tdb to find its permissions */ if (stat(old_name, &st) != 0) { perror(old_name); free(tmp_name); return 1; } /* open the old tdb */ tdb = tdb_open_ex(old_name, 0, TDB_DEFAULT | (nolock ? TDB_NOLOCK : 0), O_RDWR, 0, &log_ctx, NULL); if (!tdb) { printf("Failed to open %s\n", old_name); free(tmp_name); return 1; } /* create the new tdb */ unlink(tmp_name); tdb_new = tdb_open_ex(tmp_name, hash_size ? hash_size : tdb_hash_size(tdb), TDB_DEFAULT, O_RDWR|O_CREAT|O_EXCL, st.st_mode & 0777, &log_ctx, NULL); if (!tdb_new) { perror(tmp_name); free(tmp_name); return 1; } if (tdb_transaction_start(tdb) != 0) { printf("Failed to start transaction on old tdb\n"); tdb_close(tdb); tdb_close(tdb_new); unlink(tmp_name); free(tmp_name); return 1; } /* lock the backup tdb so that nobody else can change it */ if (tdb_lockall(tdb_new) != 0) { printf("Failed to lock backup tdb\n"); tdb_close(tdb); tdb_close(tdb_new); unlink(tmp_name); free(tmp_name); return 1; } failed = 0; /* traverse and copy */ count1 = tdb_traverse(tdb, copy_fn, (void *)tdb_new); if (count1 < 0 || failed) { fprintf(stderr,"failed to copy %s\n", old_name); tdb_close(tdb); tdb_close(tdb_new); unlink(tmp_name); free(tmp_name); return 1; } /* close the old tdb */ tdb_close(tdb); /* copy done, unlock the backup tdb */ tdb_unlockall(tdb_new); #ifdef HAVE_FDATASYNC if (fdatasync(tdb_fd(tdb_new)) != 0) { #else if (fsync(tdb_fd(tdb_new)) != 0) { #endif /* not fatal */ fprintf(stderr, "failed to fsync backup file\n"); } /* close the new tdb and re-open read-only */ tdb_close(tdb_new); tdb_new = tdb_open_ex(tmp_name, 0, TDB_DEFAULT, O_RDONLY, 0, &log_ctx, NULL); if (!tdb_new) { fprintf(stderr,"failed to reopen %s\n", tmp_name); unlink(tmp_name); perror(tmp_name); free(tmp_name); return 1; } /* traverse the new tdb to confirm */ count2 = tdb_traverse(tdb_new, test_fn, NULL); if (count2 != count1) { fprintf(stderr,"failed to copy %s\n", old_name); tdb_close(tdb_new); unlink(tmp_name); free(tmp_name); return 1; } /* close the new tdb and rename it to .bak */ tdb_close(tdb_new); if (rename(tmp_name, new_name) != 0) { perror(new_name); free(tmp_name); return 1; } free(tmp_name); return 0; } /* verify a tdb and if it is corrupt then restore from *.bak */ static int verify_tdb(const char *fname, const char *bak_name) { TDB_CONTEXT *tdb; int count = -1; /* open the tdb */ tdb = tdb_open_ex(fname, 0, 0, O_RDONLY, 0, &log_ctx, NULL); /* traverse the tdb, then close it */ if (tdb) { count = tdb_traverse(tdb, test_fn, NULL); tdb_close(tdb); } /* count is < 0 means an error */ if (count < 0) { printf("restoring %s\n", fname); return backup_tdb(bak_name, fname, 0, 0); } printf("%s : %d records\n", fname, count); return 0; }
struct db_context *db_open_tdb(TALLOC_CTX *mem_ctx, struct loadparm_context *lp_ctx, const char *name, int hash_size, int tdb_flags, int open_flags, mode_t mode, enum dbwrap_lock_order lock_order, uint64_t dbrwap_flags) { struct db_context *result = NULL; struct db_tdb_ctx *db_tdb; struct stat st; result = talloc_zero(mem_ctx, struct db_context); if (result == NULL) { DEBUG(0, ("talloc failed\n")); goto fail; } result->private_data = db_tdb = talloc(result, struct db_tdb_ctx); if (db_tdb == NULL) { DEBUG(0, ("talloc failed\n")); goto fail; } result->lock_order = lock_order; if (hash_size == 0) { hash_size = lpcfg_tdb_hash_size(lp_ctx, name); } db_tdb->wtdb = tdb_wrap_open(db_tdb, name, hash_size, lpcfg_tdb_flags(lp_ctx, tdb_flags), open_flags, mode); if (db_tdb->wtdb == NULL) { DEBUG(3, ("Could not open tdb: %s\n", strerror(errno))); goto fail; } ZERO_STRUCT(db_tdb->id); if (fstat(tdb_fd(db_tdb->wtdb->tdb), &st) == -1) { DEBUG(3, ("fstat failed: %s\n", strerror(errno))); goto fail; } db_tdb->id.dev = st.st_dev; db_tdb->id.ino = st.st_ino; result->fetch_locked = db_tdb_fetch_locked; result->try_fetch_locked = db_tdb_try_fetch_locked; result->traverse = db_tdb_traverse; result->traverse_read = db_tdb_traverse_read; result->parse_record = db_tdb_parse; result->get_seqnum = db_tdb_get_seqnum; result->persistent = ((tdb_flags & TDB_CLEAR_IF_FIRST) == 0); result->transaction_start = db_tdb_transaction_start; result->transaction_start_nonblock = db_tdb_transaction_start_nonblock; result->transaction_commit = db_tdb_transaction_commit; result->transaction_cancel = db_tdb_transaction_cancel; result->exists = db_tdb_exists; result->wipe = db_tdb_wipe; result->id = db_tdb_id; result->check = db_tdb_check; result->name = tdb_name(db_tdb->wtdb->tdb); result->hash_size = hash_size; return result; fail: TALLOC_FREE(result); return NULL; }
/* this backup function is essentially taken from lib/tdb/tools/tdbbackup.tdb */ static int tdb_backup(TALLOC_CTX *ctx, const char *src_path, const char *dst_path, int hash_size) { struct tdb_context *src_tdb = NULL; struct tdb_context *dst_tdb = NULL; char *tmp_path = NULL; struct stat st; int count1, count2; int saved_errno = 0; int ret = -1; if (stat(src_path, &st) != 0) { DEBUG(3, ("Could not stat '%s': %s\n", src_path, strerror(errno))); goto done; } /* open old tdb RDWR - so we can lock it */ src_tdb = tdb_open_log(src_path, 0, TDB_DEFAULT, O_RDWR, 0); if (src_tdb == NULL) { DEBUG(3, ("Failed to open tdb '%s'\n", src_path)); goto done; } if (tdb_lockall(src_tdb) != 0) { DEBUG(3, ("Failed to lock tdb '%s'\n", src_path)); goto done; } tmp_path = talloc_asprintf(ctx, "%s%s", dst_path, ".tmp"); unlink(tmp_path); dst_tdb = tdb_open_log(tmp_path, hash_size ? hash_size : tdb_hash_size(src_tdb), TDB_DEFAULT, O_RDWR | O_CREAT | O_EXCL, st.st_mode & 0777); if (dst_tdb == NULL) { DEBUG(3, ("Error creating tdb '%s': %s\n", tmp_path, strerror(errno))); saved_errno = errno; unlink(tmp_path); goto done; } count1 = tdb_copy(src_tdb, dst_tdb); if (count1 < 0) { DEBUG(3, ("Failed to copy tdb '%s': %s\n", src_path, strerror(errno))); tdb_close(dst_tdb); goto done; } /* reopen ro and do basic verification */ tdb_close(dst_tdb); dst_tdb = tdb_open_log(tmp_path, 0, TDB_DEFAULT, O_RDONLY, 0); if (!dst_tdb) { DEBUG(3, ("Failed to reopen tdb '%s': %s\n", tmp_path, strerror(errno))); goto done; } count2 = tdb_verify_basic(dst_tdb); if (count2 != count1) { DEBUG(3, ("Failed to verify result of copying tdb '%s'.\n", src_path)); tdb_close(dst_tdb); goto done; } DEBUG(10, ("tdb_backup: successfully copied %d entries\n", count1)); /* make sure the new tdb has reached stable storage * then rename it to its destination */ fsync(tdb_fd(dst_tdb)); tdb_close(dst_tdb); unlink(dst_path); if (rename(tmp_path, dst_path) != 0) { DEBUG(3, ("Failed to rename '%s' to '%s': %s\n", tmp_path, dst_path, strerror(errno))); goto done; } /* success */ ret = 0; done: if (src_tdb != NULL) { tdb_close(src_tdb); } if (tmp_path != NULL) { unlink(tmp_path); TALLOC_FREE(tmp_path); } if (saved_errno != 0) { errno = saved_errno; } return ret; }
/* carefully backup a tdb, validating the contents and only doing the backup if its OK this function is also used for restore */ int backup_tdb(const char *old_name, const char *new_name, int hash_size) { TDB_CONTEXT *tdb; TDB_CONTEXT *tdb_new; char *tmp_name; struct stat st; int count1, count2; tmp_name = add_suffix(new_name, ".tmp"); /* stat the old tdb to find its permissions */ if (stat(old_name, &st) != 0) { perror(old_name); free(tmp_name); return 1; } /* open the old tdb */ tdb = tdb_open(old_name, 0, 0, O_RDWR, 0); if (!tdb) { printf("Failed to open %s\n", old_name); free(tmp_name); return 1; } /* create the new tdb */ unlink(tmp_name); tdb_new = tdb_open(tmp_name, hash_size ? hash_size : tdb_hash_size(tdb), TDB_DEFAULT, O_RDWR|O_CREAT|O_EXCL, st.st_mode & 0777); if (!tdb_new) { perror(tmp_name); free(tmp_name); return 1; } /* lock the old tdb */ if (tdb_lockall(tdb) != 0) { fprintf(stderr,"Failed to lock %s\n", old_name); tdb_close(tdb); tdb_close(tdb_new); unlink(tmp_name); free(tmp_name); return 1; } failed = 0; /* traverse and copy */ count1 = tdb_traverse(tdb, copy_fn, (void *)tdb_new); if (count1 < 0 || failed) { fprintf(stderr,"failed to copy %s\n", old_name); tdb_close(tdb); tdb_close(tdb_new); unlink(tmp_name); free(tmp_name); return 1; } /* close the old tdb */ tdb_close(tdb); /* close the new tdb and re-open read-only */ tdb_close(tdb_new); tdb_new = tdb_open(tmp_name, 0, TDB_DEFAULT, O_RDONLY, 0); if (!tdb_new) { fprintf(stderr,"failed to reopen %s\n", tmp_name); unlink(tmp_name); perror(tmp_name); free(tmp_name); return 1; } /* traverse the new tdb to confirm */ count2 = tdb_traverse(tdb_new, test_fn, 0); if (count2 != count1) { fprintf(stderr,"failed to copy %s\n", old_name); tdb_close(tdb_new); unlink(tmp_name); free(tmp_name); return 1; } /* make sure the new tdb has reached stable storage */ fsync(tdb_fd(tdb_new)); /* close the new tdb and rename it to .bak */ tdb_close(tdb_new); unlink(new_name); if (rename(tmp_name, new_name) != 0) { perror(new_name); free(tmp_name); return 1; } free(tmp_name); return 0; }