security/nss/lib/softoken/legacydb/dbmshim.c

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 /*
michael@0 6 * Berkeley DB 1.85 Shim code to handle blobs.
michael@0 7 */
michael@0 8 #include "mcom_db.h"
michael@0 9 #include "secitem.h"
michael@0 10 #include "nssb64.h"
michael@0 11 #include "blapi.h"
michael@0 12 #include "secerr.h"
michael@0 13
michael@0 14 #include "lgdb.h"
michael@0 15
michael@0 16 /*
michael@0 17 * Blob block:
michael@0 18 * Byte 0 CERTDB Version -+ -+
michael@0 19 * Byte 1 certDBEntryTypeBlob | BLOB_HEAD_LEN |
michael@0 20 * Byte 2 flags (always '0'); | |
michael@0 21 * Byte 3 reserved (always '0'); -+ |
michael@0 22 * Byte 4 LSB length | <--BLOB_LENGTH_START | BLOB_BUF_LEN
michael@0 23 * Byte 5 . | |
michael@0 24 * Byte 6 . | BLOB_LENGTH_LEN |
michael@0 25 * Byte 7 MSB length | |
michael@0 26 * Byte 8 blob_filename -+ -+ <-- BLOB_NAME_START |
michael@0 27 * Byte 9 . | BLOB_NAME_LEN |
michael@0 28 * . . | |
michael@0 29 * Byte 37 . -+ -+
michael@0 30 */
michael@0 31 #define DBS_BLOCK_SIZE (16*1024) /* 16 k */
michael@0 32 #define DBS_MAX_ENTRY_SIZE (DBS_BLOCK_SIZE - (2048)) /* 14 k */
michael@0 33 #define DBS_CACHE_SIZE DBS_BLOCK_SIZE*8
michael@0 34 #define ROUNDDIV(x,y) (x+(y-1))/y
michael@0 35 #define BLOB_HEAD_LEN 4
michael@0 36 #define BLOB_LENGTH_START BLOB_HEAD_LEN
michael@0 37 #define BLOB_LENGTH_LEN 4
michael@0 38 #define BLOB_NAME_START BLOB_LENGTH_START+BLOB_LENGTH_LEN
michael@0 39 #define BLOB_NAME_LEN 1+ROUNDDIV(SHA1_LENGTH,3)*4+1
michael@0 40 #define BLOB_BUF_LEN BLOB_HEAD_LEN+BLOB_LENGTH_LEN+BLOB_NAME_LEN
michael@0 41
michael@0 42 /* a Shim data structure. This data structure has a db built into it. */
michael@0 43 typedef struct DBSStr DBS;
michael@0 44
michael@0 45 struct DBSStr {
michael@0 46 DB db;
michael@0 47 char *blobdir;
michael@0 48 int mode;
michael@0 49 PRBool readOnly;
michael@0 50 PRFileMap *dbs_mapfile;
michael@0 51 unsigned char *dbs_addr;
michael@0 52 PRUint32 dbs_len;
michael@0 53 char staticBlobArea[BLOB_BUF_LEN];
michael@0 54 };
michael@0 55
michael@0 56
michael@0 57
michael@0 58 /*
michael@0 59 * return true if the Datablock contains a blobtype
michael@0 60 */
michael@0 61 static PRBool
michael@0 62 dbs_IsBlob(DBT *blobData)
michael@0 63 {
michael@0 64 unsigned char *addr = (unsigned char *)blobData->data;
michael@0 65 if (blobData->size < BLOB_BUF_LEN) {
michael@0 66 return PR_FALSE;
michael@0 67 }
michael@0 68 return addr && ((certDBEntryType) addr[1] == certDBEntryTypeBlob);
michael@0 69 }
michael@0 70
michael@0 71 /*
michael@0 72 * extract the filename in the blob of the real data set.
michael@0 73 * This value is not malloced (does not need to be freed by the caller.
michael@0 74 */
michael@0 75 static const char *
michael@0 76 dbs_getBlobFileName(DBT *blobData)
michael@0 77 {
michael@0 78 char *addr = (char *)blobData->data;
michael@0 79
michael@0 80 return &addr[BLOB_NAME_START];
michael@0 81 }
michael@0 82
michael@0 83 /*
michael@0 84 * extract the size of the actual blob from the blob record
michael@0 85 */
michael@0 86 static PRUint32
michael@0 87 dbs_getBlobSize(DBT *blobData)
michael@0 88 {
michael@0 89 unsigned char *addr = (unsigned char *)blobData->data;
michael@0 90
michael@0 91 return (PRUint32)(addr[BLOB_LENGTH_START+3] << 24) |
michael@0 92 (addr[BLOB_LENGTH_START+2] << 16) |
michael@0 93 (addr[BLOB_LENGTH_START+1] << 8) |
michael@0 94 addr[BLOB_LENGTH_START];
michael@0 95 }
michael@0 96
michael@0 97
michael@0 98 /* We are using base64 data for the filename, but base64 data can include a
michael@0 99 * '/' which is interpreted as a path separator on many platforms. Replace it
michael@0 100 * with an inocuous '-'. We don't need to convert back because we never actual
michael@0 101 * decode the filename.
michael@0 102 */
michael@0 103
michael@0 104 static void
michael@0 105 dbs_replaceSlash(char *cp, int len)
michael@0 106 {
michael@0 107 while (len--) {
michael@0 108 if (*cp == '/') *cp = '-';
michael@0 109 cp++;
michael@0 110 }
michael@0 111 }
michael@0 112
michael@0 113 /*
michael@0 114 * create a blob record from a key, data and return it in blobData.
michael@0 115 * NOTE: The data element is static data (keeping with the dbm model).
michael@0 116 */
michael@0 117 static void
michael@0 118 dbs_mkBlob(DBS *dbsp,const DBT *key, const DBT *data, DBT *blobData)
michael@0 119 {
michael@0 120 unsigned char sha1_data[SHA1_LENGTH];
michael@0 121 char *b = dbsp->staticBlobArea;
michael@0 122 PRUint32 length = data->size;
michael@0 123 SECItem sha1Item;
michael@0 124
michael@0 125 b[0] = CERT_DB_FILE_VERSION; /* certdb version number */
michael@0 126 b[1] = (char) certDBEntryTypeBlob; /* type */
michael@0 127 b[2] = 0; /* flags */
michael@0 128 b[3] = 0; /* reserved */
michael@0 129 b[BLOB_LENGTH_START] = length & 0xff;
michael@0 130 b[BLOB_LENGTH_START+1] = (length >> 8) & 0xff;
michael@0 131 b[BLOB_LENGTH_START+2] = (length >> 16) & 0xff;
michael@0 132 b[BLOB_LENGTH_START+3] = (length >> 24) & 0xff;
michael@0 133 sha1Item.data = sha1_data;
michael@0 134 sha1Item.len = SHA1_LENGTH;
michael@0 135 SHA1_HashBuf(sha1_data,key->data,key->size);
michael@0 136 b[BLOB_NAME_START]='b'; /* Make sure we start with a alpha */
michael@0 137 NSSBase64_EncodeItem(NULL,&b[BLOB_NAME_START+1],BLOB_NAME_LEN-1,&sha1Item);
michael@0 138 b[BLOB_BUF_LEN-1] = 0;
michael@0 139 dbs_replaceSlash(&b[BLOB_NAME_START+1],BLOB_NAME_LEN-1);
michael@0 140 blobData->data = b;
michael@0 141 blobData->size = BLOB_BUF_LEN;
michael@0 142 return;
michael@0 143 }
michael@0 144
michael@0 145
michael@0 146 /*
michael@0 147 * construct a path to the actual blob. The string returned must be
michael@0 148 * freed by the caller with PR_smprintf_free.
michael@0 149 *
michael@0 150 * Note: this file does lots of consistancy checks on the DBT. The
michael@0 151 * routines that call this depend on these checks, so they don't worry
michael@0 152 * about them (success of this routine implies a good blobdata record).
michael@0 153 */
michael@0 154 static char *
michael@0 155 dbs_getBlobFilePath(char *blobdir,DBT *blobData)
michael@0 156 {
michael@0 157 const char *name;
michael@0 158
michael@0 159 if (blobdir == NULL) {
michael@0 160 PR_SetError(SEC_ERROR_BAD_DATABASE,0);
michael@0 161 return NULL;
michael@0 162 }
michael@0 163 if (!dbs_IsBlob(blobData)) {
michael@0 164 PR_SetError(SEC_ERROR_BAD_DATABASE,0);
michael@0 165 return NULL;
michael@0 166 }
michael@0 167 name = dbs_getBlobFileName(blobData);
michael@0 168 if (!name || *name == 0) {
michael@0 169 PR_SetError(SEC_ERROR_BAD_DATABASE,0);
michael@0 170 return NULL;
michael@0 171 }
michael@0 172 return PR_smprintf("%s" PATH_SEPARATOR "%s", blobdir, name);
michael@0 173 }
michael@0 174
michael@0 175 /*
michael@0 176 * Delete a blob file pointed to by the blob record.
michael@0 177 */
michael@0 178 static void
michael@0 179 dbs_removeBlob(DBS *dbsp, DBT *blobData)
michael@0 180 {
michael@0 181 char *file;
michael@0 182
michael@0 183 file = dbs_getBlobFilePath(dbsp->blobdir, blobData);
michael@0 184 if (!file) {
michael@0 185 return;
michael@0 186 }
michael@0 187 PR_Delete(file);
michael@0 188 PR_smprintf_free(file);
michael@0 189 }
michael@0 190
michael@0 191 /*
michael@0 192 * Directory modes are slightly different, the 'x' bit needs to be on to
michael@0 193 * access them. Copy all the read bits to 'x' bits
michael@0 194 */
michael@0 195 static int
michael@0 196 dbs_DirMode(int mode)
michael@0 197 {
michael@0 198 int x_bits = (mode >> 2) & 0111;
michael@0 199 return mode | x_bits;
michael@0 200 }
michael@0 201
michael@0 202 /*
michael@0 203 * write a data blob to it's file. blobdData is the blob record that will be
michael@0 204 * stored in the database. data is the actual data to go out on disk.
michael@0 205 */
michael@0 206 static int
michael@0 207 dbs_writeBlob(DBS *dbsp, int mode, DBT *blobData, const DBT *data)
michael@0 208 {
michael@0 209 char *file = NULL;
michael@0 210 PRFileDesc *filed;
michael@0 211 PRStatus status;
michael@0 212 int len;
michael@0 213 int error = 0;
michael@0 214
michael@0 215 file = dbs_getBlobFilePath(dbsp->blobdir, blobData);
michael@0 216 if (!file) {
michael@0 217 goto loser;
michael@0 218 }
michael@0 219 if (PR_Access(dbsp->blobdir, PR_ACCESS_EXISTS) != PR_SUCCESS) {
michael@0 220 status = PR_MkDir(dbsp->blobdir,dbs_DirMode(mode));
michael@0 221 if (status != PR_SUCCESS) {
michael@0 222 goto loser;
michael@0 223 }
michael@0 224 }
michael@0 225 filed = PR_OpenFile(file,PR_CREATE_FILE|PR_TRUNCATE|PR_WRONLY, mode);
michael@0 226 if (filed == NULL) {
michael@0 227 error = PR_GetError();
michael@0 228 goto loser;
michael@0 229 }
michael@0 230 len = PR_Write(filed,data->data,data->size);
michael@0 231 error = PR_GetError();
michael@0 232 PR_Close(filed);
michael@0 233 if (len < (int)data->size) {
michael@0 234 goto loser;
michael@0 235 }
michael@0 236 PR_smprintf_free(file);
michael@0 237 return 0;
michael@0 238
michael@0 239 loser:
michael@0 240 if (file) {
michael@0 241 PR_Delete(file);
michael@0 242 PR_smprintf_free(file);
michael@0 243 }
michael@0 244 /* don't let close or delete reset the error */
michael@0 245 PR_SetError(error,0);
michael@0 246 return -1;
michael@0 247 }
michael@0 248
michael@0 249
michael@0 250 /*
michael@0 251 * we need to keep a address map in memory between calls to DBM.
michael@0 252 * remember what we have mapped can close it when we get another dbm
michael@0 253 * call.
michael@0 254 *
michael@0 255 * NOTE: Not all platforms support mapped files. This code is designed to
michael@0 256 * detect this at runtime. If map files aren't supported the OS will indicate
michael@0 257 * this by failing the PR_Memmap call. In this case we emulate mapped files
michael@0 258 * by just reading in the file into regular memory. We signal this state by
michael@0 259 * making dbs_mapfile NULL and dbs_addr non-NULL.
michael@0 260 */
michael@0 261
michael@0 262 static void
michael@0 263 dbs_freemap(DBS *dbsp)
michael@0 264 {
michael@0 265 if (dbsp->dbs_mapfile) {
michael@0 266 PR_MemUnmap(dbsp->dbs_addr,dbsp->dbs_len);
michael@0 267 PR_CloseFileMap(dbsp->dbs_mapfile);
michael@0 268 dbsp->dbs_mapfile = NULL;
michael@0 269 dbsp->dbs_addr = NULL;
michael@0 270 dbsp->dbs_len = 0;
michael@0 271 } else if (dbsp->dbs_addr) {
michael@0 272 PORT_Free(dbsp->dbs_addr);
michael@0 273 dbsp->dbs_addr = NULL;
michael@0 274 dbsp->dbs_len = 0;
michael@0 275 }
michael@0 276 return;
michael@0 277 }
michael@0 278
michael@0 279 static void
michael@0 280 dbs_setmap(DBS *dbsp, PRFileMap *mapfile, unsigned char *addr, PRUint32 len)
michael@0 281 {
michael@0 282 dbsp->dbs_mapfile = mapfile;
michael@0 283 dbsp->dbs_addr = addr;
michael@0 284 dbsp->dbs_len = len;
michael@0 285 }
michael@0 286
michael@0 287 /*
michael@0 288 * platforms that cannot map the file need to read it into a temp buffer.
michael@0 289 */
michael@0 290 static unsigned char *
michael@0 291 dbs_EmulateMap(PRFileDesc *filed, int len)
michael@0 292 {
michael@0 293 unsigned char *addr;
michael@0 294 PRInt32 dataRead;
michael@0 295
michael@0 296 addr = PORT_Alloc(len);
michael@0 297 if (addr == NULL) {
michael@0 298 return NULL;
michael@0 299 }
michael@0 300
michael@0 301 dataRead = PR_Read(filed,addr,len);
michael@0 302 if (dataRead != len) {
michael@0 303 PORT_Free(addr);
michael@0 304 if (dataRead > 0) {
michael@0 305 /* PR_Read didn't set an error, we need to */
michael@0 306 PR_SetError(SEC_ERROR_BAD_DATABASE,0);
michael@0 307 }
michael@0 308 return NULL;
michael@0 309 }
michael@0 310
michael@0 311 return addr;
michael@0 312 }
michael@0 313
michael@0 314
michael@0 315 /*
michael@0 316 * pull a database record off the disk
michael@0 317 * data points to the blob record on input and the real record (if we could
michael@0 318 * read it) on output. if there is an error data is not modified.
michael@0 319 */
michael@0 320 static int
michael@0 321 dbs_readBlob(DBS *dbsp, DBT *data)
michael@0 322 {
michael@0 323 char *file = NULL;
michael@0 324 PRFileDesc *filed = NULL;
michael@0 325 PRFileMap *mapfile = NULL;
michael@0 326 unsigned char *addr = NULL;
michael@0 327 int error;
michael@0 328 int len = -1;
michael@0 329
michael@0 330 file = dbs_getBlobFilePath(dbsp->blobdir, data);
michael@0 331 if (!file) {
michael@0 332 goto loser;
michael@0 333 }
michael@0 334 filed = PR_OpenFile(file,PR_RDONLY,0);
michael@0 335 PR_smprintf_free(file); file = NULL;
michael@0 336 if (filed == NULL) {
michael@0 337 goto loser;
michael@0 338 }
michael@0 339
michael@0 340 len = dbs_getBlobSize(data);
michael@0 341 mapfile = PR_CreateFileMap(filed, len, PR_PROT_READONLY);
michael@0 342 if (mapfile == NULL) {
michael@0 343 /* USE PR_GetError instead of PORT_GetError here
michael@0 344 * because we are getting the error from PR_xxx
michael@0 345 * function */
michael@0 346 if (PR_GetError() != PR_NOT_IMPLEMENTED_ERROR) {
michael@0 347 goto loser;
michael@0 348 }
michael@0 349 addr = dbs_EmulateMap(filed, len);
michael@0 350 } else {
michael@0 351 addr = PR_MemMap(mapfile, 0, len);
michael@0 352 }
michael@0 353 if (addr == NULL) {
michael@0 354 goto loser;
michael@0 355 }
michael@0 356 PR_Close(filed);
michael@0 357 dbs_setmap(dbsp,mapfile,addr,len);
michael@0 358
michael@0 359 data->data = addr;
michael@0 360 data->size = len;
michael@0 361 return 0;
michael@0 362
michael@0 363 loser:
michael@0 364 /* preserve the error code */
michael@0 365 error = PR_GetError();
michael@0 366 if (mapfile) {
michael@0 367 PR_CloseFileMap(mapfile);
michael@0 368 }
michael@0 369 if (filed) {
michael@0 370 PR_Close(filed);
michael@0 371 }
michael@0 372 PR_SetError(error,0);
michael@0 373 return -1;
michael@0 374 }
michael@0 375
michael@0 376 /*
michael@0 377 * actual DBM shims
michael@0 378 */
michael@0 379 static int
michael@0 380 dbs_get(const DB *dbs, const DBT *key, DBT *data, unsigned int flags)
michael@0 381 {
michael@0 382 int ret;
michael@0 383 DBS *dbsp = (DBS *)dbs;
michael@0 384 DB *db = (DB *)dbs->internal;
michael@0 385
michael@0 386
michael@0 387 dbs_freemap(dbsp);
michael@0 388
michael@0 389 ret = (* db->get)(db, key, data, flags);
michael@0 390 if ((ret == 0) && dbs_IsBlob(data)) {
michael@0 391 ret = dbs_readBlob(dbsp,data);
michael@0 392 }
michael@0 393
michael@0 394 return(ret);
michael@0 395 }
michael@0 396
michael@0 397 static int
michael@0 398 dbs_put(const DB *dbs, DBT *key, const DBT *data, unsigned int flags)
michael@0 399 {
michael@0 400 DBT blob;
michael@0 401 int ret = 0;
michael@0 402 DBS *dbsp = (DBS *)dbs;
michael@0 403 DB *db = (DB *)dbs->internal;
michael@0 404
michael@0 405 dbs_freemap(dbsp);
michael@0 406
michael@0 407 /* If the db is readonly, just pass the data down to rdb and let it fail */
michael@0 408 if (!dbsp->readOnly) {
michael@0 409 DBT oldData;
michael@0 410 int ret1;
michael@0 411
michael@0 412 /* make sure the current record is deleted if it's a blob */
michael@0 413 ret1 = (*db->get)(db,key,&oldData,0);
michael@0 414 if ((ret1 == 0) && flags == R_NOOVERWRITE) {
michael@0 415 /* let DBM return the error to maintain consistancy */
michael@0 416 return (* db->put)(db, key, data, flags);
michael@0 417 }
michael@0 418 if ((ret1 == 0) && dbs_IsBlob(&oldData)) {
michael@0 419 dbs_removeBlob(dbsp, &oldData);
michael@0 420 }
michael@0 421
michael@0 422 if (data->size > DBS_MAX_ENTRY_SIZE) {
michael@0 423 dbs_mkBlob(dbsp,key,data,&blob);
michael@0 424 ret = dbs_writeBlob(dbsp, dbsp->mode, &blob, data);
michael@0 425 data = &blob;
michael@0 426 }
michael@0 427 }
michael@0 428
michael@0 429 if (ret == 0) {
michael@0 430 ret = (* db->put)(db, key, data, flags);
michael@0 431 }
michael@0 432 return(ret);
michael@0 433 }
michael@0 434
michael@0 435 static int
michael@0 436 dbs_sync(const DB *dbs, unsigned int flags)
michael@0 437 {
michael@0 438 DB *db = (DB *)dbs->internal;
michael@0 439 DBS *dbsp = (DBS *)dbs;
michael@0 440
michael@0 441 dbs_freemap(dbsp);
michael@0 442
michael@0 443 return (* db->sync)(db, flags);
michael@0 444 }
michael@0 445
michael@0 446 static int
michael@0 447 dbs_del(const DB *dbs, const DBT *key, unsigned int flags)
michael@0 448 {
michael@0 449 int ret;
michael@0 450 DBS *dbsp = (DBS *)dbs;
michael@0 451 DB *db = (DB *)dbs->internal;
michael@0 452
michael@0 453 dbs_freemap(dbsp);
michael@0 454
michael@0 455 if (!dbsp->readOnly) {
michael@0 456 DBT oldData;
michael@0 457 ret = (*db->get)(db,key,&oldData,0);
michael@0 458 if ((ret == 0) && dbs_IsBlob(&oldData)) {
michael@0 459 dbs_removeBlob(dbsp,&oldData);
michael@0 460 }
michael@0 461 }
michael@0 462
michael@0 463 return (* db->del)(db, key, flags);
michael@0 464 }
michael@0 465
michael@0 466 static int
michael@0 467 dbs_seq(const DB *dbs, DBT *key, DBT *data, unsigned int flags)
michael@0 468 {
michael@0 469 int ret;
michael@0 470 DBS *dbsp = (DBS *)dbs;
michael@0 471 DB *db = (DB *)dbs->internal;
michael@0 472
michael@0 473 dbs_freemap(dbsp);
michael@0 474
michael@0 475 ret = (* db->seq)(db, key, data, flags);
michael@0 476 if ((ret == 0) && dbs_IsBlob(data)) {
michael@0 477 /* don't return a blob read as an error so traversals keep going */
michael@0 478 (void) dbs_readBlob(dbsp,data);
michael@0 479 }
michael@0 480
michael@0 481 return(ret);
michael@0 482 }
michael@0 483
michael@0 484 static int
michael@0 485 dbs_close(DB *dbs)
michael@0 486 {
michael@0 487 DBS *dbsp = (DBS *)dbs;
michael@0 488 DB *db = (DB *)dbs->internal;
michael@0 489 int ret;
michael@0 490
michael@0 491 dbs_freemap(dbsp);
michael@0 492 ret = (* db->close)(db);
michael@0 493 PORT_Free(dbsp->blobdir);
michael@0 494 PORT_Free(dbsp);
michael@0 495 return ret;
michael@0 496 }
michael@0 497
michael@0 498 static int
michael@0 499 dbs_fd(const DB *dbs)
michael@0 500 {
michael@0 501 DB *db = (DB *)dbs->internal;
michael@0 502
michael@0 503 return (* db->fd)(db);
michael@0 504 }
michael@0 505
michael@0 506 /*
michael@0 507 * the naming convention we use is
michael@0 508 * change the .xxx into .dir. (for nss it's always .db);
michael@0 509 * if no .extension exists or is equal to .dir, add a .dir
michael@0 510 * the returned data must be freed.
michael@0 511 */
michael@0 512 #define DIRSUFFIX ".dir"
michael@0 513 static char *
michael@0 514 dbs_mkBlobDirName(const char *dbname)
michael@0 515 {
michael@0 516 int dbname_len = PORT_Strlen(dbname);
michael@0 517 int dbname_end = dbname_len;
michael@0 518 const char *cp;
michael@0 519 char *blobDir = NULL;
michael@0 520
michael@0 521 /* scan back from the end looking for either a directory separator, a '.',
michael@0 522 * or the end of the string. NOTE: Windows should check for both separators
michael@0 523 * here. For now this is safe because we know NSS always uses a '.'
michael@0 524 */
michael@0 525 for (cp = &dbname[dbname_len];
michael@0 526 (cp > dbname) && (*cp != '.') && (*cp != *PATH_SEPARATOR) ;
michael@0 527 cp--)
michael@0 528 /* Empty */ ;
michael@0 529 if (*cp == '.') {
michael@0 530 dbname_end = cp - dbname;
michael@0 531 if (PORT_Strcmp(cp,DIRSUFFIX) == 0) {
michael@0 532 dbname_end = dbname_len;
michael@0 533 }
michael@0 534 }
michael@0 535 blobDir = PORT_ZAlloc(dbname_end+sizeof(DIRSUFFIX));
michael@0 536 if (blobDir == NULL) {
michael@0 537 return NULL;
michael@0 538 }
michael@0 539 PORT_Memcpy(blobDir,dbname,dbname_end);
michael@0 540 PORT_Memcpy(&blobDir[dbname_end],DIRSUFFIX,sizeof(DIRSUFFIX));
michael@0 541 return blobDir;
michael@0 542 }
michael@0 543
michael@0 544 #define DBM_DEFAULT 0
michael@0 545 static const HASHINFO dbs_hashInfo = {
michael@0 546 DBS_BLOCK_SIZE, /* bucket size, must be greater than = to
michael@0 547 * or maximum entry size (+ header)
michael@0 548 * we allow before blobing */
michael@0 549 DBM_DEFAULT, /* Fill Factor */
michael@0 550 DBM_DEFAULT, /* number of elements */
michael@0 551 DBS_CACHE_SIZE, /* cache size */
michael@0 552 DBM_DEFAULT, /* hash function */
michael@0 553 DBM_DEFAULT, /* byte order */
michael@0 554 };
michael@0 555
michael@0 556 /*
michael@0 557 * the open function. NOTE: this is the only exposed function in this file.
michael@0 558 * everything else is called through the function table pointer.
michael@0 559 */
michael@0 560 DB *
michael@0 561 dbsopen(const char *dbname, int flags, int mode, DBTYPE type,
michael@0 562 const void *userData)
michael@0 563 {
michael@0 564 DB *db = NULL,*dbs = NULL;
michael@0 565 DBS *dbsp = NULL;
michael@0 566
michael@0 567 /* NOTE: we are overriding userData with dbs_hashInfo. since all known
michael@0 568 * callers pass 0, this is ok, otherwise we should merge the two */
michael@0 569
michael@0 570 dbsp = (DBS *)PORT_ZAlloc(sizeof(DBS));
michael@0 571 if (!dbsp) {
michael@0 572 return NULL;
michael@0 573 }
michael@0 574 dbs = &dbsp->db;
michael@0 575
michael@0 576 dbsp->blobdir=dbs_mkBlobDirName(dbname);
michael@0 577 if (dbsp->blobdir == NULL) {
michael@0 578 goto loser;
michael@0 579 }
michael@0 580 dbsp->mode = mode;
michael@0 581 dbsp->readOnly = (PRBool)(flags == NO_RDONLY);
michael@0 582 dbsp->dbs_mapfile = NULL;
michael@0 583 dbsp->dbs_addr = NULL;
michael@0 584 dbsp->dbs_len = 0;
michael@0 585
michael@0 586 /* the real dbm call */
michael@0 587 db = dbopen(dbname, flags, mode, type, &dbs_hashInfo);
michael@0 588 if (db == NULL) {
michael@0 589 goto loser;
michael@0 590 }
michael@0 591 dbs->internal = (void *) db;
michael@0 592 dbs->type = type;
michael@0 593 dbs->close = dbs_close;
michael@0 594 dbs->get = dbs_get;
michael@0 595 dbs->del = dbs_del;
michael@0 596 dbs->put = dbs_put;
michael@0 597 dbs->seq = dbs_seq;
michael@0 598 dbs->sync = dbs_sync;
michael@0 599 dbs->fd = dbs_fd;
michael@0 600
michael@0 601 return dbs;
michael@0 602 loser:
michael@0 603 if (db) {
michael@0 604 (*db->close)(db);
michael@0 605 }
michael@0 606 if (dbsp) {
michael@0 607 if (dbsp->blobdir) {
michael@0 608 PORT_Free(dbsp->blobdir);
michael@0 609 }
michael@0 610 PORT_Free(dbsp);
michael@0 611 }
michael@0 612 return NULL;
michael@0 613 }

mercurial