--- rpm-4.4.8/rpmdb/rpmdb.c.lock 2007-01-21 16:30:14.000000000 +0100 +++ rpm-4.4.8/rpmdb/rpmdb.c 2007-09-27 10:37:52.000000000 +0200 @@ -972,6 +972,18 @@ int rpmdbClose(rpmdb db) db->_dbi[dbix] = NULL; /*@=unqualifiedtrans@*/ } + + /* unlock database */ + if (db->db_perms >> 16) { + int ld = (db->db_perms >> 16) - 1; + char *env = getenv("RPMLOCK_NOWAIT"); + rpmMessage(RPMMESS_DEBUG, "unlocking %s%s/RPMLOCK\n", db->db_root, db->db_home); + close(ld); + /* below is not fully correct when manipulating more than one DB in the same prog, + but it still catches most issues... */ + if (env && strcmp(env, "_set_by_rpm_") == 0) unsetenv("RPMLOCK_NOWAIT"); + } + db->db_errpfx = _free(db->db_errpfx); db->db_root = _free(db->db_root); db->db_home = _free(db->db_home); @@ -1116,10 +1128,7 @@ fprintf(stderr, "==> %s(%s, %s, 0x%x, 0% if (!(db->db_home && db->db_home[0])) { rpmError(RPMERR_DBOPEN, _("no dbpath has been set\n")); - db->db_root = _free(db->db_root); - db->db_home = _free(db->db_home); - db = _free(db); - /*@-globstate@*/ return NULL; /*@=globstate@*/ + goto fail; } /* XXX if default "/var/lib/rpm" path, manage %{_hrmib_path} entries too. */ @@ -1143,9 +1152,52 @@ fprintf(stderr, "==> %s(%s, %s, 0x%x, 0% dbiTagsInit(&db->db_tagn, &db->db_ndbi); db->_dbi = xcalloc(db->db_ndbi, sizeof(*db->_dbi)); db->nrefs = 0; + + { /* create the db_home (even in O_RDONLY mode) */ + const char *dbhome = rpmGenPath(db->db_root, db->db_home, NULL); + (void) rpmioMkpath(dbhome, 0755, getuid(), getgid()); + _free(dbhome); + } + { + /* lock database using exclusive lock on write, and shared lock on read */ + const char *lockfilename = rpmGenPath(db->db_root, db->db_home, "RPMLOCK"); + /* compute lockfilename and open it according to mode used */ + int ld = open(lockfilename, O_WRONLY | O_CREAT, 0644); + /* don't care if we can't write to the db (eg: running as non-superuser and doing a simple query) */ + if (ld >= 0) { + int ok = 1; + int lockmode = mode == O_RDONLY ? LOCK_SH : LOCK_EX; + rpmMessage(RPMMESS_DEBUG, "locking file %s in %s mode\n", lockfilename, (mode == O_RDONLY ? "shared" : "exclusive")); + if (flock(ld, lockmode | LOCK_NB) != 0) { + if (getenv("RPMLOCK_NOWAIT") == NULL) { + rpmMessage(RPMMESS_WARNING, + _("waiting for transaction lock on %s\n"), lockfilename); + if (flock(ld, lockmode) != 0) ok = 0; + } else ok = 0; + } + if (ok) { + /* HACK to keep ld without breaking compabilities (changing db size) */ + db->db_perms |= (ld + 1) << 16; + setenv("RPMLOCK_NOWAIT", "_set_by_rpm_", 0); + } else { + close(ld); + rpmError( RPMERR_DBOPEN, + _("cannot open lock file %s in %s mode\n"), lockfilename, (mode == O_RDONLY ? "shared" : "exclusive")); + _free(lockfilename); + goto fail; + } + } + _free(lockfilename); + } /*@-globstate@*/ return rpmdbLink(db, "rpmdbCreate"); /*@=globstate@*/ + + fail: + db->db_root = _free(db->db_root); + db->db_home = _free(db->db_home); + db = _free(db); + /*@-globstate@*/ return NULL; /*@=globstate@*/ } /*@=mods@*/ /*@=exportheader@*/ @@ -3665,6 +3717,11 @@ static int rpmdbRemoveDatabase(const cha break; } + sprintf(filename, "%s/%s/RPMLOCK", prefix, dbpath); + (void)rpmCleanPath(filename); + rpmMessage(RPMMESS_DEBUG, "removing %s\n", filename); + xx = unlink(filename); + sprintf(filename, "%s/%s", prefix, dbpath); (void)rpmCleanPath(filename); xx = rmdir(filename); @@ -3775,6 +3832,12 @@ static int rpmdbMoveDatabase(const char case 0: break; } + + sprintf(ofilename, "%s/%s/RPMLOCK", prefix, olddbpath); + (void)rpmCleanPath(ofilename); + rpmMessage(RPMMESS_DEBUG, "removing %s\n", ofilename); + xx = unlink(ofilename); + #ifdef SQLITE_HACK_XXX if (rc || _olddbapi == _newdbapi) return rc;