1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
14 * The Original Code is the Netscape security libraries.
16 * The Initial Developer of the Original Code is
17 * Netscape Communications Corporation.
18 * Portions created by the Initial Developer are Copyright (C) 1994-2000
19 * the Initial Developer. All Rights Reserved.
23 * Alternatively, the contents of this file may be used under the terms of
24 * either the GNU General Public License Version 2 or later (the "GPL"), or
25 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26 * in which case the provisions of the GPL or the LGPL are applicable instead
27 * of those above. If you wish to allow use of your version of this file only
28 * under the terms of either the GPL or the LGPL, and not to allow others to
29 * use your version of this file under the terms of the MPL, indicate your
30 * decision by deleting the provisions above and replace them with the notice
31 * and other provisions required by the GPL or the LGPL. If you do not delete
32 * the provisions above, a recipient may use your version of this file under
33 * the terms of any one of the MPL, the GPL or the LGPL.
35 * ***** END LICENSE BLOCK ***** */
38 * Berkeley DB 1.85 Shim code to handle blobs.
40 * $Id: dbmshim.c,v 1.2 2007/06/13 00:24:57 rrelyea%redhat.com Exp $
52 * Byte 0 CERTDB Version -+ -+
53 * Byte 1 certDBEntryTypeBlob | BLOB_HEAD_LEN |
54 * Byte 2 flags (always '0'); | |
55 * Byte 3 reserved (always '0'); -+ |
56 * Byte 4 LSB length | <--BLOB_LENGTH_START | BLOB_BUF_LEN
58 * Byte 6 . | BLOB_LENGTH_LEN |
59 * Byte 7 MSB length | |
60 * Byte 8 blob_filename -+ -+ <-- BLOB_NAME_START |
61 * Byte 9 . | BLOB_NAME_LEN |
65 #define DBS_BLOCK_SIZE (16*1024) /* 16 k */
66 #define DBS_MAX_ENTRY_SIZE (DBS_BLOCK_SIZE - (2048)) /* 14 k */
67 #define DBS_CACHE_SIZE DBS_BLOCK_SIZE*8
68 #define ROUNDDIV(x,y) (x+(y-1))/y
69 #define BLOB_HEAD_LEN 4
70 #define BLOB_LENGTH_START BLOB_HEAD_LEN
71 #define BLOB_LENGTH_LEN 4
72 #define BLOB_NAME_START BLOB_LENGTH_START+BLOB_LENGTH_LEN
73 #define BLOB_NAME_LEN 1+ROUNDDIV(SHA1_LENGTH,3)*4+1
74 #define BLOB_BUF_LEN BLOB_HEAD_LEN+BLOB_LENGTH_LEN+BLOB_NAME_LEN
76 /* a Shim data structure. This data structure has a db built into it. */
77 typedef struct DBSStr DBS;
84 PRFileMap *dbs_mapfile;
85 unsigned char *dbs_addr;
87 char staticBlobArea[BLOB_BUF_LEN];
93 * return true if the Datablock contains a blobtype
96 dbs_IsBlob(DBT *blobData)
98 unsigned char *addr = (unsigned char *)blobData->data;
99 if (blobData->size < BLOB_BUF_LEN) {
102 return addr && ((certDBEntryType) addr[1] == certDBEntryTypeBlob);
106 * extract the filename in the blob of the real data set.
107 * This value is not malloced (does not need to be freed by the caller.
110 dbs_getBlobFileName(DBT *blobData)
112 char *addr = (char *)blobData->data;
114 return &addr[BLOB_NAME_START];
118 * extract the size of the actual blob from the blob record
121 dbs_getBlobSize(DBT *blobData)
123 unsigned char *addr = (unsigned char *)blobData->data;
125 return (PRUint32)(addr[BLOB_LENGTH_START+3] << 24) |
126 (addr[BLOB_LENGTH_START+2] << 16) |
127 (addr[BLOB_LENGTH_START+1] << 8) |
128 addr[BLOB_LENGTH_START];
132 /* We are using base64 data for the filename, but base64 data can include a
133 * '/' which is interpreted as a path separator on many platforms. Replace it
134 * with an inocuous '-'. We don't need to convert back because we never actual
135 * decode the filename.
139 dbs_replaceSlash(char *cp, int len)
142 if (*cp == '/') *cp = '-';
148 * create a blob record from a key, data and return it in blobData.
149 * NOTE: The data element is static data (keeping with the dbm model).
152 dbs_mkBlob(DBS *dbsp,const DBT *key, const DBT *data, DBT *blobData)
154 unsigned char sha1_data[SHA1_LENGTH];
155 char *b = dbsp->staticBlobArea;
156 PRUint32 length = data->size;
159 b[0] = CERT_DB_FILE_VERSION; /* certdb version number */
160 b[1] = (char) certDBEntryTypeBlob; /* type */
161 b[2] = 0; /* flags */
162 b[3] = 0; /* reserved */
163 b[BLOB_LENGTH_START] = length & 0xff;
164 b[BLOB_LENGTH_START+1] = (length >> 8) & 0xff;
165 b[BLOB_LENGTH_START+2] = (length >> 16) & 0xff;
166 b[BLOB_LENGTH_START+3] = (length >> 24) & 0xff;
167 sha1Item.data = sha1_data;
168 sha1Item.len = SHA1_LENGTH;
169 SHA1_HashBuf(sha1_data,key->data,key->size);
170 b[BLOB_NAME_START]='b'; /* Make sure we start with a alpha */
171 NSSBase64_EncodeItem(NULL,&b[BLOB_NAME_START+1],BLOB_NAME_LEN-1,&sha1Item);
172 b[BLOB_BUF_LEN-1] = 0;
173 dbs_replaceSlash(&b[BLOB_NAME_START+1],BLOB_NAME_LEN-1);
175 blobData->size = BLOB_BUF_LEN;
181 * construct a path to the actual blob. The string returned must be
182 * freed by the caller with PR_smprintf_free.
184 * Note: this file does lots of consistancy checks on the DBT. The
185 * routines that call this depend on these checks, so they don't worry
186 * about them (success of this routine implies a good blobdata record).
189 dbs_getBlobFilePath(char *blobdir,DBT *blobData)
193 if (blobdir == NULL) {
194 PR_SetError(SEC_ERROR_BAD_DATABASE,0);
197 if (!dbs_IsBlob(blobData)) {
198 PR_SetError(SEC_ERROR_BAD_DATABASE,0);
201 name = dbs_getBlobFileName(blobData);
202 if (!name || *name == 0) {
203 PR_SetError(SEC_ERROR_BAD_DATABASE,0);
206 return PR_smprintf("%s" PATH_SEPARATOR "%s", blobdir, name);
210 * Delete a blob file pointed to by the blob record.
213 dbs_removeBlob(DBS *dbsp, DBT *blobData)
217 file = dbs_getBlobFilePath(dbsp->blobdir, blobData);
222 PR_smprintf_free(file);
226 * Directory modes are slightly different, the 'x' bit needs to be on to
227 * access them. Copy all the read bits to 'x' bits
230 dbs_DirMode(int mode)
232 int x_bits = (mode >> 2) & 0111;
233 return mode | x_bits;
237 * write a data blob to it's file. blobdData is the blob record that will be
238 * stored in the database. data is the actual data to go out on disk.
241 dbs_writeBlob(DBS *dbsp, int mode, DBT *blobData, const DBT *data)
249 file = dbs_getBlobFilePath(dbsp->blobdir, blobData);
253 if (PR_Access(dbsp->blobdir, PR_ACCESS_EXISTS) != PR_SUCCESS) {
254 status = PR_MkDir(dbsp->blobdir,dbs_DirMode(mode));
255 if (status != PR_SUCCESS) {
259 filed = PR_OpenFile(file,PR_CREATE_FILE|PR_TRUNCATE|PR_WRONLY, mode);
261 error = PR_GetError();
264 len = PR_Write(filed,data->data,data->size);
265 error = PR_GetError();
267 if (len < (int)data->size) {
270 PR_smprintf_free(file);
276 PR_smprintf_free(file);
278 /* don't let close or delete reset the error */
279 PR_SetError(error,0);
285 * we need to keep a address map in memory between calls to DBM.
286 * remember what we have mapped can close it when we get another dbm
289 * NOTE: Not all platforms support mapped files. This code is designed to
290 * detect this at runtime. If map files aren't supported the OS will indicate
291 * this by failing the PR_Memmap call. In this case we emulate mapped files
292 * by just reading in the file into regular memory. We signal this state by
293 * making dbs_mapfile NULL and dbs_addr non-NULL.
297 dbs_freemap(DBS *dbsp)
299 if (dbsp->dbs_mapfile) {
300 PR_MemUnmap(dbsp->dbs_addr,dbsp->dbs_len);
301 PR_CloseFileMap(dbsp->dbs_mapfile);
302 dbsp->dbs_mapfile = NULL;
303 dbsp->dbs_addr = NULL;
305 } else if (dbsp->dbs_addr) {
306 PORT_Free(dbsp->dbs_addr);
307 dbsp->dbs_addr = NULL;
314 dbs_setmap(DBS *dbsp, PRFileMap *mapfile, unsigned char *addr, PRUint32 len)
316 dbsp->dbs_mapfile = mapfile;
317 dbsp->dbs_addr = addr;
322 * platforms that cannot map the file need to read it into a temp buffer.
324 static unsigned char *
325 dbs_EmulateMap(PRFileDesc *filed, int len)
330 addr = PORT_Alloc(len);
335 dataRead = PR_Read(filed,addr,len);
336 if (dataRead != len) {
339 /* PR_Read didn't set an error, we need to */
340 PR_SetError(SEC_ERROR_BAD_DATABASE,0);
350 * pull a database record off the disk
351 * data points to the blob record on input and the real record (if we could
352 * read it) on output. if there is an error data is not modified.
355 dbs_readBlob(DBS *dbsp, DBT *data)
358 PRFileDesc *filed = NULL;
359 PRFileMap *mapfile = NULL;
360 unsigned char *addr = NULL;
364 file = dbs_getBlobFilePath(dbsp->blobdir, data);
368 filed = PR_OpenFile(file,PR_RDONLY,0);
369 PR_smprintf_free(file); file = NULL;
374 len = dbs_getBlobSize(data);
375 mapfile = PR_CreateFileMap(filed, len, PR_PROT_READONLY);
376 if (mapfile == NULL) {
377 /* USE PR_GetError instead of PORT_GetError here
378 * because we are getting the error from PR_xxx
380 if (PR_GetError() != PR_NOT_IMPLEMENTED_ERROR) {
383 addr = dbs_EmulateMap(filed, len);
385 addr = PR_MemMap(mapfile, 0, len);
391 dbs_setmap(dbsp,mapfile,addr,len);
398 /* preserve the error code */
399 error = PR_GetError();
401 PR_CloseFileMap(mapfile);
406 PR_SetError(error,0);
414 dbs_get(const DB *dbs, const DBT *key, DBT *data, unsigned int flags)
417 DBS *dbsp = (DBS *)dbs;
418 DB *db = (DB *)dbs->internal;
423 ret = (* db->get)(db, key, data, flags);
424 if ((ret == 0) && dbs_IsBlob(data)) {
425 ret = dbs_readBlob(dbsp,data);
432 dbs_put(const DB *dbs, DBT *key, const DBT *data, unsigned int flags)
436 DBS *dbsp = (DBS *)dbs;
437 DB *db = (DB *)dbs->internal;
441 /* If the db is readonly, just pass the data down to rdb and let it fail */
442 if (!dbsp->readOnly) {
446 /* make sure the current record is deleted if it's a blob */
447 ret1 = (*db->get)(db,key,&oldData,0);
448 if ((ret1 == 0) && flags == R_NOOVERWRITE) {
449 /* let DBM return the error to maintain consistancy */
450 return (* db->put)(db, key, data, flags);
452 if ((ret1 == 0) && dbs_IsBlob(&oldData)) {
453 dbs_removeBlob(dbsp, &oldData);
456 if (data->size > DBS_MAX_ENTRY_SIZE) {
457 dbs_mkBlob(dbsp,key,data,&blob);
458 ret = dbs_writeBlob(dbsp, dbsp->mode, &blob, data);
464 ret = (* db->put)(db, key, data, flags);
470 dbs_sync(const DB *dbs, unsigned int flags)
472 DB *db = (DB *)dbs->internal;
473 DBS *dbsp = (DBS *)dbs;
477 return (* db->sync)(db, flags);
481 dbs_del(const DB *dbs, const DBT *key, unsigned int flags)
484 DBS *dbsp = (DBS *)dbs;
485 DB *db = (DB *)dbs->internal;
489 if (!dbsp->readOnly) {
491 ret = (*db->get)(db,key,&oldData,0);
492 if ((ret == 0) && dbs_IsBlob(&oldData)) {
493 dbs_removeBlob(dbsp,&oldData);
497 return (* db->del)(db, key, flags);
501 dbs_seq(const DB *dbs, DBT *key, DBT *data, unsigned int flags)
504 DBS *dbsp = (DBS *)dbs;
505 DB *db = (DB *)dbs->internal;
509 ret = (* db->seq)(db, key, data, flags);
510 if ((ret == 0) && dbs_IsBlob(data)) {
511 /* don't return a blob read as an error so traversals keep going */
512 (void) dbs_readBlob(dbsp,data);
521 DBS *dbsp = (DBS *)dbs;
522 DB *db = (DB *)dbs->internal;
526 ret = (* db->close)(db);
527 PORT_Free(dbsp->blobdir);
533 dbs_fd(const DB *dbs)
535 DB *db = (DB *)dbs->internal;
537 return (* db->fd)(db);
541 * the naming convention we use is
542 * change the .xxx into .dir. (for nss it's always .db);
543 * if no .extension exists or is equal to .dir, add a .dir
544 * the returned data must be freed.
546 #define DIRSUFFIX ".dir"
548 dbs_mkBlobDirName(const char *dbname)
550 int dbname_len = PORT_Strlen(dbname);
551 int dbname_end = dbname_len;
553 char *blobDir = NULL;
555 /* scan back from the end looking for either a directory separator, a '.',
556 * or the end of the string. NOTE: Windows should check for both separators
557 * here. For now this is safe because we know NSS always uses a '.'
559 for (cp = &dbname[dbname_len];
560 (cp > dbname) && (*cp != '.') && (*cp != *PATH_SEPARATOR) ;
564 dbname_end = cp - dbname;
565 if (PORT_Strcmp(cp,DIRSUFFIX) == 0) {
566 dbname_end = dbname_len;
569 blobDir = PORT_ZAlloc(dbname_end+sizeof(DIRSUFFIX));
570 if (blobDir == NULL) {
573 PORT_Memcpy(blobDir,dbname,dbname_end);
574 PORT_Memcpy(&blobDir[dbname_end],DIRSUFFIX,sizeof(DIRSUFFIX));
578 #define DBM_DEFAULT 0
579 static const HASHINFO dbs_hashInfo = {
580 DBS_BLOCK_SIZE, /* bucket size, must be greater than = to
581 * or maximum entry size (+ header)
582 * we allow before blobing */
583 DBM_DEFAULT, /* Fill Factor */
584 DBM_DEFAULT, /* number of elements */
585 DBS_CACHE_SIZE, /* cache size */
586 DBM_DEFAULT, /* hash function */
587 DBM_DEFAULT, /* byte order */
591 * the open function. NOTE: this is the only exposed function in this file.
592 * everything else is called through the function table pointer.
595 dbsopen(const char *dbname, int flags, int mode, DBTYPE type,
596 const void *userData)
598 DB *db = NULL,*dbs = NULL;
601 /* NOTE: we are overriding userData with dbs_hashInfo. since all known
602 * callers pass 0, this is ok, otherwise we should merge the two */
604 dbsp = (DBS *)PORT_ZAlloc(sizeof(DBS));
610 dbsp->blobdir=dbs_mkBlobDirName(dbname);
611 if (dbsp->blobdir == NULL) {
615 dbsp->readOnly = (PRBool)(flags == NO_RDONLY);
616 dbsp->dbs_mapfile = NULL;
617 dbsp->dbs_addr = NULL;
620 /* the real dbm call */
621 db = dbopen(dbname, flags, mode, type, &dbs_hashInfo);
625 dbs->internal = (void *) db;
627 dbs->close = dbs_close;
632 dbs->sync = dbs_sync;
642 PORT_Free(dbsp->blobdir);