Panu Matilainen e033e9
commit e7d5980e2a7b091d973171144de04397204ebcd7
Panu Matilainen e033e9
Author: Panu Matilainen <pmatilai@redhat.com>
Panu Matilainen e033e9
Date:   Tue May 28 08:56:22 2013 +0300
Panu Matilainen e033e9
Panu Matilainen e033e9
    Serialize BDB environment open/close (RhBug:924417 etc)
Panu Matilainen e033e9
    
Panu Matilainen e033e9
    - Introduce Yet Another Broken Lock[*] to serialize BDB environment open:
Panu Matilainen e033e9
      otherwise we can end up calling dbenv->failchk() while another process
Panu Matilainen e033e9
      is just joining the environment, leading to transient "Thread died in..."
Panu Matilainen e033e9
      DB_RUNRECOVER errors. Also prevents races on chrooted operations where
Panu Matilainen e033e9
      we remove the entire environment on close.
Panu Matilainen e033e9
    - This should also make it possible to handle at least some cases of
Panu Matilainen e033e9
      real DB_RUNRECOVER errors by just nuking the environment but that's
Panu Matilainen e033e9
      another topic...
Panu Matilainen e033e9
    
Panu Matilainen e033e9
      [*] YABL as this is nowhere near foolproof or sufficient for all
Panu Matilainen e033e9
          the possible variants, but better than not having it...
Panu Matilainen e033e9
    (cherry picked from commit ad874d60e3804f1bcd64f3510e1e2dfbf81456cd)
Panu Matilainen e033e9
Panu Matilainen e033e9
diff --git a/lib/backend/db3.c b/lib/backend/db3.c
Panu Matilainen e033e9
index de8071b..9d385c6 100644
Panu Matilainen e033e9
--- a/lib/backend/db3.c
Panu Matilainen e033e9
+++ b/lib/backend/db3.c
Panu Matilainen e033e9
@@ -57,10 +57,42 @@ static uint32_t db_envflags(DB * db)
Panu Matilainen e033e9
     return eflags;
Panu Matilainen e033e9
 }
Panu Matilainen e033e9
 
Panu Matilainen e033e9
+/*
Panu Matilainen e033e9
+ * Try to acquire db environment open/close serialization lock.
Panu Matilainen e033e9
+ * Return the open, locked fd on success, -1 on failure.
Panu Matilainen e033e9
+ */
Panu Matilainen e033e9
+static int serialize_env(const char *dbhome)
Panu Matilainen e033e9
+{
Panu Matilainen e033e9
+    char *lock_path = rstrscat(NULL, dbhome, "/.dbenv.lock", NULL);
Panu Matilainen e033e9
+    mode_t oldmask = umask(022);
Panu Matilainen e033e9
+    int fd = open(lock_path, (O_RDWR|O_CREAT), 0644);
Panu Matilainen e033e9
+    umask(oldmask);
Panu Matilainen e033e9
+
Panu Matilainen e033e9
+    if (fd >= 0) {
Panu Matilainen e033e9
+	int rc;
Panu Matilainen e033e9
+	struct flock info;
Panu Matilainen e033e9
+	memset(&info, 0, sizeof(info));
Panu Matilainen e033e9
+	info.l_type = F_WRLCK;
Panu Matilainen e033e9
+	info.l_whence = SEEK_SET;
Panu Matilainen e033e9
+	do {
Panu Matilainen e033e9
+	    rc = fcntl(fd, F_SETLKW, &info;;
Panu Matilainen e033e9
+	} while (rc == -1 && errno == EINTR);
Panu Matilainen e033e9
+	    
Panu Matilainen e033e9
+	if (rc == -1) {
Panu Matilainen e033e9
+	    close(fd);
Panu Matilainen e033e9
+	    fd = -1;
Panu Matilainen e033e9
+	}
Panu Matilainen e033e9
+    }
Panu Matilainen e033e9
+
Panu Matilainen e033e9
+    free(lock_path);
Panu Matilainen e033e9
+    return fd;
Panu Matilainen e033e9
+}
Panu Matilainen e033e9
+
Panu Matilainen e033e9
 static int db_fini(rpmdb rdb, const char * dbhome)
Panu Matilainen e033e9
 {
Panu Matilainen e033e9
     DB_ENV * dbenv = rdb->db_dbenv;
Panu Matilainen e033e9
     int rc;
Panu Matilainen e033e9
+    int lockfd = -1;
Panu Matilainen e033e9
     uint32_t eflags = 0;
Panu Matilainen e033e9
 
Panu Matilainen e033e9
     if (dbenv == NULL)
Panu Matilainen e033e9
@@ -72,6 +104,9 @@ static int db_fini(rpmdb rdb, const char * dbhome)
Panu Matilainen e033e9
     }
Panu Matilainen e033e9
 
Panu Matilainen e033e9
     (void) dbenv->get_open_flags(dbenv, &eflags);
Panu Matilainen e033e9
+    if (!(eflags & DB_PRIVATE))
Panu Matilainen e033e9
+	lockfd = serialize_env(dbhome);
Panu Matilainen e033e9
+
Panu Matilainen e033e9
     rc = dbenv->close(dbenv, 0);
Panu Matilainen e033e9
     rc = dbapi_err(rdb, "dbenv->close", rc, _debug);
Panu Matilainen e033e9
 
Panu Matilainen e033e9
@@ -89,6 +124,10 @@ static int db_fini(rpmdb rdb, const char * dbhome)
Panu Matilainen e033e9
 	rpmlog(RPMLOG_DEBUG, "removed  db environment %s\n", dbhome);
Panu Matilainen e033e9
 
Panu Matilainen e033e9
     }
