lib/tdb: fix transaction issue for HAVE_INCOHERENT_MMAP.
authorRusty Russell <rusty@rustcorp.com.au>
Fri, 23 Mar 2012 00:15:18 +0000 (10:45 +1030)
committerRusty Russell <rusty@rustcorp.com.au>
Fri, 23 Mar 2012 01:53:15 +0000 (02:53 +0100)
We unmap the tdb on expand, the remap.  But when we have INCOHERENT_MMAP
(ie. OpenBSD) and we're inside a transaction, doing the expand can mean
we need to read from the database to partially fill a transaction block.
This fails, because if mmap is incoherent we never allow accessing the
database via read/write.

The solution is not to unmap and remap until we've actually written the
padding at the end of the file.

Reported-by: Amitay Isaacs <amitay@gmail.com>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Autobuild-User: Rusty Russell <rusty@rustcorp.com.au>
Autobuild-Date: Fri Mar 23 02:53:15 CET 2012 on sn-devel-104

lib/tdb/common/io.c

index be5a9ca5b2d235e0ce5c748d193c188f8ea954f6..3131f4fa0af6fb1e331a918526f2d8986cd3980f 100644 (file)
@@ -381,37 +381,36 @@ int tdb_expand(struct tdb_context *tdb, tdb_off_t size)
 
        size = tdb_expand_adjust(tdb->map_size, size, tdb->page_size);
 
-       if (!(tdb->flags & TDB_INTERNAL))
-               tdb_munmap(tdb);
-
        /* expand the file itself */
        if (!(tdb->flags & TDB_INTERNAL)) {
                if (tdb->methods->tdb_expand_file(tdb, tdb->map_size, size) != 0)
                        goto fail;
        }
 
-       tdb->map_size += size;
+       /* form a new freelist record */
+       offset = tdb->map_size;
+       memset(&rec,'\0',sizeof(rec));
+       rec.rec_len = size - sizeof(rec);
 
        if (tdb->flags & TDB_INTERNAL) {
                char *new_map_ptr = (char *)realloc(tdb->map_ptr,
-                                                   tdb->map_size);
+                                                   tdb->map_size + size);
                if (!new_map_ptr) {
-                       tdb->map_size -= size;
                        goto fail;
                }
                tdb->map_ptr = new_map_ptr;
+               tdb->map_size += size;
        } else {
+               /* Explicitly remap: if we're in a transaction, this won't
+                * happen automatically! */
+               tdb_munmap(tdb);
+               tdb->map_size += size;
                if (tdb_mmap(tdb) != 0) {
                        goto fail;
                }
        }
 
-       /* form a new freelist record */
-       memset(&rec,'\0',sizeof(rec));
-       rec.rec_len = size - sizeof(rec);
-
        /* link it into the free list */
-       offset = tdb->map_size - size;
        if (tdb_free(tdb, offset, &rec) == -1)
                goto fail;