2 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
5 #pragma ident "%Z%%M% %I% %E% SMI"
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
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 $";
37 #include "policy_db.h"
41 #define MAX_LOCK_TRIES 5
44 osa_adb_lock_ent lockinfo
;
45 struct _locklist
*next
;
48 krb5_error_code
osa_adb_create_db(char *filename
, char *lockfilename
,
55 memset(&btinfo
, 0, sizeof(btinfo
));
60 btinfo
.minkeypage
= 0;
61 btinfo
.compare
= NULL
;
63 db
= dbopen(filename
, O_RDWR
| O_CREAT
| O_EXCL
, 0600, DB_BTREE
, &btinfo
);
66 if (db
->close(db
) < 0)
69 /* only create the lock file if we successfully created the db */
70 lf
= THREEPARAMOPEN(lockfilename
, O_RDWR
| O_CREAT
| O_EXCL
, 0600);
78 krb5_error_code
osa_adb_destroy_db(char *filename
, char *lockfilename
,
81 /* the admin databases do not contain security-critical data */
82 if (unlink(filename
) < 0 ||
83 unlink(lockfilename
) < 0)
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
;
94 /* make sure todb exists */
96 if ((ret
= osa_adb_create_db(fileto
, lockto
, magic
)) &&
100 if ((ret
= osa_adb_init_db(&fromdb
, filefrom
, lockfrom
, magic
)))
102 if ((ret
= osa_adb_init_db(&todb
, fileto
, lockto
, magic
))) {
103 (void) osa_adb_fini_db(fromdb
, magic
);
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
);
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
);
116 if ((rename(filefrom
, fileto
) < 0)) {
117 (void) osa_adb_fini_db(fromdb
, magic
);
118 (void) osa_adb_fini_db(todb
, magic
);
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
);
131 (void) osa_adb_fini_db(fromdb
, magic
);
132 (void) osa_adb_fini_db(todb
, magic
);
136 krb5_error_code
osa_adb_init_db(osa_adb_db_t
*dbp
, char *filename
,
137 char *lockfilename
, int magic
)
140 static struct _locklist
*locklist
= NULL
;
141 struct _locklist
*lockp
;
142 krb5_error_code code
;
144 if (dbp
== NULL
|| filename
== NULL
)
147 db
= (osa_adb_princ_t
) malloc(sizeof(osa_adb_db_ent
));
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;
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 */
195 if (strcmp(lockp
->lockinfo
.filename
, lockfilename
) == 0)
201 /* doesn't exist, create it, add to list */
202 lockp
= (struct _locklist
*) malloc(sizeof(*lockp
));
207 memset(lockp
, 0, sizeof(*lockp
));
208 lockp
->next
= locklist
;
212 /* now initialize lockp->lockinfo if necessary */
213 if (lockp
->lockinfo
.lockfile
== NULL
) {
214 if ((code
= krb5int_init_context_kdc(&lockp
->lockinfo
.context
))) {
216 return((krb5_error_code
) code
);
220 * needs be open read/write so that write locking can work with
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
229 if ((lockp
->lockinfo
.lockfile
= fopen(lockfilename
, "rF"))
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
;
243 db
->filename
= strdup(filename
);
251 krb5_error_code
osa_adb_fini_db(osa_adb_db_t db
, int magic
)
253 if (db
->magic
!= magic
)
255 if (db
->lock
->refcnt
== 0) {
256 /* barry says this can't happen */
257 return OSA_ADB_FAILURE
;
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
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
);
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 */
294 case KRB5_DB_LOCKMODE_PERMANENT
:
297 case KRB5_DB_LOCKMODE_EXCLUSIVE
:
298 krb5_mode
= KRB5_LOCKMODE_EXCLUSIVE
;
300 case KRB5_DB_LOCKMODE_SHARED
:
301 krb5_mode
= KRB5_LOCKMODE_SHARED
;
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) {
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
;
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
;
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
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 */
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 */
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 */
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
;
368 krb5_error_code
osa_adb_release_lock(osa_adb_db_t db
)
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
,
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
)))
387 db
->lock
->lockmode
= 0;
392 krb5_error_code
osa_adb_open_and_lock(osa_adb_princ_t db
, int locktype
)
396 ret
= osa_adb_get_lock(db
, locktype
);
397 if (ret
!= OSA_ADB_OK
)
402 db
->db
= dbopen(db
->filename
, O_RDWR
, 0600, DB_BTREE
, &db
->btinfo
);
410 db
->db
= dbopen(db
->filename
, O_RDWR
, 0600, DB_HASH
, &db
->info
);
414 (void) osa_adb_release_lock(db
);
416 return OSA_ADB_BAD_DB
;
424 krb5_error_code
osa_adb_close_and_unlock(osa_adb_princ_t db
)
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
;
435 return(osa_adb_release_lock(db
));