Panu Matilainen e033e9
+
Panu Matilainen e033e9
+    if (lockfd >= 0)
Panu Matilainen e033e9
+	close(lockfd);
Panu Matilainen e033e9
+
Panu Matilainen e033e9
     return rc;
Panu Matilainen e033e9
 }
Panu Matilainen e033e9
 
Panu Matilainen e033e9
@@ -122,6 +161,7 @@ static int db_init(rpmdb rdb, const char * dbhome)
Panu Matilainen e033e9
     DB_ENV *dbenv = NULL;
Panu Matilainen e033e9
     int rc, xx;
Panu Matilainen e033e9
     int retry_open = 2;
Panu Matilainen e033e9
+    int lockfd = -1;
Panu Matilainen e033e9
     struct dbConfig_s * cfg = &rdb->cfg;
Panu Matilainen e033e9
     /* This is our setup, thou shall not have other setups before us */
Panu Matilainen e033e9
     uint32_t eflags = (DB_CREATE|DB_INIT_MPOOL|DB_INIT_CDB);
Panu Matilainen e033e9
@@ -176,6 +216,24 @@ static int db_init(rpmdb rdb, const char * dbhome)
Panu Matilainen e033e9
     }
Panu Matilainen e033e9
 
Panu Matilainen e033e9
     /*
Panu Matilainen e033e9
+     * Serialize shared environment open (and clock) via fcntl() lock.
Panu Matilainen e033e9
+     * Otherwise we can end up calling dbenv->failchk() while another
Panu Matilainen e033e9
+     * process is joining the environment, leading to transient
Panu Matilainen e033e9
+     * DB_RUNRECOVER errors. Also prevents races wrt removing the
Panu Matilainen e033e9
+     * environment (eg chrooted operation). Silently fall back to
Panu Matilainen e033e9
+     * private environment on failure to allow non-privileged queries
Panu Matilainen e033e9
+     * to "work", broken as it might be.
Panu Matilainen e033e9
+     */
Panu Matilainen e033e9
+    if (!(eflags & DB_PRIVATE)) {
Panu Matilainen e033e9
+	lockfd = serialize_env(dbhome);
Panu Matilainen e033e9
+	if (lockfd < 0) {
Panu Matilainen e033e9
+	    eflags |= DB_PRIVATE;
Panu Matilainen e033e9
+	    retry_open--;
Panu Matilainen e033e9
+	    rpmlog(RPMLOG_DEBUG, "serialize failed, using private dbenv\n");
Panu Matilainen e033e9
+	}
Panu Matilainen e033e9
+    }
Panu Matilainen e033e9
+
Panu Matilainen e033e9
+    /*
Panu Matilainen e033e9
      * Actually open the environment. Fall back to private environment
Panu Matilainen e033e9
      * if we dont have permission to join/create shared environment or
Panu Matilainen e033e9
      * system doesn't support it..
Panu Matilainen e033e9
@@ -208,6 +266,8 @@ static int db_init(rpmdb rdb, const char * dbhome)
Panu Matilainen e033e9
     rdb->db_dbenv = dbenv;
Panu Matilainen e033e9
     rdb->db_opens = 1;
Panu Matilainen e033e9
 
Panu Matilainen e033e9
+    if (lockfd >= 0)
Panu Matilainen e033e9
+	close(lockfd);
Panu Matilainen e033e9
     return 0;
Panu Matilainen e033e9
 
Panu Matilainen e033e9
 errxit:
Panu Matilainen e033e9
@@ -216,6 +276,8 @@ errxit:
Panu Matilainen e033e9
 	xx = dbenv->close(dbenv, 0);
Panu Matilainen e033e9
 	xx = dbapi_err(rdb, "dbenv->close", xx, _debug);
Panu Matilainen e033e9
     }
Panu Matilainen e033e9
+    if (lockfd >= 0)
Panu Matilainen e033e9
+	close(lockfd);
Panu Matilainen e033e9
     return rc;
Panu Matilainen e033e9
 }
Panu Matilainen e033e9