Sophie

Sophie

distrib > Mageia > 1 > i586 > media > core-updates-src > by-pkgid > 2d27fe3dff73e21c1aac97aebe0dff40 > files > 18

rpm-4.8.1-10.5.mga1.src.rpm

commit 115e413d9708799a8ada6643b35022f9a99cad0c
Author: unknown author <cooker@mandrivalinux.org>
Date:   Mon Jan 5 13:29:57 2009 +0000

    protect against non robust futex
    
    The basic idea is to workaround libdb using futex (through pthread_mutex)
    which are not "robust" (ie when a process dies, a futex may still be lying
    around, uselessly blocking, until a reboot is done).
    
    the workaround is to ensure __db* are removed when unused:
    - when we open the db, we share lock (through flock) __db.001
    - when the db is closed, __db* are removed if nobody locks __db.001 anymore
    - when the db is opened, __db* are removed if nobody locks __db.001:
      this is useful to rip stale locks
    
    this fixes #41868, but it doesn't explain why stale locks are lying around :-/
    
    PS: another solution would be to patch libdb to use "robust futex", but the
    solution above is considered safer (since it won't impact the other programs
    of libdb)
    
    Signed-off-by: Pascal "Pixel" Rigaux <pixel@mandriva.com>

diff --git a/lib/backend/db3.c b/lib/backend/db3.c
index bc3b8a4..73216e4 100644
--- a/lib/backend/db3.c
+++ b/lib/backend/db3.c
@@ -10,6 +10,8 @@ static int _debug = 1;	/* XXX if < 0 debugging, > 0 unusual error returns */
 #include <sys/ipc.h>
 #endif
 
+#include <sys/file.h>
+
 #include <rpm/rpmtypes.h>
 #include <rpm/rpmmacro.h>
 #include <rpm/rpmfileutil.h>
@@ -384,6 +386,100 @@ static int db3stat(dbiIndex dbi, unsigned int flags)
     return rc;
 }
 
