[SCM] Samba Shared Repository - branch master updated

Rusty Russell rusty at samba.org
Wed Oct 3 19:17:02 MDT 2012


The branch, master has been updated
       via  100d38d tdb: add -e option to tdbdump (and docment it).
       via  ffde867 tdb: tdbdump should log errors, and fail in that case.
       via  90f463b tdb: add tdb_rescue()
      from  fe38a93 Correct fix for bug #9222 - smbd ignores the "server signing = no" setting for SMB2.

http://gitweb.samba.org/?p=samba.git;a=shortlog;h=master


- Log -----------------------------------------------------------------
commit 100d38d6e0fae1dc666ae43941570c9f106b73c2
Author: Rusty Russell <rusty at rustcorp.com.au>
Date:   Thu Oct 4 09:04:23 2012 +0930

    tdb: add -e option to tdbdump (and docment it).
    
    This allows for an emergency best-effort dump.  It's a little better than
    strings(1).
    
    Signed-off-by: Rusty Russell <rusty at rustcorp.com.au>
    
    Autobuild-User(master): Rusty Russell <rusty at rustcorp.com.au>
    Autobuild-Date(master): Thu Oct  4 03:16:06 CEST 2012 on sn-devel-104

commit ffde8678910ae838588ab380b48a544333f81241
Author: Rusty Russell <rusty at rustcorp.com.au>
Date:   Thu Oct 4 09:04:23 2012 +0930

    tdb: tdbdump should log errors, and fail in that case.
    
    Dumping a corrupt database should not exit silently with 0 status!
    
    Signed-off-by: Rusty Russell <rusty at rustcorp.com.au>

commit 90f463b25f7bb0bc944732773c56e356834ea203
Author: Rusty Russell <rusty at rustcorp.com.au>
Date:   Thu Oct 4 09:04:19 2012 +0930

    tdb: add tdb_rescue()
    
    This allows for an emergency best-effort dump.  It's a little better than
    strings(1).
    
    Signed-off-by: Rusty Russell <rusty at rustcorp.com.au>

-----------------------------------------------------------------------

Summary of changes:
 lib/tdb/ABI/{tdb-1.2.10.sigs => tdb-1.2.11.sigs} |    1 +
 lib/tdb/common/rescue.c                          |  349 ++++++++++++++++++++++
 lib/tdb/include/tdb.h                            |   22 ++
 lib/tdb/libtdb.m4                                |    2 +-
 lib/tdb/manpages/tdbdump.8.xml                   |   31 ++
 lib/tdb/test/run-rescue-find_entry.c             |   50 +++
 lib/tdb/test/run-rescue.c                        |  126 ++++++++
 lib/tdb/tools/tdbdump.c                          |   65 ++++-
 lib/tdb/wscript                                  |   10 +-
 9 files changed, 647 insertions(+), 9 deletions(-)
 copy lib/tdb/ABI/{tdb-1.2.10.sigs => tdb-1.2.11.sigs} (97%)
 create mode 100644 lib/tdb/common/rescue.c
 create mode 100644 lib/tdb/test/run-rescue-find_entry.c
 create mode 100644 lib/tdb/test/run-rescue.c


Changeset truncated at 500 lines:

diff --git a/lib/tdb/ABI/tdb-1.2.10.sigs b/lib/tdb/ABI/tdb-1.2.11.sigs
similarity index 97%
copy from lib/tdb/ABI/tdb-1.2.10.sigs
copy to lib/tdb/ABI/tdb-1.2.11.sigs
index 61f6c19..d727f21 100644
--- a/lib/tdb/ABI/tdb-1.2.10.sigs
+++ b/lib/tdb/ABI/tdb-1.2.11.sigs
@@ -45,6 +45,7 @@ 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 *)
diff --git a/lib/tdb/common/rescue.c b/lib/tdb/common/rescue.c
new file mode 100644
index 0000000..03ae8d6
--- /dev/null
+++ b/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/lib/tdb/include/tdb.h b/lib/tdb/include/tdb.h
index 5eb9562..d19439e 100644
--- a/lib/tdb/include/tdb.h
+++ b/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/lib/tdb/libtdb.m4 b/lib/tdb/libtdb.m4
index b5164fc..47f098e 100644
--- a/lib/tdb/libtdb.m4
+++ b/lib/tdb/libtdb.m4
@@ -13,7 +13,7 @@ if test x"$tdbdir" = "x"; 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 common/summary.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)
 AC_SUBST(LIBREPLACEOBJ)
 
diff --git a/lib/tdb/manpages/tdbdump.8.xml b/lib/tdb/manpages/tdbdump.8.xml
index 90465e5..3420193 100644
--- a/lib/tdb/manpages/tdbdump.8.xml
+++ b/lib/tdb/manpages/tdbdump.8.xml
@@ -19,6 +19,9 @@
 <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>
@@ -39,6 +42,34 @@
 	</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>
diff --git a/lib/tdb/test/run-rescue-find_entry.c b/lib/tdb/test/run-rescue-find_entry.c
new file mode 100644
index 0000000..25f4f1c
--- /dev/null
+++ b/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;
+	}


-- 
Samba Shared Repository


More information about the samba-cvs mailing list