tdb: Allow !CLEAR_IF_FIRST & MUTEX_LOCKING
authorVolker Lendecke <vl@samba.org>
Mon, 22 Oct 2018 06:57:00 +0000 (08:57 +0200)
committerJeremy Allison <jra@samba.org>
Tue, 6 Nov 2018 17:57:26 +0000 (18:57 +0100)
This is a prerequisite to allow gencache to run on a non-transactioned
database with mutexes.

Signed-off-by: Volker Lendecke <vl@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
lib/tdb/common/open.c
lib/tdb/test/run-mutex-openflags2.c

index 899a2fcaffbd9d073efe69fb55913b43c75d719e..be5f8075557b317acdabba5329e473be31f5ee4c 100644 (file)
@@ -230,8 +230,6 @@ static bool check_header_hash(struct tdb_context *tdb,
 static bool tdb_mutex_open_ok(struct tdb_context *tdb,
                              const struct tdb_header *header)
 {
-       int locked;
-
        if (tdb->flags & TDB_NOLOCK) {
                /*
                 * We don't look at locks, so it does not matter to have a
@@ -240,37 +238,6 @@ static bool tdb_mutex_open_ok(struct tdb_context *tdb,
                return true;
        }
 
-       locked = tdb_nest_lock(tdb, ACTIVE_LOCK, F_WRLCK,
-                              TDB_LOCK_NOWAIT|TDB_LOCK_PROBE);
-
-       if ((locked == -1) && (tdb->ecode == TDB_ERR_LOCK)) {
-               /*
-                * CLEAR_IF_FIRST still active. The tdb was created on this
-                * host, so we can assume the mutex implementation is
-                * compatible. Important for tools like tdbdump on a still
-                * open locking.tdb.
-                */
-               goto check_local_settings;
-       }
-
-       /*
-        * We got the CLEAR_IF_FIRST lock. That means the database was
-        * potentially copied from somewhere else. The mutex implementation
-        * might be incompatible.
-        */
-
-       if (tdb_nest_unlock(tdb, ACTIVE_LOCK, F_WRLCK, false) == -1) {
-               /*
-                * Should not happen
-                */
-               TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_mutex_open_ok: "
-                        "failed to release ACTIVE_LOCK on %s: %s\n",
-                        tdb->name, strerror(errno)));
-               return false;
-       }
-
-check_local_settings:
-
        if (!(tdb->flags & TDB_MUTEX_LOCKING)) {
                TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_mutex_open_ok[%s]: "
                         "Can use mutexes only with "
@@ -416,14 +383,6 @@ _PUBLIC_ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int td
                 * the runtime check for existing tdb's comes later.
                 */
 
-               if (!(tdb->flags & TDB_CLEAR_IF_FIRST)) {
-                       TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: "
-                               "invalid flags for %s - TDB_MUTEX_LOCKING "
-                               "requires TDB_CLEAR_IF_FIRST\n", name));
-                       errno = EINVAL;
-                       goto fail;
-               }
-
                if (tdb->flags & TDB_INTERNAL) {
                        TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: "
                                "invalid flags for %s - TDB_MUTEX_LOCKING and "
@@ -632,6 +591,30 @@ _PUBLIC_ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int td
                 * mutex locking.
                 */
                tdb->hdr_ofs = header.mutex_size;
+
+               if ((!(tdb_flags & TDB_CLEAR_IF_FIRST)) && (!tdb->read_only)) {
+                       /*
+                        * Open an existing mutexed tdb, but without
+                        * CLEAR_IF_FIRST. We need to initialize the
+                        * mutex array and keep the CLEAR_IF_FIRST
+                        * lock locked.
+                        */
+                       ret = tdb_nest_lock(tdb, ACTIVE_LOCK, F_WRLCK,
+                                           TDB_LOCK_NOWAIT|TDB_LOCK_PROBE);
+                       locked = (ret == 0);
+
+                       if (locked) {
+                               ret = tdb_mutex_init(tdb);
+                               if (ret == -1) {
+                                       TDB_LOG((tdb,
+                                                TDB_DEBUG_FATAL,
+                                                "tdb_open_ex: tdb_mutex_init "
+                                                "failed for ""%s: %s\n",
+                                                name, strerror(errno)));
+                                       goto fail;
+                               }
+                       }
+               }
        }
 
        if ((header.magic1_hash == 0) && (header.magic2_hash == 0)) {
@@ -708,15 +691,19 @@ _PUBLIC_ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int td
                        goto fail;
                }
 
+
        }
 
-       /* We always need to do this if the CLEAR_IF_FIRST flag is set, even if
-          we didn't get the initial exclusive lock as we need to let all other
-          users know we're using it. */
+       if (locked || (tdb_flags & TDB_CLEAR_IF_FIRST)) {
+               /*
+                * We always need to do this if the CLEAR_IF_FIRST
+                * flag is set, even if we didn't get the initial
+                * exclusive lock as we need to let all other users
+                * know we're using it.
+                */
 
-       if (tdb_flags & TDB_CLEAR_IF_FIRST) {
-               /* leave this lock in place to indicate it's in use */
-               if (tdb_nest_lock(tdb, ACTIVE_LOCK, F_RDLCK, TDB_LOCK_WAIT) == -1) {
+               ret = tdb_nest_lock(tdb, ACTIVE_LOCK, F_RDLCK, TDB_LOCK_WAIT);
+               if (ret == -1) {
                        goto fail;
                }
        }
@@ -932,7 +919,10 @@ fail:
    seek pointer from our parent and to re-establish locks */
 _PUBLIC_ int tdb_reopen(struct tdb_context *tdb)
 {
-       return tdb_reopen_internal(tdb, tdb->flags & TDB_CLEAR_IF_FIRST);
+       bool active_lock;
+       active_lock = (tdb->flags & (TDB_CLEAR_IF_FIRST|TDB_MUTEX_LOCKING));
+
+       return tdb_reopen_internal(tdb, active_lock);
 }
 
 /* reopen all tdb's */
@@ -941,7 +931,10 @@ _PUBLIC_ int tdb_reopen_all(int parent_longlived)
        struct tdb_context *tdb;
 
        for (tdb=tdbs; tdb; tdb = tdb->next) {
-               bool active_lock = (tdb->flags & TDB_CLEAR_IF_FIRST);
+               bool active_lock;
+
+               active_lock =
+                       (tdb->flags & (TDB_CLEAR_IF_FIRST|TDB_MUTEX_LOCKING));
 
                /*
                 * If the parent is longlived (ie. a
index 6522ae42faa33c06af5a925590a3581d95f5b9ef..89603e638c2669361b0b5d9ecbbab4ea9030b5a9 100644 (file)
@@ -112,13 +112,6 @@ int main(int argc, char *argv[])
        data.dsize = strlen("world");
        data.dptr = discard_const_p(uint8_t, "world");
 
-       tdb = tdb_open_ex("mutex-openflags2.tdb", 0,
-                         TDB_INCOMPATIBLE_HASH|
-                         TDB_MUTEX_LOCKING,
-                         O_RDWR|O_CREAT, 0755, &nolog_ctx, NULL);
-       ok((tdb == NULL) && (errno == EINVAL), "TDB_MUTEX_LOCKING without "
-          "TDB_CLEAR_IF_FIRST should fail with EINVAL - %d", errno);
-
        if (!runtime_support) {
                tdb = tdb_open_ex("mutex-openflags2.tdb", 0,
                                  TDB_CLEAR_IF_FIRST|