+static int fcntl_SETLK(int fd, int operation)
+{
+    struct flock l;
+    memset(&l, 0, sizeof(l));
+    l.l_type = operation;
+    return flock(fd, operation | LOCK_NB);
+}
+static void clean_db_regions(const char *dbhome)
+{
+    rpmlog(RPMLOG_DEBUG, "cleaning db regions (ie db__* files) in %s\n", dbhome);
+  
+    char *filename = alloca(strlen(dbhome) + 40);
+    struct stat st;
+
+    int i;
+    for (i = 0; i < 16; i++) {
+        sprintf(filename, "%s/__db.%03d", dbhome, i);
+	(void)rpmCleanPath(filename);
+	if (stat(filename, &st) != 0) continue;
+	unlink(filename);
+    }
+}
+static int open_extra_lock(const char *dbhome)
+{
+    int fd = -1;
+    char *f = NULL;
+    if (asprintf(&f, "%s/__db.001", dbhome) != -1) {
+        fd = open(f, O_RDWR);
+	free(f);
+    }
+    return fd;
+}
+static void _acquire_extra_lock(int lock_fd)
+{
+    if (fcntl_SETLK(lock_fd, LOCK_SH) == 0)
+	rpmlog(RPMLOG_DEBUG, "acquire_extra_lock: locked %d\n", lock_fd);
+    else
+	rpmlog(RPMLOG_ERR, "acquire_extra: failed to lock extra lock: %s\n", strerror(errno));
+}
+static void release_and_close_extra_lock(int lock_fd)
+{
+    if (fcntl_SETLK(lock_fd, LOCK_UN) != 0) {
+	rpmlog(RPMLOG_ERR, "release_and_close_extra_lock: failed to unlock extra lock: %s\n", strerror(errno));
+    }
+    if (close(lock_fd) != 0) {
+	rpmlog(RPMLOG_ERR, "release_and_close_extra_lock: failed to close extra lock fd: %s\n", strerror(errno));
+    }
+}
+static int may_clean_db_regions(const char * dbhome, int lock_fd)
+{
+    if (fcntl_SETLK(lock_fd, LOCK_EX) == 0) {
+	/* cool, we are the only one, we can safely clean berkeley db regions */
+	/* this is useful in case the previous db access crashed and did not close the db correctly */
+	clean_db_regions(dbhome);
+	return 1;
+    } else
+	return 0;
+}
+static int acquire_extra_lock_or_clean_db_regions(const char *dbhome)
+{
+    int fd = open_extra_lock(dbhome);
+
+    if (fd == -1) {
+	/* that's ok */
+    } else if (may_clean_db_regions(dbhome, fd)) {
+	rpmlog(RPMLOG_WARNING, "cleaning stale lock\n");
+	release_and_close_extra_lock(fd);
+	fd = -1;
+    } else {
+	/* we are not the only user of rpmdb */
+	_acquire_extra_lock(fd);
+    }
+    return fd;
+}
+static int acquire_extra_lock(const char *dbhome)
+{
+    int fd = open_extra_lock(dbhome);
+
+    if (fd == -1) {
+	rpmlog(RPMLOG_ERR, "acquire_extra_lock: failed to open extra lock: %s\n", strerror(errno));
+    } else {
+	_acquire_extra_lock(fd);
+    }
+    return fd;
+}
+static void release_extra_lock_may_clean(const char *dbhome, int lock_fd)
+{
+    if (lock_fd != -1) {
+	rpmlog(RPMLOG_DEBUG, "release_extra_lock_may_clean(%s, %d)\n", dbhome, lock_fd);
+	may_clean_db_regions(dbhome, lock_fd);
+	release_and_close_extra_lock(lock_fd);
+    }
+}
+
 static int db3close(dbiIndex dbi, unsigned int flags)
 {
     rpmdb rpmdb = dbi->dbi_rpmdb;
@@ -408,6 +504,7 @@ static int db3close(dbiIndex dbi, unsigned int flags)
 
     if (rpmdb->db_dbenv != NULL && dbi->dbi_use_dbenv) {
 	if (rpmdb->db_opens == 1) {
+	    release_extra_lock_may_clean(dbhome, (int) ((DB_ENV *) rpmdb->db_dbenv)->app_private);
 	    xx = db_fini(dbi, (dbhome ? dbhome : ""));
 	    rpmdb->db_dbenv = NULL;
 	}
@@ -534,6 +631,7 @@ static int db3open(rpmdb rpmdb, rpmTag rpmtag, dbiIndex * dbip)
     /*
      * Avoid incompatible DB_CREATE/DB_RDONLY flags on DBENV->open.
      */
+    int extra_lock = -1;
     if (dbi->dbi_use_dbenv) {
 
 	if (access(dbhome, W_OK) == -1) {
@@ -565,6 +663,9 @@ static int db3open(rpmdb rpmdb, rpmTag rpmtag, dbiIndex * dbip)
 	    }
 
 	} else {	/* dbhome is writable, check for persistent dbenv. */
+	    if (rpmdb->db_dbenv == NULL)
+	        extra_lock = acquire_extra_lock_or_clean_db_regions(dbhome);
+
 	    char * dbf = rpmGetPath(dbhome, "/__db.001", NULL);
 
 	    if (access(dbf, F_OK) == -1) {
@@ -621,6 +722,10 @@ static int db3open(rpmdb rpmdb, rpmTag rpmtag, dbiIndex * dbip)
 	    if (rc == 0) {
 		rpmdb->db_dbenv = dbenv;
 		rpmdb->db_opens = 1;
+
+		if (extra_lock == -1 && getuid() == 0)
+		    extra_lock = acquire_extra_lock(dbhome);
+	        dbenv->app_private = (void*) extra_lock;
 	    }
 	} else {
 	    dbenv = rpmdb->db_dbenv;