diff options
Diffstat (limited to 'nss/nss_db/db-open.c')
-rw-r--r-- | nss/nss_db/db-open.c | 299 |
1 files changed, 145 insertions, 154 deletions
diff --git a/nss/nss_db/db-open.c b/nss/nss_db/db-open.c index 470af043a6..6a21a0af58 100644 --- a/nss/nss_db/db-open.c +++ b/nss/nss_db/db-open.c @@ -20,35 +20,42 @@ #include <errno.h> #include <fcntl.h> #include <dlfcn.h> -#include <errno.h> #include <stdlib.h> -#include <libintl.h> +#include <string.h> #include <bits/libc-lock.h> #include "dummy-db.h" -#include "nsswitch.h" #include "nss_db.h" /* This file contains the functions used to open and close the databases - read by the rest of libnss_db. They are not thread safe; the caller - must handle locking. + read by the rest of libnss_db. Not all of them are thread safe; + make sure the caller does the appropriate locking. We dynamically load the database library, so that it does not have - to be present when glibc is compiled. Once loaded, libdb is never - unloaded again unless this library is unloaded (from the free_mem - routine in nsswitch.c) - we catch the unload by providing a shlib - destructor. (XXX Does it work?) */ - + to be present when glibc is compiled. Once loaded, the database + library is never never unloaded again until the libnss_db module is + unloaded (from the free_mem routine in nsswitch.c) -- we catch the + unload by providing a shlib destructor. (XXX Does that actually + work?) */ + +/* Handle for the shared Berkeley DB library. If non-null, the + database library is completely loaded and ready to be used by + multithreaded code. */ static void *libdb_handle; + +/* The version of the Berkeley DB library we are using. */ enum { nodb, db24, db27, db30 } libdb_version; + +/* Pointer to the db_open function. For use with DB 2.x. */ static int (*libdb_db_open) (const char *, int, uint32_t, int, void *, void *, void **); +/* Pointer to the db_create function. For use with DB 3.x. */ static int (*libdb_db_create) (void *, void *, uint32_t); /* Constants which vary from version to version are actually variables @@ -65,13 +72,17 @@ int db_notfound; /* Locks the static variables in this file. */ __libc_lock_define_initialized (static, lock) -/* Dynamically load the database library. +/* Dynamically load the database library. Return zero if successful, + non-zero if no suitable version of the library could be loaded. + Must be called with the above lock held if it might run in a + multithreaded context. + We try currently: - libdb.so.3: the name used by glibc 2.1 - libdb-3.0.so: the name used by db-3.0.x and maybe others in the future. */ - -int + +enum nss_status load_db (void) { static const char *libnames[] = { "libdb.so.3", "libdb-3.0.so" }; @@ -83,11 +94,11 @@ load_db (void) if (libdb_handle == NULL) continue; - /* db 3.0 has db_create instead of db_open. */ + /* DB 3.0 has db_create instead of db_open. */ libdb_db_create = dlsym (libdb_handle, "db_create"); if (libdb_db_create == NULL) - /* db 2.x uses db_open. */ + /* DB 2.x uses db_open. */ libdb_db_open = dlsym (libdb_handle, "db_open"); if (libdb_db_open != NULL || libdb_db_create != NULL) @@ -129,6 +140,7 @@ load_db (void) db_rdonly = DB2x_RDONLY; } break; + case 3: /* Sanity check: Do we have db_create? */ if (libdb_db_create != NULL) @@ -141,12 +153,14 @@ load_db (void) db_rdonly = DB30_RDONLY; } break; + default: break; } } + if (libdb_version != nodb) - return 0; + return NSS_STATUS_SUCCESS; /* Clear variables. */ libdb_db_open = NULL; @@ -157,7 +171,22 @@ load_db (void) } (void) dlerror (); - return 1; + return NSS_STATUS_UNAVAIL; +} + +/* Set the `FD_CLOEXEC' flag of FD. Return 0 on success, or -1 on + error with `errno' set. */ +static int +set_cloexec_flag (int fd) +{ + int oldflags = fcntl (fd, F_GETFD, 0); + + if (oldflags < 0) + return oldflags; + + oldflags |= FD_CLOEXEC; + + return fcntl (fd, F_SETFD, oldflags); } /* Make sure we don't use the library anymore once we are shutting down. */ @@ -173,6 +202,9 @@ unload_db (void) } } +/* Open the database stored in FILE. If succesful, store the database + handle in *DBP and return NSS_STATUS_SUCCESS. On failure, return + the appropriate lookup status. */ enum nss_status internal_setent (const char *file, NSS_DB **dbp) { @@ -184,26 +216,26 @@ internal_setent (const char *file, NSS_DB **dbp) { __libc_lock_lock (lock); - status = load_db (); + if (libdb_db_open == NULL && libdb_db_create == NULL) + status = load_db (); __libc_lock_unlock (lock); - - if (status != 0) - return status; } - status = dbopen (file, db_rdonly, 0, dbp); + if (status == NSS_STATUS_SUCCESS) + status = dbopen (file, db_rdonly, 0, dbp); } return status; } -/* Close the database file. */ +/* Close the database *DBP. */ void internal_endent (NSS_DB **dbp) { NSS_DB *db = *dbp; + if (db != NULL) { DL_CALL_FCT (db->close, (db->db, 0)); @@ -211,188 +243,147 @@ internal_endent (NSS_DB **dbp) } } +/* Allocate a cursor for database DB and transaction TXN. On success, + store the cursor in *DBCP and return zero. Otherwise return an + error value. */ int db_cursor (void *db, void *txn, NSS_DBC **dbcp) { - void *ptr; - NSS_DBC *dbc = NULL; + NSS_DBC *dbc; int ret; + dbc = (NSS_DBC *) malloc (sizeof (NSS_DBC)); + if (dbc == NULL) + return ENOMEM; + switch (libdb_version) { case db24: - ret = ((struct db24 *) db)->cursor (db, txn, &ptr); + ret = ((struct db24 *) db)->cursor (db, txn, &dbc->cursor); + + if (ret == 0) + dbc->c_get = ((struct dbc24 *) dbc->cursor)->c_get; break; + case db27: - ret = ((struct db27 *) db)->cursor (db, txn, &ptr, 0); + ret = ((struct db27 *) db)->cursor (db, txn, &dbc->cursor, 0); + + if (ret == 0) + dbc->c_get = ((struct dbc27 *) dbc->cursor)->c_get; break; + case db30: - ret = ((struct db30 *) db)->cursor (db, txn, &ptr, 0); + ret = ((struct db30 *) db)->cursor (db, txn, &dbc->cursor, 0); + + if (ret == 0) + dbc->c_get = ((struct dbc30 *) dbc->cursor)->c_get; break; + default: abort (); } - if (ret == 0) + if (ret != 0) { - dbc = (NSS_DBC *) malloc (sizeof (NSS_DBC)); - if (dbc == NULL) - return 1; - dbc->cursor = ptr; - - switch (libdb_version) - { - case db24: - dbc->c_get = - (int (*) (void *, void *, void *, uint32_t)) - ((struct dbc24 *) ptr)->c_get; - break; - case db27: - dbc->c_get = - (int (*) (void *, void *, void *, uint32_t)) - ((struct dbc27 *) ptr)->c_get; - break; - case db30: - dbc->c_get = - (int (*) (void *, void *, void *, uint32_t)) - ((struct dbc30 *) ptr)->c_get; - default: - abort (); - } + free (dbc); + return ret; } *dbcp = dbc; - return ret; + return 0; } +/* Open the database in FNAME, for access specified by FLAGS. If + opening the database causes the file FNAME to be created, it is + created with MODE. If succesful, store the database handle in *DBP + and return NSS_STATUS_SUCCESS. On failure, return the appropriate + lookup status. */ int dbopen (const char *fname, int oper, int mode, NSS_DB **dbp) { int err; - int result; int fd; - void *odb; - enum nss_status status = NSS_STATUS_SUCCESS; NSS_DB *db; + /* Construct the object we pass up. */ + db = (NSS_DB *) calloc (1, sizeof (NSS_DB)); + if (db == NULL) + return NSS_STATUS_UNAVAIL; + + /* Initialize the object. */ + db->cursor = db_cursor; + /* Actually open the database. */ switch (libdb_version) { case db24: case db27: err = DL_CALL_FCT (libdb_db_open, - (fname, DB_BTREE, oper, mode, NULL, NULL, &odb)); + (fname, DB_BTREE, oper, mode, NULL, NULL, &db->db)); if (err != 0) + goto fail; + + if (libdb_version) { - __set_errno (err); - *dbp = NULL; - return err == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL; + db->close = ((struct db24 *) db->db)->close; + db->fd = ((struct db24 *) db->db)->fd; + db->get = ((struct db24 *) db->db)->get; + db->put = ((struct db24 *) db->db)->put; + } + else + { + db->close = ((struct db27 *) db->db)->close; + db->fd = ((struct db27 *) db->db)->fd; + db->get = ((struct db27 *) db->db)->get; + db->put = ((struct db27 *) db->db)->put; } break; + case db30: - err = DL_CALL_FCT (libdb_db_create, (&odb, NULL, 0)); + err = DL_CALL_FCT (libdb_db_create, (db->db, NULL, 0)); if (err != 0) - { - __set_errno (err); - *dbp = NULL; - return err == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL; - } - err = ((struct db30 *) odb)->open (odb, fname, NULL, DB_BTREE, - oper, mode); + goto fail; + + db->close = ((struct db30 *) db->db)->close; + db->fd = ((struct db30 *) db->db)->fd; + db->get = ((struct db30 *) db->db)->get; + db->put = ((struct db30 *) db->db)->put; + + err = ((struct db30 *) db->db)->open (db->db, fname, NULL, DB_BTREE, + oper, mode); if (err != 0) - { - __set_errno (err); - /* Free all resources. */ - ((struct db30 *) odb)->close (odb, 0); - *dbp = NULL; - return NSS_STATUS_UNAVAIL; - } + goto fail; break; + default: abort (); } - - /* Construct the object we pass up. */ - db = (NSS_DB *) malloc (sizeof (NSS_DB)); - if (db != NULL) - { - db->db = odb; - /* The functions are at different positions for the different - versions. Sigh. */ - switch (libdb_version) - { - case db24: - db->close = - (int (*) (void *, uint32_t)) ((struct db24 *) odb)->close; - db->fd = - (int (*) (void *, int *)) ((struct db24 *) odb)->fd; - db->get = - (int (*) (void *, void *, void *, void *, uint32_t)) - ((struct db24 *) odb)->get; - db->put = - (int (*) (void *, void *, void *, void *, uint32_t)) - ((struct db24 *) odb)->put; - break; - case db27: - db->close = - (int (*) (void *, uint32_t)) ((struct db27 *) odb)->close; - db->fd = - (int (*) (void *, int *)) ((struct db27 *) odb)->fd; - db->get = - (int (*) (void *, void *, void *, void *, uint32_t)) - ((struct db27 *) odb)->get; - db->put = - (int (*) (void *, void *, void *, void *, uint32_t)) - ((struct db27 *) odb)->put; - break; - case db30: - db->close = - (int (*) (void *, uint32_t)) ((struct db30 *) odb)->close; - db->fd = - (int (*) (void *, int *)) ((struct db30 *) odb)->fd; - db->get = - (int (*) (void *, void *, void *, void *, uint32_t)) - ((struct db30 *) odb)->get; - db->put = - (int (*) (void *, void *, void *, void *, uint32_t)) - ((struct db30 *) odb)->put; - break; - default: - abort (); - } - db->cursor = db_cursor; + /* We have to make sure the file is `closed on exec'. */ + err = DL_CALL_FCT (db->fd, (db->db, &fd)); + if (err != 0) + goto fail; + if (set_cloexec_flag (fd) < 0) + goto fail; - /* We have to make sure the file is `closed on exec'. */ - err = DL_CALL_FCT (db->fd, (odb, &fd)); - if (err != 0) - { - __set_errno (err); - result = -1; - } - else - { - int flags = result = fcntl (fd, F_GETFD, 0); + *dbp = db; - if (result >= 0) - { - flags |= FD_CLOEXEC; - result = fcntl (fd, F_SETFD, flags); - } - } - if (result < 0) - { - /* Something went wrong. Close the stream and return a - failure. */ - DL_CALL_FCT (db->close, (odb, 0)); - free (db); - status = NSS_STATUS_UNAVAIL; - db = NULL; - } + return NSS_STATUS_UNAVAIL; + + fail: + /* Something went wrong. Close the database if necessary. */ + if (db) + { + if (db->db && db->close) + DL_CALL_FCT (db->close, (db->db, 0)); + free (db); } - *dbp = db; - - return status; + + /* Make sure `errno' is set. */ + if (err) + __set_errno (err); + + return err == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL; } |