summaryrefslogtreecommitdiffstats
path: root/lib/libaccess/nsdbmgmt.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libaccess/nsdbmgmt.cpp')
-rw-r--r--lib/libaccess/nsdbmgmt.cpp685
1 files changed, 685 insertions, 0 deletions
diff --git a/lib/libaccess/nsdbmgmt.cpp b/lib/libaccess/nsdbmgmt.cpp
new file mode 100644
index 00000000..c1151e08
--- /dev/null
+++ b/lib/libaccess/nsdbmgmt.cpp
@@ -0,0 +1,685 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/*
+ * Description (nsdbmgmt.h)
+ *
+ * The file describes the interface for managing information in
+ * a Netscape (server) database. A database is composed of
+ * two (libdbm) DB files. One of these (<dbname>.db) contains
+ * records indexed by a string key. These records contain the
+ * primary information in the database. A second DB file
+ * (<dbname>.id) is used to map an integer id value to a string
+ * key, which can then be used to locate a record in the first file.
+ * The interface for retrieving information from a database is
+ * described in nsdb.h.
+ */
+
+#include <base/systems.h>
+#include <netsite.h>
+#include <base/file.h>
+#define __PRIVATE_NSDB
+#include <libaccess/nsdbmgmt.h>
+#include <base/util.h>
+
+/*
+ * Description (ndbAllocId)
+ *
+ * This function allocates a unique id to be associated with a
+ * name in the primary DB file. An id bitmap is maintained in
+ * the primary DB file as a metadata record, and an entry is
+ * created in the id-to-name DB for the assigned id and the
+ * specified name. An allocated id value is always non-zero.
+ *
+ * Arguments:
+ *
+ * errp - error frame list pointer (may be null)
+ * ndb - database handle from ndbOpen()
+ * namelen - length of key of the desired record,
+ * including null terminator if any
+ * name - pointer to the key of the desired record
+ * id - pointer to returned id value
+ *
+ * Returns:
+ *
+ * If successful, the return value is zero, and the allocated id
+ * is returned through 'id'. Otherwise a non-zero error code is
+ * returned (NDBERRxxxx - see nsdb.h). If an error list is
+ * provided, an error frame will be generated when the return
+ * value is non-zero.
+ */
+
+int ndbAllocId(NSErr_t * errp,
+ void * ndb, int namelen, char * name, unsigned int * id)
+{
+ NSDB_t * ndbp = (NSDB_t *)ndb; /* database object pointer */
+ DBT key;
+ DBT rec;
+ unsigned char * idmap;
+ unsigned char * newmap = 0;
+ int m;
+ int mmsk;
+ uint32 idval;
+ int myid;
+ int i, n;
+ int rv;
+ long eid;
+
+ /*
+ * Ensure that the name does not start with the metadata
+ * prefix character.
+ */
+ if (!name || (name[0] == NDB_MDPREFIX)) goto err_name;
+
+ /*
+ * Read the primary DB file metadata record containing the id
+ * allocation bitmap.
+ */
+
+ /*
+ * We need the primary and the id-to-name DB files open for write
+ * (and implicitly read) access.
+ */
+ if ((ndbp->ndb_flags & (NDBF_WRNAME|NDBF_WRID))
+ != (NDBF_WRNAME|NDBF_WRID)) {
+
+ /* No, (re)open it */
+ rv = ndbReOpen(errp, ndb, (NDBF_WRNAME|NDBF_WRID));
+ if (rv < 0) goto punt;
+ }
+
+ /* Set the key to the id allocation bitmap record name */
+ key.data = (void *)NDB_IDMAP;
+ key.size = strlen(NDB_IDMAP) + 1;
+
+ rec.data = 0;
+ rec.size = 0;
+
+ /* Retrieve the record by its key */
+ rv = (*ndbp->ndb_pdb->get)(ndbp->ndb_pdb, &key, &rec, 0);
+ if (rv) goto err_mdget;
+
+ /* Search for an available id in the bitmap */
+ n = rec.size;
+ idmap = (unsigned char *)rec.data;
+
+ for (i = 0, m = 0; i < n; ++i) {
+
+ m = idmap[i];
+ if (m != 0) break;
+ }
+
+ /* Did we find a byte with an available bit? */
+ if (m == 0) {
+
+ /* No, need to grow the bitmap */
+ newmap = (unsigned char *)MALLOC(rec.size + 32);
+ if (newmap == 0) goto err_nomem1;
+
+ /* Initialize free space at the beginning of the new map */
+ for (i = 0; i < 32; ++i) {
+ newmap[i] = 0xff;
+ }
+
+ /* Copy the old map after it */
+ n += 32;
+ for ( ; i < n; ++i) {
+ newmap[i] = idmap[i-32];
+ }
+
+ /* Set i and m to allocate the new highest id value */
+ i = 0;
+ m = 0xff;
+ }
+ else {
+
+ /*
+ * It's unfortunate, but it appears to be necessary to copy the
+ * the ?idmap record into a new buffer before updating it, rather
+ * than simply updating it in place. The problem is that the
+ * libdbm put routine deletes the old record and then re-inserts
+ * it. But once it has deleted the old record, it may take the
+ * opportunity to move another record into the space that the
+ * old record occupied, which is the same space that the new
+ * record occupies. So the new record data is overwritten before
+ * new record is inserted. :-(
+ */
+
+ newmap = (unsigned char *)MALLOC(rec.size);
+ if (newmap == 0) goto err_nomem2;
+
+ memcpy((void *)newmap, (void *)idmap, rec.size);
+ }
+
+ /* Calculate the id associated with the low-order bit of byte i */
+ myid = (n - i - 1) << 3;
+
+ /* Find the first free (set) bit in that word */
+ for (mmsk = 1; !(m & mmsk); mmsk <<= 1, myid += 1) ;
+
+ /* Clear the bit */
+ m &= ~mmsk;
+ newmap[i] = m;
+
+ /* Write the bitmap back out */
+
+ rec.data = (void *)newmap;
+ rec.size = n;
+
+ rv = (*ndbp->ndb_pdb->put)(ndbp->ndb_pdb, &key, &rec, 0);
+
+ /* Check for error on preceding put operation */
+ if (rv) goto err_putpdb;
+
+ /* Create the key for the id-to-name record */
+ idval = myid;
+#if BYTE_ORDER == LITTLE_ENDIAN
+ M_32_SWAP(idval);
+#endif
+
+ key.data = (void *)&idval;
+ key.size = sizeof(uint32);
+
+ rec.data = (void *)name;
+ rec.size = (namelen > 0) ? namelen : (strlen(name) + 1);
+
+ /* Write the id-to-name record */
+ rv = (*ndbp->ndb_idb->put)(ndbp->ndb_idb, &key, &rec, 0);
+ if (rv) goto err_putidb;
+
+ /* Return the id value + 1, to avoid returning a zero id */
+ if (id) *id = myid + 1;
+
+ punt:
+
+ /* Free the new map space if any */
+ if (newmap) {
+ FREE(newmap);
+ }
+
+ return rv;
+
+ err_name: /* invalid name parameter */
+ eid = NSDBERR2000;
+ rv = NDBERRNAME;
+ if (name == 0) {
+ name = "(null)";
+ }
+ else if ((namelen > 0) && (namelen != strlen(name) + 1)) {
+ name = "(unprintable)";
+ }
+ (void)nserrGenerate(errp, rv, eid, NSDB_Program,
+ 2,
+ ndbp->ndb_pname, /* primary DB filename */
+ name /* name string */
+ );
+ goto punt;
+
+ err_mdget: /* error on get from primary DB file */
+ eid = NSDBERR2020;
+ rv = NDBERRMDGET;
+ (void)nserrGenerate(errp, rv, eid, NSDB_Program,
+ 2,
+ ndbp->ndb_pname, /* primary DB filename */
+ (char *)key.data /* key name string */
+ );
+ goto punt;
+
+ err_nomem1:
+ eid = NSDBERR2040;
+ goto err_nomem;
+
+ err_nomem2:
+ eid = NSDBERR2060;
+ err_nomem: /* insufficient memory */
+ rv = NDBERRNOMEM;
+ (void)nserrGenerate(errp, rv, eid, NSDB_Program, 0);
+ goto punt;
+
+ err_putpdb: /* error on put to primary DB file */
+ eid = NSDBERR2080;
+ rv = NDBERRMDPUT;
+ (void)nserrGenerate(errp, rv, eid, NSDB_Program,
+ 2,
+ ndbp->ndb_pname, /* primary DB filename */
+ (char *)key.data /* key name string */
+ );
+ goto punt;
+
+ err_putidb: /* error on put to id-to-name DB */
+ {
+ char idstring[16];
+
+ eid = NSDBERR2100;
+ rv = NDBERRIDPUT;
+
+ util_sprintf(idstring, "%d", myid);
+ (void)nserrGenerate(errp, rv, eid, NSDB_Program,
+ 2,
+ ndbp->ndb_iname, /* id-to-name DB file */
+ idstring /* id value for key */
+ );
+ }
+ goto punt;
+}
+
+/*
+ * Description (ndbDeleteName)
+ *
+ * This function deletes a named record from the primary DB file.
+ *
+ * Arguments:
+ *
+ * errp - error frame list pointer (may be null)
+ * ndb - database handle from ndbOpen()
+ * flags - (currently unused - should be zero)
+ * namelen - length of name key, including null
+ * terminator if any
+ * name - pointer to name key
+ *
+ * Returns:
+ *
+ * If successful, the return value is zero. Otherwise a non-zero
+ * error code is returned (NDBERRxxxx - see nsdberr.h). If an error
+ * list is provided, an error frame will be generated when the
+ * return value is non-zero.
+ */
+
+int ndbDeleteName(NSErr_t * errp,
+ void * ndb, int flags, int namelen, char * name)
+{
+ NSDB_t * ndbp = (NSDB_t *)ndb; /* database object pointer */
+ DBT key;
+ int eid;
+ int rv;
+
+ /* Is the primary DB open for write access? */
+ if (!(ndbp->ndb_flags & NDBF_WRNAME)) {
+
+ /* No, (re)open it */
+ rv = ndbReOpen(errp, ndb, NDBF_WRNAME);
+ if (rv) goto punt;
+ }
+
+ /* Set up the key descriptor */
+ key.data = (void *)name;
+ key.size = (namelen > 0) ? namelen : (strlen(name) + 1);
+
+ /* Delete the record from the primary DB file */
+ rv = (*ndbp->ndb_pdb->del)(ndbp->ndb_pdb, &key, 0);
+ if (rv) goto err_delpdb;
+
+ /* Successful completion */
+ return 0;
+
+ /* Begin error handlers */
+
+ err_delpdb: /* error deleting record from primary DB */
+ eid = NSDBERR2200;
+ rv = NDBERRNMDEL;
+ (void)nserrGenerate(errp, rv, eid, NSDB_Program,
+ 2,
+ ndbp->ndb_pname, /* primary DB name */
+ (char *)key.data /* primary key */
+ );
+ punt:
+ return rv;
+}
+
+/*
+ * Description (ndbFreeId)
+ *
+ * This function frees an id value associated with a name in the
+ * primary DB file. It is normally called when the named record
+ * is being deleted from the primary DB file. It deletes the
+ * record in the id-to-name DB file that is keyed by the id value,
+ * and updates the id allocation bitmap in the primary DB file.
+ * The caller may specify the name that is associated with the id
+ * value, in which case the id-to-name record will be fetched,
+ * and the name matched, before the record is deleted. Alternatively
+ * the name parameter can be specified as zero, and id-to-name
+ * record will be deleted without a check.
+ *
+ * Arguments:
+ *
+ * errp - error frame list pointer (may be null)
+ * ndb - database handle from ndbOpen()
+ * namelen - length of name (including null terminator)
+ * name - name associated with the id value (optional)
+ * id - id value to be freed
+ *
+ * Returns:
+ *
+ * If successful, the return value is zero. Otherwise a non-zero
+ * error code is returned, and an error frame is generated if the
+ * caller provided an error frame list.
+ */
+
+int ndbFreeId(NSErr_t * errp,
+ void * ndb, int namelen, char * name, unsigned int id)
+{
+ NSDB_t * ndbp = (NSDB_t *)ndb; /* database object pointer */
+ char * recname;
+ DBT key;
+ DBT rec;
+ uint32 idval;
+ int reclen;
+ int mmsk;
+ unsigned char * idmap = 0;
+ int i;
+ int eid;
+ int rv;
+
+ /*
+ * We need the primary and the id-to-name DB files open for write
+ * (and implicitly read) access.
+ */
+ if ((ndbp->ndb_flags & (NDBF_WRNAME|NDBF_WRID))
+ != (NDBF_WRNAME|NDBF_WRID)) {
+
+ /* No, (re)open it */
+ rv = ndbReOpen(errp, ndb, (NDBF_WRNAME|NDBF_WRID));
+ if (rv) goto punt;
+ }
+
+ /* Was the name for this id value provided by the caller? */
+ if (name) {
+
+ /* Get length of name if not provided */
+ if (namelen <= 0) namelen = strlen(name) + 1;
+
+ /* Yes, look up the id and check for a match */
+ rv = ndbIdToName(errp, ndb, id, &reclen, &recname);
+ if (rv < 0) goto punt;
+
+ /* Fail if the supplied name doesn't match */
+ if ((namelen != reclen) ||
+ strncmp(recname, name, reclen)) goto err_badid1;
+ }
+
+ /* Caller views the id space as starting at 1, but we start at 0 */
+ id -= 1;
+
+ /* Create the key for the id-to-name record */
+ idval = id;
+#if BYTE_ORDER == LITTLE_ENDIAN
+ M_32_SWAP(idval);
+#endif
+
+ key.data = (void *)&idval;
+ key.size = sizeof(uint32);
+
+ /* Delete the id-to-name record */
+ rv = (*ndbp->ndb_idb->del)(ndbp->ndb_idb, &key, 0);
+ if (rv) goto err_del;
+
+ /* Set the key to the id allocation bitmap record name */
+ key.data = (void *)NDB_IDMAP;
+ key.size = strlen(NDB_IDMAP) + 1;
+
+ rec.data = 0;
+ rec.size = 0;
+
+ /* Retrieve the record by its key */
+ rv = (*ndbp->ndb_pdb->get)(ndbp->ndb_pdb, &key, &rec, 0);
+ if (rv) goto err_mdget;
+
+ /* Make sure the id is in the range of the bitmap */
+ i = (rec.size << 3) - id - 1;
+ if (i < 0) goto err_badid2;
+
+ /*
+ * See comment in ndbAllocId() about updating ?idmap. Bottom line
+ * is: we have to copy the record before updating it.
+ */
+
+ idmap = (unsigned char *)MALLOC(rec.size);
+ if (idmap == 0) goto err_nomem;
+
+ memcpy((void *)idmap, rec.data, rec.size);
+
+ /* Calculate the index of the byte with this id's bit */
+ i >>= 3;
+
+ /* Calculate the bitmask for the bitmap byte */
+ mmsk = 1 << (id & 7);
+
+ /* Set the bit in the bitmap */
+ idmap[i] |= mmsk;
+
+ /* Write the bitmap back out */
+
+ rec.data = (void *)idmap;
+
+ rv = (*ndbp->ndb_pdb->put)(ndbp->ndb_pdb, &key, &rec, 0);
+ if (rv) goto err_mdput;
+
+ punt:
+
+ if (idmap) {
+ FREE(idmap);
+ }
+
+ return rv;
+
+ err_badid1:
+ /* Name associated with id doesn't match supplied name */
+ eid = NSDBERR2300;
+ rv = NDBERRBADID;
+ goto err_id;
+
+ err_del:
+ /* Error deleting id-to-name record */
+ eid = NSDBERR2320;
+ rv = NDBERRIDDEL;
+ goto err_dbio;
+
+ err_mdget:
+ /* Error reading id bitmap from primary DB file */
+ eid = NSDBERR2340;
+ rv = NDBERRMDGET;
+ goto err_dbio;
+
+ err_badid2:
+ eid = NSDBERR2360;
+ rv = NDBERRBADID;
+ err_id:
+ {
+ char idbuf[16];
+
+ util_sprintf(idbuf, "%d", id);
+ nserrGenerate(errp, rv, eid, NSDB_Program, 2, ndbp->ndb_pname, idbuf);
+ }
+ goto punt;
+
+ err_nomem:
+ eid = NSDBERR2380;
+ rv = NDBERRNOMEM;
+ nserrGenerate(errp, rv, eid, NSDB_Program, 0);
+ goto punt;
+
+ err_mdput:
+ eid = NSDBERR2400;
+ rv = NDBERRMDPUT;
+ goto err_dbio;
+
+ err_dbio:
+ nserrGenerate(errp, rv, eid, NSDB_Program,
+ 2, ndbp->ndb_pname, system_errmsg());
+ goto punt;
+}
+
+/*
+ * Description (ndbRenameId)
+ *
+ * This function changes the name associated with a specified id
+ * int the id-to-name DB file.
+ *
+ * Arguments:
+ *
+ * errp - error frame list pointer (may be null)
+ * ndb - database handle from ndbOpen()
+ * namelen - length of new name string, including
+ * null terminator if any
+ * newname - pointer to the new name string
+ * id - id value to be renamed
+ *
+ * Returns:
+ *
+ * The return value is zero if the operation is successful. An
+ * error is indicated by a non-zero return value, and an error
+ * frame is generated if the caller provided an error frame list.
+ */
+
+int ndbRenameId(NSErr_t * errp,
+ void * ndb, int namelen, char * newname, unsigned int id)
+{
+ NSDB_t * ndbp = (NSDB_t *)ndb; /* database object pointer */
+ DBT key;
+ DBT rec;
+ uint32 idval = id - 1;
+ int eid;
+ int rv;
+
+ /*
+ * Ensure that the name does not start with the metadata
+ * prefix character.
+ */
+ if (!newname || (newname[0] == NDB_MDPREFIX)) goto err_name;
+
+ /*
+ * We need the id-to-name DB file open for write
+ * (and implicitly read) access.
+ */
+ if (!(ndbp->ndb_flags & NDBF_WRID)) {
+
+ /* No, (re)open it */
+ rv = ndbReOpen(errp, ndb, NDBF_WRID);
+ if (rv) goto punt;
+ }
+
+ /* Set up record key */
+#if BYTE_ORDER == LITTLE_ENDIAN
+ M_32_SWAP(idval);
+#endif
+ key.data = (void *)&idval;
+ key.size = sizeof(uint32);
+
+ rec.data = 0;
+ rec.size = 0;
+
+ /* Retrieve the record by its key */
+ rv = (*ndbp->ndb_idb->get)(ndbp->ndb_idb, &key, &rec, 0);
+ if (rv) goto err_idget;
+
+ /* Set up to write the new name */
+ rec.data = (void *)newname;
+ rec.size = (namelen > 0) ? namelen : (strlen(newname) + 1);
+
+ /* Write the id-to-name record */
+ rv = (*ndbp->ndb_idb->put)(ndbp->ndb_idb, &key, &rec, 0);
+ if (rv) goto err_idput;
+
+ punt:
+ return rv;
+
+ err_name:
+ eid = NSDBERR2500;
+ rv = NDBERRNAME;
+ if (newname == 0) newname = "(null)";
+ else if ((namelen > 0) && (namelen != (strlen(newname) + 1))) {
+ newname = "(unprintable)";
+ }
+ (void)nserrGenerate(errp, rv, eid, NSDB_Program,
+ 2,
+ ndbp->ndb_pname, /* primary DB filename */
+ newname /* name string */
+ );
+ goto punt;
+
+ err_idget:
+ /* Error getting id record from id-to-name database */
+ eid = NSDBERR2520;
+ rv = NDBERRGET;
+ goto err_dbio;
+
+ err_idput:
+ /* Error putting id record back to id-to-name database */
+ eid = NSDBERR2540;
+ rv = NDBERRIDPUT;
+ err_dbio:
+ nserrGenerate(errp, rv, eid, NSDB_Program,
+ 2, ndbp->ndb_pname, system_errmsg());
+ goto punt;
+}
+
+/*
+ * Description (ndbStoreName)
+ *
+ * This function stores a record, keyed by a specified name, in the
+ * primary DB file. The record will overwrite any existing record
+ * with the same key, unless NDBF_NEWNAME, is included in the 'flags'
+ * argument. If NDBF_NEWNAME is set, and the record already exists,
+ * it is not overwritten, and an error is returned.
+ *
+ * Arguments:
+ *
+ * errp - error frame list pointer (may be null)
+ * ndb - database handle from ndbOpen()
+ * flags - bit flags:
+ * NDBF_NEWNAME - name is new
+ * namelen - length of name key, including null
+ * terminator if any
+ * name - pointer to name key
+ * reclen - length of the record data
+ * recptr - pointer to the record data
+ *
+ * Returns:
+ *
+ * If successful, the return value is zero. Otherwise a non-zero
+ * error code is returned, and an error frame is generated if the
+ * caller provided an error frame list.
+ */
+
+int ndbStoreName(NSErr_t * errp, void * ndb, int flags,
+ int namelen, char * name, int reclen, char * recptr)
+{
+ NSDB_t * ndbp = (NSDB_t *)ndb; /* database object pointer */
+ DBT key;
+ DBT rec;
+ int eid;
+ int rv;
+
+ /* Is the primary DB open for write access? */
+ if (!(ndbp->ndb_flags & NDBF_WRNAME)) {
+
+ /* No, (re)open it */
+ rv = ndbReOpen(errp, ndb, NDBF_WRNAME);
+ if (rv) goto punt;
+ }
+
+ /* Set up the key and record descriptors */
+ key.data = (void *)name;
+ key.size = (namelen > 0) ? namelen : (strlen(name) + 1);
+
+ rec.data = (void *)recptr;
+ rec.size = reclen;
+
+ /* Write the record to the primary DB file */
+ rv = (*ndbp->ndb_pdb->put)(ndbp->ndb_pdb, &key, &rec,
+ (flags & NDBF_NEWNAME) ? R_NOOVERWRITE : 0);
+ if (rv) goto err_put;
+
+ punt:
+ return rv;
+
+ err_put:
+ eid = NSDBERR2700;
+ rv = NDBERRPUT;
+ nserrGenerate(errp, rv, eid, NSDB_Program,
+ 2, ndbp->ndb_pname, system_errmsg());
+ goto punt;
+}