dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / lib / krb5 / plugins / kdb / db2 / adb_openclose.c
blobe65474f460bcf55fe66a712466d839bb96a1ccce
1 /*
2 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
5 #pragma ident "%Z%%M% %I% %E% SMI"
7 /*
8 * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
10 * Openvision retains the copyright to derivative works of
11 * this source code. Do *NOT* create a derivative of this
12 * source code before consulting with your legal department.
13 * Do *NOT* integrate *ANY* of this source code into another
14 * product before consulting with your legal department.
16 * For further information, read the top-level Openvision
17 * copyright which is contained in the top-level MIT Kerberos
18 * copyright.
20 * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
26 * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
28 * $Header: /cvs/krbdev/krb5/src/lib/kadm5/srv/adb_openclose.c,v 1.8 2002/10/08 20:20:29 tlyu Exp $
31 static char *rcsid = "$Header: /cvs/krbdev/krb5/src/lib/kadm5/srv/adb_openclose.c,v 1.8 2002/10/08 20:20:29 tlyu Exp $";
33 #include <sys/file.h>
34 #include <fcntl.h>
35 #include <unistd.h>
36 #include <k5-int.h>
37 #include "policy_db.h"
38 #include <stdlib.h>
39 #include <db.h>
41 #define MAX_LOCK_TRIES 5
43 struct _locklist {
44 osa_adb_lock_ent lockinfo;
45 struct _locklist *next;
48 krb5_error_code osa_adb_create_db(char *filename, char *lockfilename,
49 int magic)
51 int lf;
52 DB *db;
53 BTREEINFO btinfo;
55 memset(&btinfo, 0, sizeof(btinfo));
56 btinfo.flags = 0;
57 btinfo.cachesize = 0;
58 btinfo.psize = 4096;
59 btinfo.lorder = 0;
60 btinfo.minkeypage = 0;
61 btinfo.compare = NULL;
62 btinfo.prefix = NULL;
63 db = dbopen(filename, O_RDWR | O_CREAT | O_EXCL, 0600, DB_BTREE, &btinfo);
64 if (db == NULL)
65 return errno;
66 if (db->close(db) < 0)
67 return errno;
69 /* only create the lock file if we successfully created the db */
70 lf = THREEPARAMOPEN(lockfilename, O_RDWR | O_CREAT | O_EXCL, 0600);
71 if (lf == -1)
72 return errno;
73 (void) close(lf);
75 return OSA_ADB_OK;
78 krb5_error_code osa_adb_destroy_db(char *filename, char *lockfilename,
79 int magic)
81 /* the admin databases do not contain security-critical data */
82 if (unlink(filename) < 0 ||
83 unlink(lockfilename) < 0)
84 return errno;
85 return OSA_ADB_OK;
88 krb5_error_code osa_adb_rename_db(char *filefrom, char *lockfrom,
89 char *fileto, char *lockto, int magic)
91 osa_adb_db_t fromdb, todb;
92 krb5_error_code ret;
94 /* make sure todb exists */
95 /*LINTED*/
96 if ((ret = osa_adb_create_db(fileto, lockto, magic)) &&
97 ret != EEXIST)
98 return ret;
100 if ((ret = osa_adb_init_db(&fromdb, filefrom, lockfrom, magic)))
101 return ret;
102 if ((ret = osa_adb_init_db(&todb, fileto, lockto, magic))) {
103 (void) osa_adb_fini_db(fromdb, magic);
104 return ret;
106 if ((ret = osa_adb_get_lock(fromdb, KRB5_DB_LOCKMODE_PERMANENT))) {
107 (void) osa_adb_fini_db(fromdb, magic);
108 (void) osa_adb_fini_db(todb, magic);
109 return ret;
111 if ((ret = osa_adb_get_lock(todb, KRB5_DB_LOCKMODE_PERMANENT))) {
112 (void) osa_adb_fini_db(fromdb, magic);
113 (void) osa_adb_fini_db(todb, magic);
114 return ret;
116 if ((rename(filefrom, fileto) < 0)) {
117 (void) osa_adb_fini_db(fromdb, magic);
118 (void) osa_adb_fini_db(todb, magic);
119 return errno;
122 * Do not release the lock on fromdb because it is being renamed
123 * out of existence; no one can ever use it again.
125 if ((ret = osa_adb_release_lock(todb))) {
126 (void) osa_adb_fini_db(fromdb, magic);
127 (void) osa_adb_fini_db(todb, magic);
128 return ret;
131 (void) osa_adb_fini_db(fromdb, magic);
132 (void) osa_adb_fini_db(todb, magic);
133 return 0;
136 krb5_error_code osa_adb_init_db(osa_adb_db_t *dbp, char *filename,
137 char *lockfilename, int magic)
139 osa_adb_db_t db;
140 static struct _locklist *locklist = NULL;
141 struct _locklist *lockp;
142 krb5_error_code code;
144 if (dbp == NULL || filename == NULL)
145 return EINVAL;
147 db = (osa_adb_princ_t) malloc(sizeof(osa_adb_db_ent));
148 if (db == NULL)
149 return ENOMEM;
151 memset(db, 0, sizeof(*db));
152 db->info.hash = NULL;
153 db->info.bsize = 256;
154 db->info.ffactor = 8;
155 db->info.nelem = 25000;
156 db->info.lorder = 0;
158 db->btinfo.flags = 0;
159 db->btinfo.cachesize = 0;
160 db->btinfo.psize = 4096;
161 db->btinfo.lorder = 0;
162 db->btinfo.minkeypage = 0;
163 db->btinfo.compare = NULL;
164 db->btinfo.prefix = NULL;
166 * A process is allowed to open the same database multiple times
167 * and access it via different handles. If the handles use
168 * distinct lockinfo structures, things get confused: lock(A),
169 * lock(B), release(B) will result in the kernel unlocking the
170 * lock file but handle A will still think the file is locked.
171 * Therefore, all handles using the same lock file must share a
172 * single lockinfo structure.
174 * It is not sufficient to have a single lockinfo structure,
175 * however, because a single process may also wish to open
176 * multiple different databases simultaneously, with different
177 * lock files. This code used to use a single static lockinfo
178 * structure, which means that the second database opened used
179 * the first database's lock file. This was Bad.
181 * We now maintain a linked list of lockinfo structures, keyed by
182 * lockfilename. An entry is added when this function is called
183 * with a new lockfilename, and all subsequent calls with that
184 * lockfilename use the existing entry, updating the refcnt.
185 * When the database is closed with fini_db(), the refcnt is
186 * decremented, and when it is zero the lockinfo structure is
187 * freed and reset. The entry in the linked list, however, is
188 * never removed; it will just be reinitialized the next time
189 * init_db is called with the right lockfilename.
192 /* find or create the lockinfo structure for lockfilename */
193 lockp = locklist;
194 while (lockp) {
195 if (strcmp(lockp->lockinfo.filename, lockfilename) == 0)
196 break;
197 else
198 lockp = lockp->next;
200 if (lockp == NULL) {
201 /* doesn't exist, create it, add to list */
202 lockp = (struct _locklist *) malloc(sizeof(*lockp));
203 if (lockp == NULL) {
204 free(db);
205 return ENOMEM;
207 memset(lockp, 0, sizeof(*lockp));
208 lockp->next = locklist;
209 locklist = lockp;
212 /* now initialize lockp->lockinfo if necessary */
213 if (lockp->lockinfo.lockfile == NULL) {
214 if ((code = krb5int_init_context_kdc(&lockp->lockinfo.context))) {
215 free(db);
216 return((krb5_error_code) code);
220 * needs be open read/write so that write locking can work with
221 * POSIX systems
223 lockp->lockinfo.filename = strdup(lockfilename);
224 if ((lockp->lockinfo.lockfile = fopen(lockfilename, "r+F")) == NULL) {
226 * maybe someone took away write permission so we could only
227 * get shared locks?
229 if ((lockp->lockinfo.lockfile = fopen(lockfilename, "rF"))
230 == NULL) {
231 free(db);
232 return OSA_ADB_NOLOCKFILE;
235 lockp->lockinfo.lockmode = lockp->lockinfo.lockcnt = 0;
238 /* lockp is set, lockinfo is initialized, update the reference count */
239 db->lock = &lockp->lockinfo;
240 db->lock->refcnt++;
242 db->opencnt = 0;
243 db->filename = strdup(filename);
244 db->magic = magic;
246 *dbp = db;
248 return OSA_ADB_OK;
251 krb5_error_code osa_adb_fini_db(osa_adb_db_t db, int magic)
253 if (db->magic != magic)
254 return EINVAL;
255 if (db->lock->refcnt == 0) {
256 /* barry says this can't happen */
257 return OSA_ADB_FAILURE;
258 } else {
259 db->lock->refcnt--;
262 if (db->lock->refcnt == 0) {
264 * Don't free db->lock->filename, it is used as a key to
265 * find the lockinfo entry in the linked list. If the
266 * lockfile doesn't exist, we must be closing the database
267 * after trashing it. This has to be allowed, so don't
268 * generate an error.
270 if (db->lock->lockmode != KRB5_DB_LOCKMODE_PERMANENT)
271 (void) fclose(db->lock->lockfile);
272 db->lock->lockfile = NULL;
273 krb5_free_context(db->lock->context);
276 db->magic = 0;
277 free(db->filename);
278 free(db);
279 return OSA_ADB_OK;
282 krb5_error_code osa_adb_get_lock(osa_adb_db_t db, int mode)
284 int tries, gotlock, perm, krb5_mode, ret = 0;
286 if (db->lock->lockmode >= mode) {
287 /* No need to upgrade lock, just incr refcnt and return */
288 db->lock->lockcnt++;
289 return(OSA_ADB_OK);
292 perm = 0;
293 switch (mode) {
294 case KRB5_DB_LOCKMODE_PERMANENT:
295 perm = 1;
296 /*LINTED*/
297 case KRB5_DB_LOCKMODE_EXCLUSIVE:
298 krb5_mode = KRB5_LOCKMODE_EXCLUSIVE;
299 break;
300 case KRB5_DB_LOCKMODE_SHARED:
301 krb5_mode = KRB5_LOCKMODE_SHARED;
302 break;
303 default:
304 return(EINVAL);
307 for (gotlock = tries = 0; tries < MAX_LOCK_TRIES; tries++) {
308 if ((ret = krb5_lock_file(db->lock->context,
309 fileno(db->lock->lockfile),
310 krb5_mode|KRB5_LOCKMODE_DONTBLOCK)) == 0) {
311 gotlock++;
312 break;
313 } else if (ret == EBADF && mode == KRB5_DB_LOCKMODE_EXCLUSIVE)
314 /* tried to exclusive-lock something we don't have */
315 /* write access to */
316 return OSA_ADB_NOEXCL_PERM;
318 sleep(1);
321 /* test for all the likely "can't get lock" error codes */
322 if (ret == EACCES || ret == EAGAIN || ret == EWOULDBLOCK)
323 return OSA_ADB_CANTLOCK_DB;
324 else if (ret != 0)
325 return ret;
328 * If the file no longer exists, someone acquired a permanent
329 * lock. If that process terminates its exclusive lock is lost,
330 * but if we already had the file open we can (probably) lock it
331 * even though it has been unlinked. So we need to insist that
332 * it exist.
334 if (access(db->lock->filename, F_OK) < 0) {
335 (void) krb5_lock_file(db->lock->context,
336 fileno(db->lock->lockfile),
337 KRB5_LOCKMODE_UNLOCK);
338 return OSA_ADB_NOLOCKFILE;
341 /* we have the shared/exclusive lock */
343 if (perm) {
344 if (unlink(db->lock->filename) < 0) {
345 /* somehow we can't delete the file, but we already */
346 /* have the lock, so release it and return */
348 ret = errno;
349 (void) krb5_lock_file(db->lock->context,
350 fileno(db->lock->lockfile),
351 KRB5_LOCKMODE_UNLOCK);
353 /* maybe we should return CANTLOCK_DB.. but that would */
354 /* look just like the db was already locked */
355 return ret;
358 /* this releases our exclusive lock.. which is okay because */
359 /* now no one else can get one either */
360 (void) fclose(db->lock->lockfile);
363 db->lock->lockmode = mode;
364 db->lock->lockcnt++;
365 return OSA_ADB_OK;
368 krb5_error_code osa_adb_release_lock(osa_adb_db_t db)
370 int ret, fd;
372 if (!db->lock->lockcnt) /* lock already unlocked */
373 return OSA_ADB_NOTLOCKED;
375 if (--db->lock->lockcnt == 0) {
376 if (db->lock->lockmode == KRB5_DB_LOCKMODE_PERMANENT) {
377 /* now we need to create the file since it does not exist */
378 fd = THREEPARAMOPEN(db->lock->filename,O_RDWR | O_CREAT | O_EXCL,
379 0600);
380 if ((db->lock->lockfile = fdopen(fd, "w+F")) == NULL)
381 return OSA_ADB_NOLOCKFILE;
382 } else if ((ret = krb5_lock_file(db->lock->context,
383 fileno(db->lock->lockfile),
384 KRB5_LOCKMODE_UNLOCK)))
385 return ret;
387 db->lock->lockmode = 0;
389 return OSA_ADB_OK;
392 krb5_error_code osa_adb_open_and_lock(osa_adb_princ_t db, int locktype)
394 int ret;
396 ret = osa_adb_get_lock(db, locktype);
397 if (ret != OSA_ADB_OK)
398 return ret;
399 if (db->opencnt)
400 goto open_ok;
402 db->db = dbopen(db->filename, O_RDWR, 0600, DB_BTREE, &db->btinfo);
403 if (db->db != NULL)
404 goto open_ok;
405 switch (errno) {
406 #ifdef EFTYPE
407 case EFTYPE:
408 #endif
409 case EINVAL:
410 db->db = dbopen(db->filename, O_RDWR, 0600, DB_HASH, &db->info);
411 if (db->db != NULL)
412 goto open_ok;
413 default:
414 (void) osa_adb_release_lock(db);
415 if (errno == EINVAL)
416 return OSA_ADB_BAD_DB;
417 return errno;
419 open_ok:
420 db->opencnt++;
421 return OSA_ADB_OK;
424 krb5_error_code osa_adb_close_and_unlock(osa_adb_princ_t db)
426 if (--db->opencnt)
427 return osa_adb_release_lock(db);
428 if(db->db != NULL && db->db->close(db->db) == -1) {
429 (void) osa_adb_release_lock(db);
430 return OSA_ADB_FAILURE;
433 db->db = NULL;
435 return(osa_adb_release_lock(db));