tdb: Make tdb robust against improper CLEAR_IF_FIRST restart
[metze/samba/wip.git] / lib / tdb / common / open.c
index ec45689ffccd26146538a244e2251ba74c8d1804..eeea8c7dd875bd240726d3301d75ecc72274331f 100644 (file)
@@ -313,12 +313,36 @@ _PUBLIC_ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int td
        if ((tdb_flags & TDB_CLEAR_IF_FIRST) &&
            (!tdb->read_only) &&
            (locked = (tdb_nest_lock(tdb, ACTIVE_LOCK, F_WRLCK, TDB_LOCK_NOWAIT|TDB_LOCK_PROBE) == 0))) {
-               open_flags |= O_CREAT;
-               if (ftruncate(tdb->fd, 0) == -1) {
+               int ret;
+               ret = tdb_brlock(tdb, F_WRLCK, FREELIST_TOP, 0,
+                               TDB_LOCK_WAIT);
+               if (ret == -1) {
                        TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_open_ex: "
-                                "failed to truncate %s: %s\n",
-                                name, strerror(errno)));
-                       goto fail; /* errno set by ftruncate */
+                               "tdb_brlock failed for %s: %s\n",
+                               name, strerror(errno)));
+                       goto fail;
+               }
+               ret = tdb_new_database(tdb, hash_size);
+               if (ret == -1) {
+                       TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_open_ex: "
+                               "tdb_new_database failed for %s: %s\n",
+                               name, strerror(errno)));
+                       tdb_unlockall(tdb);
+                       goto fail;
+               }
+               ret = tdb_brunlock(tdb, F_WRLCK, FREELIST_TOP, 0);
+               if (ret == -1) {
+                       TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_open_ex: "
+                               "tdb_unlockall failed for %s: %s\n",
+                               name, strerror(errno)));
+                       goto fail;
+               }
+               ret = lseek(tdb->fd, 0, SEEK_SET);
+               if (ret == -1) {
+                       TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_open_ex: "
+                               "lseek failed for %s: %s\n",
+                               name, strerror(errno)));
+                       goto fail;
                }
        }