summaryrefslogtreecommitdiff
path: root/source3/lib
diff options
context:
space:
mode:
Diffstat (limited to 'source3/lib')
-rw-r--r--source3/lib/util_tdb.c203
1 files changed, 203 insertions, 0 deletions
diff --git a/source3/lib/util_tdb.c b/source3/lib/util_tdb.c
index 6d783198d6..cb53b7f2e3 100644
--- a/source3/lib/util_tdb.c
+++ b/source3/lib/util_tdb.c
@@ -980,3 +980,206 @@ NTSTATUS map_nt_error_from_tdb(enum TDB_ERROR err)
return NT_STATUS_INTERNAL_ERROR;
}
+
+
+/*********************************************************************
+ * the following is a generic validation mechanism for tdbs.
+ *********************************************************************/
+
+/*
+ * internal validation function, executed by the child.
+ */
+static int tdb_validate_child(const char *tdb_path,
+ tdb_validate_data_func validate_fn,
+ int pfd)
+{
+ int ret = -1;
+ int tfd = -1;
+ int num_entries = 0;
+ TDB_CONTEXT *tdb = NULL;
+ struct tdb_validation_status v_status;
+
+ v_status.tdb_error = False;
+ v_status.bad_freelist = False;
+ v_status.bad_entry = False;
+ v_status.unknown_key = False;
+ v_status.success = True;
+
+ tdb = tdb_open_log(tdb_path,
+ WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
+ lp_winbind_offline_logon()
+ ? TDB_DEFAULT
+ : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
+ O_RDWR|O_CREAT, 0600);
+ if (!tdb) {
+ v_status.tdb_error = True;
+ v_status.success = False;
+ goto out;
+ }
+
+ tfd = tdb_fd(tdb);
+
+ /* Check the cache freelist is good. */
+ if (tdb_validate_freelist(tdb, &num_entries) == -1) {
+ DEBUG(0,("tdb_validate_child: bad freelist in cache %s\n",
+ tdb_path));
+ v_status.bad_freelist = True;
+ v_status.success = False;
+ goto out;
+ }
+
+ DEBUG(10,("tdb_validate_child: cache %s freelist has %d entries\n",
+ tdb_path, num_entries));
+
+ /* Now traverse the cache to validate it. */
+ num_entries = tdb_traverse(tdb, validate_fn, (void *)&v_status);
+ if (num_entries == -1 || !(v_status.success)) {
+ DEBUG(0,("tdb_validate_child: cache %s traverse failed\n",
+ tdb_path));
+ if (!(v_status.success)) {
+ if (v_status.bad_entry) {
+ DEBUGADD(0, (" -> bad entry found\n"));
+ }
+ if (v_status.unknown_key) {
+ DEBUGADD(0, (" -> unknown key encountered\n"));
+ }
+ }
+ goto out;
+ }
+
+ DEBUG(10,("tdb_validate_child: cache %s is good "
+ "with %d entries\n", tdb_path, num_entries));
+ ret = 0; /* Cache is good. */
+
+out:
+ if (tdb) {
+ if (ret == 0) {
+ tdb_close(tdb);
+ }
+ else if (tfd != -1) {
+ close(tfd);
+ }
+ }
+
+ DEBUG(10, ("tdb_validate_child: writing status to pipe\n"));
+ write (pfd, (const char *)&v_status, sizeof(v_status));
+ close(pfd);
+
+ return ret;
+}
+
+int tdb_validate(const char *tdb_path, tdb_validate_data_func validate_fn)
+{
+ pid_t child_pid = -1;
+ int child_status = 0;
+ int wait_pid = 0;
+ int ret = -1;
+ int pipe_fds[2];
+ struct tdb_validation_status v_status;
+ int bytes_read = 0;
+
+ /* fork and let the child do the validation.
+ * benefit: no need to twist signal handlers and panic functions.
+ * just let the child panic. we catch the signal.
+ * communicate the extended status struct over a pipe. */
+
+ if (pipe(pipe_fds) != 0) {
+ DEBUG(0, ("tdb_validate: unable to create pipe, "
+ "error %s", strerror(errno)));
+ smb_panic("winbind_validate_cache: unable to create pipe.");
+ }
+
+ DEBUG(10, ("tdb_validate: forking to let child do validation.\n"));
+ child_pid = sys_fork();
+ if (child_pid == 0) {
+ DEBUG(10, ("tdb_validate (validation child): created\n"));
+ close(pipe_fds[0]); /* close reading fd */
+ DEBUG(10, ("tdb_validate (validation child): "
+ "calling tdb_validate_child\n"));
+ exit(tdb_validate_child(tdb_path, validate_fn, pipe_fds[1]));
+ }
+ else if (child_pid < 0) {
+ smb_panic("tdb_validate: fork for validation failed.");
+ }
+
+ /* parent */
+
+ DEBUG(10, ("tdb_validate: fork succeeded, child PID = %d\n",
+ child_pid));
+ close(pipe_fds[1]); /* close writing fd */
+
+ v_status.success = True;
+ v_status.bad_entry = False;
+ v_status.unknown_key = False;
+
+ DEBUG(10, ("tdb_validate: reading from pipe.\n"));
+ bytes_read = read(pipe_fds[0], (void *)&v_status, sizeof(v_status));
+ close(pipe_fds[0]);
+
+ if (bytes_read != sizeof(v_status)) {
+ DEBUG(10, ("tdb_validate: read %d bytes from pipe "
+ "but expected %d", bytes_read, (int)sizeof(v_status)));
+ DEBUGADD(10, (" -> assuming child crashed\n"));
+ v_status.success = False;
+ }
+ else {
+ DEBUG(10, ("tdb_validate: read status from child\n"));
+ DEBUGADD(10, (" * tdb error: %s\n", v_status.tdb_error ? "yes" : "no"));
+ DEBUGADD(10, (" * bad freelist: %s\n", v_status.bad_freelist ? "yes" : "no"));
+ DEBUGADD(10, (" * bad entry: %s\n", v_status.bad_entry ? "yes" : "no"));
+ DEBUGADD(10, (" * unknown key: %s\n", v_status.unknown_key ? "yes" : "no"));
+ DEBUGADD(10, (" => overall success: %s\n", v_status.success ? "yes" : "no"));
+ }
+
+ if (!v_status.success) {
+ DEBUG(10, ("tdb_validate: validation not successful.\n"));
+ DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
+ unlink(tdb_path);
+ }
+
+ DEBUG(10, ("tdb_validate: waiting for child to finish...\n"));
+ while ((wait_pid = sys_waitpid(child_pid, &child_status, 0)) < 0) {
+ if (errno == EINTR) {
+ DEBUG(10, ("tdb_validate: got signal during "
+ "waitpid, retrying\n"));
+ errno = 0;
+ continue;
+ }
+ DEBUG(0, ("tdb_validate: waitpid failed with "
+ "errno %s\n", strerror(errno)));
+ smb_panic("tdb_validate: waitpid failed.");
+ }
+ if (wait_pid != child_pid) {
+ DEBUG(0, ("tdb_validate: waitpid returned pid %d, "
+ "but %d was expexted\n", wait_pid, child_pid));
+ smb_panic("tdb_validate: waitpid returned "
+ "unexpected PID.");
+ }
+
+
+ DEBUG(10, ("tdb_validate: validating child returned.\n"));
+ if (WIFEXITED(child_status)) {
+ DEBUG(10, ("tdb_validate: child exited, code %d.\n",
+ WEXITSTATUS(child_status)));
+ ret = WEXITSTATUS(child_status);
+ }
+ if (WIFSIGNALED(child_status)) {
+ DEBUG(10, ("tdb_validate: child terminated "
+ "by signal %d\n", WTERMSIG(child_status)));
+#ifdef WCOREDUMP
+ if (WCOREDUMP(child_status)) {
+ DEBUGADD(10, ("core dumped\n"));
+ }
+#endif
+ ret = WTERMSIG(child_status);
+ }
+ if (WIFSTOPPED(child_status)) {
+ DEBUG(10, ("tdb_validate: child was stopped "
+ "by signal %d\n",
+ WSTOPSIG(child_status)));
+ ret = WSTOPSIG(child_status);
+ }
+
+ return ret;
+}
+