tdb: Make robust against shrinking tdbs
authorRusty Russell <rusty@rustcorp.com.au>
Sat, 6 Oct 2012 11:23:05 +0000 (13:23 +0200)
committerVolker Lendecke <vl@samba.org>
Sat, 6 Oct 2012 11:23:41 +0000 (13:23 +0200)
When probing for a size change (eg. just before tdb_expand, tdb_check,
tdb_rescue) we call tdb_oob(tdb, tdb->map_size, 1, 1).  Unfortunately
this does nothing if the tdb has actually shrunk, which as Volker
demonstrated, can actually happen if a "longlived" parent crashes.

So move the map/update size/remap before the limit check.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
lib/tdb/common/io.c

index 3131f4fa0af6fb1e331a918526f2d8986cd3980f..25968bfef2dbad1da3a1a2e0ec6bfbaf2071b7ea 100644 (file)
@@ -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 */