aboutsummaryrefslogtreecommitdiff
path: root/src/plugins/kdb/db2/adb_openclose.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/kdb/db2/adb_openclose.c')
-rw-r--r--src/plugins/kdb/db2/adb_openclose.c412
1 files changed, 412 insertions, 0 deletions
diff --git a/src/plugins/kdb/db2/adb_openclose.c b/src/plugins/kdb/db2/adb_openclose.c
new file mode 100644
index 0000000..97ce112
--- /dev/null
+++ b/src/plugins/kdb/db2/adb_openclose.c
@@ -0,0 +1,412 @@
+/*
+ * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
+ *
+ * $Header$
+ */
+
+#if !defined(lint) && !defined(__CODECENTER__)
+static char *rcsid = "$Header$";
+#endif
+
+#include <sys/file.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <k5-int.h>
+#include "policy_db.h"
+#include <stdlib.h>
+#include <db.h>
+
+#define MAX_LOCK_TRIES 5
+
+struct _locklist {
+ osa_adb_lock_ent lockinfo;
+ struct _locklist *next;
+};
+
+krb5_error_code osa_adb_create_db(char *filename, char *lockfilename,
+ int magic)
+{
+ int lf;
+ DB *db;
+ BTREEINFO btinfo;
+
+ memset(&btinfo, 0, sizeof(btinfo));
+ btinfo.flags = 0;
+ btinfo.cachesize = 0;
+ btinfo.psize = 4096;
+ btinfo.lorder = 0;
+ btinfo.minkeypage = 0;
+ btinfo.compare = NULL;
+ btinfo.prefix = NULL;
+ db = dbopen(filename, O_RDWR | O_CREAT | O_EXCL, 0600, DB_BTREE, &btinfo);
+ if (db == NULL)
+ return errno;
+ if (db->close(db) < 0)
+ return errno;
+
+ /* only create the lock file if we successfully created the db */
+ lf = THREEPARAMOPEN(lockfilename, O_RDWR | O_CREAT | O_EXCL, 0600);
+ if (lf == -1)
+ return errno;
+ (void) close(lf);
+
+ return OSA_ADB_OK;
+}
+
+krb5_error_code osa_adb_destroy_db(char *filename, char *lockfilename,
+ int magic)
+{
+ /* the admin databases do not contain security-critical data */
+ if (unlink(filename) < 0 ||
+ unlink(lockfilename) < 0)
+ return errno;
+ return OSA_ADB_OK;
+}
+
+krb5_error_code osa_adb_rename_db(char *filefrom, char *lockfrom,
+ char *fileto, char *lockto, int magic)
+{
+ osa_adb_db_t fromdb, todb;
+ krb5_error_code ret;
+
+ /* make sure todb exists */
+ if ((ret = osa_adb_create_db(fileto, lockto, magic)) &&
+ ret != EEXIST)
+ return ret;
+
+ if ((ret = osa_adb_init_db(&fromdb, filefrom, lockfrom, magic)))
+ return ret;
+ if ((ret = osa_adb_init_db(&todb, fileto, lockto, magic))) {
+ (void) osa_adb_fini_db(fromdb, magic);
+ return ret;
+ }
+ if ((ret = osa_adb_get_lock(fromdb, KRB5_DB_LOCKMODE_PERMANENT))) {
+ (void) osa_adb_fini_db(fromdb, magic);
+ (void) osa_adb_fini_db(todb, magic);
+ return ret;
+ }
+ if ((ret = osa_adb_get_lock(todb, KRB5_DB_LOCKMODE_PERMANENT))) {
+ (void) osa_adb_fini_db(fromdb, magic);
+ (void) osa_adb_fini_db(todb, magic);
+ return ret;
+ }
+ if ((rename(filefrom, fileto) < 0)) {
+ (void) osa_adb_fini_db(fromdb, magic);
+ (void) osa_adb_fini_db(todb, magic);
+ return errno;
+ }
+ /*
+ * Do not release the lock on fromdb because it is being renamed
+ * out of existence; no one can ever use it again.
+ */
+ if ((ret = osa_adb_release_lock(todb))) {
+ (void) osa_adb_fini_db(fromdb, magic);
+ (void) osa_adb_fini_db(todb, magic);
+ return ret;
+ }
+
+ (void) osa_adb_fini_db(fromdb, magic);
+ (void) osa_adb_fini_db(todb, magic);
+ return 0;
+}
+
+krb5_error_code osa_adb_init_db(osa_adb_db_t *dbp, char *filename,
+ char *lockfilename, int magic)
+{
+ osa_adb_db_t db;
+ static struct _locklist *locklist = NULL;
+ struct _locklist *lockp;
+ krb5_error_code code;
+
+ if (dbp == NULL || filename == NULL)
+ return EINVAL;
+
+ db = (osa_adb_princ_t) malloc(sizeof(osa_adb_db_ent));
+ if (db == NULL)
+ return ENOMEM;
+
+ memset(db, 0, sizeof(*db));
+ db->info.hash = NULL;
+ db->info.bsize = 256;
+ db->info.ffactor = 8;
+ db->info.nelem = 25000;
+ db->info.lorder = 0;
+
+ db->btinfo.flags = 0;
+ db->btinfo.cachesize = 0;
+ db->btinfo.psize = 4096;
+ db->btinfo.lorder = 0;
+ db->btinfo.minkeypage = 0;
+ db->btinfo.compare = NULL;
+ db->btinfo.prefix = NULL;
+ /*
+ * A process is allowed to open the same database multiple times
+ * and access it via different handles. If the handles use
+ * distinct lockinfo structures, things get confused: lock(A),
+ * lock(B), release(B) will result in the kernel unlocking the
+ * lock file but handle A will still think the file is locked.
+ * Therefore, all handles using the same lock file must share a
+ * single lockinfo structure.
+ *
+ * It is not sufficient to have a single lockinfo structure,
+ * however, because a single process may also wish to open
+ * multiple different databases simultaneously, with different
+ * lock files. This code used to use a single static lockinfo
+ * structure, which means that the second database opened used
+ * the first database's lock file. This was Bad.
+ *
+ * We now maintain a linked list of lockinfo structures, keyed by
+ * lockfilename. An entry is added when this function is called
+ * with a new lockfilename, and all subsequent calls with that
+ * lockfilename use the existing entry, updating the refcnt.
+ * When the database is closed with fini_db(), the refcnt is
+ * decremented, and when it is zero the lockinfo structure is
+ * freed and reset. The entry in the linked list, however, is
+ * never removed; it will just be reinitialized the next time
+ * init_db is called with the right lockfilename.
+ */
+
+ /* find or create the lockinfo structure for lockfilename */
+ lockp = locklist;
+ while (lockp) {
+ if (strcmp(lockp->lockinfo.filename, lockfilename) == 0)
+ break;
+ else
+ lockp = lockp->next;
+ }
+ if (lockp == NULL) {
+ /* doesn't exist, create it, add to list */
+ lockp = (struct _locklist *) malloc(sizeof(*lockp));
+ if (lockp == NULL) {
+ free(db);
+ return ENOMEM;
+ }
+ memset(lockp, 0, sizeof(*lockp));
+ lockp->next = locklist;
+ locklist = lockp;
+ }
+
+ /* now initialize lockp->lockinfo if necessary */
+ if (lockp->lockinfo.lockfile == NULL) {
+ if ((code = krb5_init_context(&lockp->lockinfo.context))) {
+ free(db);
+ return((krb5_error_code) code);
+ }
+
+ /*
+ * needs be open read/write so that write locking can work with
+ * POSIX systems
+ */
+ lockp->lockinfo.filename = strdup(lockfilename);
+ if ((lockp->lockinfo.lockfile = fopen(lockfilename, "r+")) == NULL) {
+ /*
+ * maybe someone took away write permission so we could only
+ * get shared locks?
+ */
+ if ((lockp->lockinfo.lockfile = fopen(lockfilename, "r"))
+ == NULL) {
+ free(db);
+ return OSA_ADB_NOLOCKFILE;
+ }
+ }
+ lockp->lockinfo.lockmode = lockp->lockinfo.lockcnt = 0;
+ }
+
+ /* lockp is set, lockinfo is initialized, update the reference count */
+ db->lock = &lockp->lockinfo;
+ db->lock->refcnt++;
+
+ db->opencnt = 0;
+ db->filename = strdup(filename);
+ db->magic = magic;
+
+ *dbp = db;
+
+ return OSA_ADB_OK;
+}
+
+krb5_error_code osa_adb_fini_db(osa_adb_db_t db, int magic)
+{
+ if (db->magic != magic)
+ return EINVAL;
+ if (db->lock->refcnt == 0) {
+ /* barry says this can't happen */
+ return OSA_ADB_FAILURE;
+ } else {
+ db->lock->refcnt--;
+ }
+
+ if (db->lock->refcnt == 0) {
+ /*
+ * Don't free db->lock->filename, it is used as a key to
+ * find the lockinfo entry in the linked list. If the
+ * lockfile doesn't exist, we must be closing the database
+ * after trashing it. This has to be allowed, so don't
+ * generate an error.
+ */
+ if (db->lock->lockmode != KRB5_DB_LOCKMODE_PERMANENT)
+ (void) fclose(db->lock->lockfile);
+ db->lock->lockfile = NULL;
+ krb5_free_context(db->lock->context);
+ }
+
+ db->magic = 0;
+ free(db->filename);
+ free(db);
+ return OSA_ADB_OK;
+}
+
+krb5_error_code osa_adb_get_lock(osa_adb_db_t db, int mode)
+{
+ int tries, gotlock, perm, krb5_mode, ret = 0;
+
+ if (db->lock->lockmode >= mode) {
+ /* No need to upgrade lock, just incr refcnt and return */
+ db->lock->lockcnt++;
+ return(OSA_ADB_OK);
+ }
+
+ perm = 0;
+ switch (mode) {
+ case KRB5_DB_LOCKMODE_PERMANENT:
+ perm = 1;
+ case KRB5_DB_LOCKMODE_EXCLUSIVE:
+ krb5_mode = KRB5_LOCKMODE_EXCLUSIVE;
+ break;
+ case KRB5_DB_LOCKMODE_SHARED:
+ krb5_mode = KRB5_LOCKMODE_SHARED;
+ break;
+ default:
+ return(EINVAL);
+ }
+
+ for (gotlock = tries = 0; tries < MAX_LOCK_TRIES; tries++) {
+ if ((ret = krb5_lock_file(db->lock->context,
+ fileno(db->lock->lockfile),
+ krb5_mode|KRB5_LOCKMODE_DONTBLOCK)) == 0) {
+ gotlock++;
+ break;
+ } else if (ret == EBADF && mode == KRB5_DB_LOCKMODE_EXCLUSIVE)
+ /* tried to exclusive-lock something we don't have */
+ /* write access to */
+ return OSA_ADB_NOEXCL_PERM;
+
+ sleep(1);
+ }
+
+ /* test for all the likely "can't get lock" error codes */
+ if (ret == EACCES || ret == EAGAIN || ret == EWOULDBLOCK)
+ return OSA_ADB_CANTLOCK_DB;
+ else if (ret != 0)
+ return ret;
+
+ /*
+ * If the file no longer exists, someone acquired a permanent
+ * lock. If that process terminates its exclusive lock is lost,
+ * but if we already had the file open we can (probably) lock it
+ * even though it has been unlinked. So we need to insist that
+ * it exist.
+ */
+ if (access(db->lock->filename, F_OK) < 0) {
+ (void) krb5_lock_file(db->lock->context,
+ fileno(db->lock->lockfile),
+ KRB5_LOCKMODE_UNLOCK);
+ return OSA_ADB_NOLOCKFILE;
+ }
+
+ /* we have the shared/exclusive lock */
+
+ if (perm) {
+ if (unlink(db->lock->filename) < 0) {
+ /* somehow we can't delete the file, but we already */
+ /* have the lock, so release it and return */
+
+ ret = errno;
+ (void) krb5_lock_file(db->lock->context,
+ fileno(db->lock->lockfile),
+ KRB5_LOCKMODE_UNLOCK);
+
+ /* maybe we should return CANTLOCK_DB.. but that would */
+ /* look just like the db was already locked */
+ return ret;
+ }
+
+ /* this releases our exclusive lock.. which is okay because */
+ /* now no one else can get one either */
+ (void) fclose(db->lock->lockfile);
+ }
+
+ db->lock->lockmode = mode;
+ db->lock->lockcnt++;
+ return OSA_ADB_OK;
+}
+
+krb5_error_code osa_adb_release_lock(osa_adb_db_t db)
+{
+ int ret, fd;
+
+ if (!db->lock->lockcnt) /* lock already unlocked */
+ return OSA_ADB_NOTLOCKED;
+
+ if (--db->lock->lockcnt == 0) {
+ if (db->lock->lockmode == KRB5_DB_LOCKMODE_PERMANENT) {
+ /* now we need to create the file since it does not exist */
+ fd = THREEPARAMOPEN(db->lock->filename,O_RDWR | O_CREAT | O_EXCL,
+ 0600);
+ if ((db->lock->lockfile = fdopen(fd, "w+")) == NULL)
+ return OSA_ADB_NOLOCKFILE;
+ } else if ((ret = krb5_lock_file(db->lock->context,
+ fileno(db->lock->lockfile),
+ KRB5_LOCKMODE_UNLOCK)))
+ return ret;
+
+ db->lock->lockmode = 0;
+ }
+ return OSA_ADB_OK;
+}
+
+krb5_error_code osa_adb_open_and_lock(osa_adb_princ_t db, int locktype)
+{
+ int ret;
+
+ ret = osa_adb_get_lock(db, locktype);
+ if (ret != OSA_ADB_OK)
+ return ret;
+ if (db->opencnt)
+ goto open_ok;
+
+ db->db = dbopen(db->filename, O_RDWR, 0600, DB_BTREE, &db->btinfo);
+ if (db->db != NULL)
+ goto open_ok;
+ switch (errno) {
+#ifdef EFTYPE
+ case EFTYPE:
+#endif
+ case EINVAL:
+ db->db = dbopen(db->filename, O_RDWR, 0600, DB_HASH, &db->info);
+ if (db->db != NULL)
+ goto open_ok;
+ default:
+ (void) osa_adb_release_lock(db);
+ if (errno == EINVAL)
+ return OSA_ADB_BAD_DB;
+ return errno;
+ }
+open_ok:
+ db->opencnt++;
+ return OSA_ADB_OK;
+}
+
+krb5_error_code osa_adb_close_and_unlock(osa_adb_princ_t db)
+{
+ if (--db->opencnt)
+ return osa_adb_release_lock(db);
+ if(db->db != NULL && db->db->close(db->db) == -1) {
+ (void) osa_adb_release_lock(db);
+ return OSA_ADB_FAILURE;
+ }
+
+ db->db = NULL;
+
+ return(osa_adb_release_lock(db));
+}