LCOV - code coverage report
Current view: top level - dirmngr - cdblib.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 447 0.0 %
Date: 2015-11-05 17:10:59 Functions: 0 22 0.0 %

          Line data    Source code
       1             : /* cdblib.c - all CDB library functions.
       2             :  *
       3             :  * This file is a part of tinycdb package by Michael Tokarev, mjt@corpit.ru.
       4             :  * Public domain.
       5             :  *
       6             :  * Taken from tinycdb-0.73 and merged into one file for easier
       7             :  * inclusion into Dirmngr.  By Werner Koch <wk@gnupg.org> 2003-12-12.
       8             :  */
       9             : 
      10             : /* A cdb database is a single file used to map 'keys' to 'values',
      11             :    having records of (key,value) pairs.  File consists of 3 parts: toc
      12             :    (table of contents), data and index (hash tables).
      13             : 
      14             :    Toc has fixed length of 2048 bytes, containing 256 pointers to hash
      15             :    tables inside index sections.  Every pointer consists of position
      16             :    of a hash table in bytes from the beginning of a file, and a size
      17             :    of a hash table in entries, both are 4-bytes (32 bits) unsigned
      18             :    integers in little-endian form.  Hash table length may have zero
      19             :    length, meaning that corresponding hash table is empty.
      20             : 
      21             :    Right after toc section, data section follows without any
      22             :    alingment.  It consists of series of records, each is a key length,
      23             :    value (data) length, key and value.  Again, key and value length
      24             :    are 4-byte unsigned integers.  Each next record follows previous
      25             :    without any special alignment.
      26             : 
      27             :    After data section, index (hash tables) section follows.  It should
      28             :    be looked to in conjunction with toc section, where each of max 256
      29             :    hash tables are defined.  Index section consists of series of hash
      30             :    tables, with starting position and length defined in toc section.
      31             :    Every hash table is a sequence of records each holds two numbers:
      32             :    key's hash value and record position inside data section (bytes
      33             :    from the beginning of a file to first byte of key length starting
      34             :    data record).  If record position is zero, then this is an empty
      35             :    hash table slot, pointed to nowhere.
      36             : 
      37             :    CDB hash function is
      38             :      hv = ((hv << 5) + hv) ^ c
      39             :    for every single c byte of a key, starting with hv = 5381.
      40             : 
      41             :    Toc section indexed by (hv % 256), i.e. hash value modulo 256
      42             :    (number of entries in toc section).
      43             : 
      44             :    In order to find a record, one should: first, compute the hash
      45             :    value (hv) of a key.  Second, look to hash table number hv modulo
      46             :    256.  If it is empty, then there is no such key exists.  If it is
      47             :    not empty, then third, loop by slots inside that hash table,
      48             :    starting from slot with number hv divided by 256 modulo length of
      49             :    that table, or ((hv / 256) % htlen), searching for this hv in hash
      50             :    table.  Stop search on empty slot (if record position is zero) or
      51             :    when all slots was probed (note cyclic search, jumping from end to
      52             :    beginning of a table).  When hash value in question is found in
      53             :    hash table, look to key of corresponding record, comparing it with
      54             :    key in question.  If them of the same length and equals to each
      55             :    other, then record is found, overwise, repeat with next hash table
      56             :    slot.  Note that there may be several records with the same key.
      57             : */
      58             : 
      59             : #ifdef HAVE_CONFIG_H
      60             : #include <config.h>
      61             : #endif
      62             : #include <stdlib.h>
      63             : #include <errno.h>
      64             : #include <string.h>
      65             : #include <unistd.h>
      66             : #include <sys/types.h>
      67             : #ifdef _WIN32
      68             : # include <windows.h>
      69             : #else
      70             : # include <sys/mman.h>
      71             : # ifndef MAP_FAILED
      72             : #  define MAP_FAILED ((void*)-1)
      73             : # endif
      74             : #endif
      75             : #include <sys/stat.h>
      76             : 
      77             : #include "dirmngr-err.h"
      78             : #include "cdb.h"
      79             : 
      80             : #ifndef EPROTO
      81             : # define EPROTO EINVAL
      82             : #endif
      83             : #ifndef SEEK_SET
      84             : # define SEEK_SET 0
      85             : #endif
      86             : 
      87             : 
      88             : struct cdb_rec {
      89             :   cdbi_t hval;
      90             :   cdbi_t rpos;
      91             : };
      92             : 
      93             : struct cdb_rl {
      94             :   struct cdb_rl *next;
      95             :   cdbi_t cnt;
      96             :   struct cdb_rec rec[254];
      97             : };
      98             : 
      99             : static int make_find(struct cdb_make *cdbmp,
     100             :                    const void *key, cdbi_t klen, cdbi_t hval,
     101             :                    struct cdb_rl **rlp);
     102             : static int make_write(struct cdb_make *cdbmp,
     103             :                     const char *ptr, cdbi_t len);
     104             : 
     105             : 
     106             : 
     107             : /* Initializes structure given by CDBP pointer and associates it with
     108             :    the open file descriptor FD.  Allocate memory for the structure
     109             :    itself if needed and file open operation should be done by
     110             :    application.  File FD should be opened at least read-only, and
     111             :    should be seekable.  Routine returns 0 on success or negative value
     112             :    on error. */
     113             : int
     114           0 : cdb_init(struct cdb *cdbp, int fd)
     115             : {
     116             :   struct stat st;
     117             :   unsigned char *mem;
     118             : #ifdef _WIN32
     119             :   HANDLE hFile, hMapping;
     120             : #else
     121             :   unsigned int fsize;
     122             : #endif
     123             : 
     124             :   /* get file size */
     125           0 :   if (fstat(fd, &st) < 0)
     126           0 :     return -1;
     127             :   /* trivial sanity check: at least toc should be here */
     128           0 :   if (st.st_size < 2048) {
     129           0 :     gpg_err_set_errno (EPROTO);
     130           0 :     return -1;
     131             :   }
     132             :   /* memory-map file */
     133             : #ifdef _WIN32
     134             : # ifdef __MINGW32CE__
     135             :   hFile = fd;
     136             : # else
     137             :   hFile = (HANDLE) _get_osfhandle(fd);
     138             : # endif
     139             :   if (hFile == (HANDLE) -1)
     140             :     return -1;
     141             :   hMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
     142             :   if (!hMapping)
     143             :     return -1;
     144             :   mem = (unsigned char *)MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0);
     145             :   if (!mem)
     146             :     return -1;
     147             :   cdbp->cdb_mapping = hMapping;
     148             : #else /*!_WIN32*/
     149           0 :   fsize = (unsigned int)(st.st_size & 0xffffffffu);
     150           0 :   mem = (unsigned char*)mmap(NULL, fsize, PROT_READ, MAP_SHARED, fd, 0);
     151           0 :   if (mem == MAP_FAILED)
     152           0 :     return -1;
     153             : #endif /*!_WIN32*/
     154             : 
     155           0 :   cdbp->cdb_fd = fd;
     156           0 :   cdbp->cdb_fsize = st.st_size;
     157           0 :   cdbp->cdb_mem = mem;
     158             : 
     159             : #if 0
     160             :   /* XXX don't know well about madvise syscall -- is it legal
     161             :      to set different options for parts of one mmap() region?
     162             :      There is also posix_madvise() exist, with POSIX_MADV_RANDOM etc...
     163             :   */
     164             : #ifdef MADV_RANDOM
     165             :   /* set madvise() parameters. Ignore errors for now if system
     166             :      doesn't support it */
     167             :   madvise(mem, 2048, MADV_WILLNEED);
     168             :   madvise(mem + 2048, cdbp->cdb_fsize - 2048, MADV_RANDOM);
     169             : #endif
     170             : #endif
     171             : 
     172           0 :   cdbp->cdb_vpos = cdbp->cdb_vlen = 0;
     173             : 
     174           0 :   return 0;
     175             : }
     176             : 
     177             : 
     178             : /* Frees the internal resources held by structure.  Note that this
     179             :    routine does not close the file. */
     180             : void
     181           0 : cdb_free(struct cdb *cdbp)
     182             : {
     183           0 :   if (cdbp->cdb_mem) {
     184             : #ifdef _WIN32
     185             :     UnmapViewOfFile ((void*) cdbp->cdb_mem);
     186             :     CloseHandle (cdbp->cdb_mapping);
     187             :     cdbp->cdb_mapping = NULL;
     188             : #else
     189           0 :     munmap((void*)cdbp->cdb_mem, cdbp->cdb_fsize);
     190             : #endif /* _WIN32 */
     191           0 :     cdbp->cdb_mem = NULL;
     192             :   }
     193           0 :   cdbp->cdb_fsize = 0;
     194           0 : }
     195             : 
     196             : 
     197             : /* Read data from cdb file, starting at position pos of length len,
     198             :    placing result to buf.  This routine may be used to get actual
     199             :    value found by cdb_find() or other routines that returns position
     200             :    and length of a data.  Returns 0 on success or negative value on
     201             :    error. */
     202             : int
     203           0 : cdb_read(const struct cdb *cdbp, void *buf, unsigned len, cdbi_t pos)
     204             : {
     205           0 :   if (pos > cdbp->cdb_fsize || cdbp->cdb_fsize - pos < len) {
     206           0 :     gpg_err_set_errno (EPROTO);
     207           0 :     return -1;
     208             :   }
     209           0 :   memcpy(buf, cdbp->cdb_mem + pos, len);
     210           0 :   return 0;
     211             : }
     212             : 
     213             : 
     214             : /* Attempts to find a key given by (key,klen) parameters.  If key
     215             :    exists in database, routine returns 1 and places position and
     216             :    length of value associated with this key to internal fields inside
     217             :    cdbp structure, to be accessible by cdb_datapos() and
     218             :    cdb_datalen().  If key is not in database, routines returns 0.  On
     219             :    error, negative value is returned.  Note that using cdb_find() it
     220             :    is possible to lookup only first record with a given key. */
     221             : int
     222           0 : cdb_find(struct cdb *cdbp, const void *key, cdbi_t klen)
     223             : {
     224             :   const unsigned char *htp;     /* hash table pointer */
     225             :   const unsigned char *htab;    /* hash table */
     226             :   const unsigned char *htend;   /* end of hash table */
     227             :   cdbi_t httodo;                /* ht bytes left to look */
     228             :   cdbi_t pos, n;
     229             : 
     230             :   cdbi_t hval;
     231             : 
     232           0 :   if (klen > cdbp->cdb_fsize)     /* if key size is larger than file */
     233           0 :     return 0;
     234             : 
     235           0 :   hval = cdb_hash(key, klen);
     236             : 
     237             :   /* find (pos,n) hash table to use */
     238             :   /* first 2048 bytes (toc) are always available */
     239             :   /* (hval % 256) * 8 */
     240           0 :   htp = cdbp->cdb_mem + ((hval << 3) & 2047); /* index in toc (256x8) */
     241           0 :   n = cdb_unpack(htp + 4);      /* table size */
     242           0 :   if (!n)                       /* empty table */
     243           0 :     return 0;                   /* not found */
     244           0 :   httodo = n << 3;                /* bytes of htab to lookup */
     245           0 :   pos = cdb_unpack(htp);        /* htab position */
     246           0 :   if (n > (cdbp->cdb_fsize >> 3) /* overflow of httodo ? */
     247           0 :       || pos > cdbp->cdb_fsize /* htab start within file ? */
     248           0 :       || httodo > cdbp->cdb_fsize - pos) /* entrie htab within file ? */
     249             :   {
     250           0 :     gpg_err_set_errno (EPROTO);
     251           0 :     return -1;
     252             :   }
     253             : 
     254           0 :   htab = cdbp->cdb_mem + pos;        /* htab pointer */
     255           0 :   htend = htab + httodo;        /* after end of htab */
     256             :   /* htab starting position: rest of hval modulo htsize, 8bytes per elt */
     257           0 :   htp = htab + (((hval >> 8) % n) << 3);
     258             : 
     259             :   for(;;) {
     260           0 :     pos = cdb_unpack(htp + 4);  /* record position */
     261           0 :     if (!pos)
     262           0 :       return 0;
     263           0 :     if (cdb_unpack(htp) == hval) {
     264           0 :       if (pos > cdbp->cdb_fsize - 8) { /* key+val lengths */
     265           0 :         gpg_err_set_errno (EPROTO);
     266           0 :         return -1;
     267             :       }
     268           0 :       if (cdb_unpack(cdbp->cdb_mem + pos) == klen) {
     269           0 :         if (cdbp->cdb_fsize - klen < pos + 8) {
     270           0 :           gpg_err_set_errno (EPROTO);
     271           0 :           return -1;
     272             :         }
     273           0 :         if (memcmp(key, cdbp->cdb_mem + pos + 8, klen) == 0) {
     274           0 :           n = cdb_unpack(cdbp->cdb_mem + pos + 4);
     275           0 :           pos += 8 + klen;
     276           0 :           if (cdbp->cdb_fsize < n || cdbp->cdb_fsize - n < pos) {
     277           0 :             gpg_err_set_errno (EPROTO);
     278           0 :             return -1;
     279             :           }
     280           0 :           cdbp->cdb_vpos = pos;
     281           0 :           cdbp->cdb_vlen = n;
     282           0 :           return 1;
     283             :         }
     284             :       }
     285             :     }
     286           0 :     httodo -= 8;
     287           0 :     if (!httodo)
     288           0 :       return 0;
     289           0 :     if ((htp += 8) >= htend)
     290           0 :       htp = htab;
     291           0 :   }
     292             : 
     293             : }
     294             : 
     295             : 
     296             : 
     297             : /* Sequential-find routines that used separate structure.  It is
     298             :    possible to have many than one record with the same key in a
     299             :    database, and these routines allows to enumerate all them.
     300             :    cdb_findinit() initializes search structure pointed to by cdbfp.
     301             :    It will return negative value on error or 0 on success.  cdb_find­
     302             :    next() attempts to find next matching key, setting value position
     303             :    and length in cdbfp structure.  It will return positive value if
     304             :    given key was found, 0 if there is no more such key(s), or negative
     305             :    value on error.  To access value position and length after
     306             :    successeful call to cdb_findnext() (when it returned positive
     307             :    result), use cdb_datapos() and cdb_datalen() macros with cdbp
     308             :    pointer.  It is error to use cdb_findnext() after it returned 0 or
     309             :    error condition.  These routines is a bit slower than
     310             :    cdb_find().
     311             : 
     312             :    Setting KEY to NULL will start a sequential search through the
     313             :    entire DB.
     314             : */
     315             : int
     316           0 : cdb_findinit(struct cdb_find *cdbfp, struct cdb *cdbp,
     317             :              const void *key, cdbi_t klen)
     318             : {
     319             :   cdbi_t n, pos;
     320             : 
     321           0 :   cdbfp->cdb_cdbp = cdbp;
     322           0 :   cdbfp->cdb_key  = key;
     323           0 :   cdbfp->cdb_klen = klen;
     324           0 :   cdbfp->cdb_hval = key? cdb_hash(key, klen) : 0;
     325             : 
     326           0 :   if (key)
     327             :     {
     328           0 :       cdbfp->cdb_htp = cdbp->cdb_mem + ((cdbfp->cdb_hval << 3) & 2047);
     329           0 :       n = cdb_unpack(cdbfp->cdb_htp + 4);
     330           0 :       cdbfp->cdb_httodo = n << 3; /* Set to size of hash table. */
     331           0 :       if (!n)
     332           0 :         return 0; /* The hash table is empry. */
     333           0 :       pos = cdb_unpack(cdbfp->cdb_htp);
     334           0 :       if (n > (cdbp->cdb_fsize >> 3)
     335           0 :           || pos > cdbp->cdb_fsize
     336           0 :           || cdbfp->cdb_httodo > cdbp->cdb_fsize - pos)
     337             :         {
     338           0 :           gpg_err_set_errno (EPROTO);
     339           0 :           return -1;
     340             :         }
     341             : 
     342           0 :       cdbfp->cdb_htab = cdbp->cdb_mem + pos;
     343           0 :       cdbfp->cdb_htend = cdbfp->cdb_htab + cdbfp->cdb_httodo;
     344           0 :       cdbfp->cdb_htp = cdbfp->cdb_htab + (((cdbfp->cdb_hval >> 8) % n) << 3);
     345             :     }
     346             :   else /* Walk over all entries. */
     347             :     {
     348           0 :       cdbfp->cdb_hval = 0;
     349             :       /* Force stepping in findnext. */
     350           0 :       cdbfp->cdb_htp = cdbfp->cdb_htend = cdbp->cdb_mem;
     351             :     }
     352           0 :   return 0;
     353             : }
     354             : 
     355             : 
     356             : /* See cdb_findinit. */
     357             : int
     358           0 : cdb_findnext(struct cdb_find *cdbfp)
     359             : {
     360             :   cdbi_t pos, n;
     361           0 :   struct cdb *cdbp = cdbfp->cdb_cdbp;
     362             : 
     363           0 :   if (cdbfp->cdb_key)
     364             :     {
     365           0 :       while(cdbfp->cdb_httodo) {
     366           0 :         pos = cdb_unpack(cdbfp->cdb_htp + 4);
     367           0 :         if (!pos)
     368           0 :           return 0;
     369           0 :         n = cdb_unpack(cdbfp->cdb_htp) == cdbfp->cdb_hval;
     370           0 :         if ((cdbfp->cdb_htp += 8) >= cdbfp->cdb_htend)
     371           0 :           cdbfp->cdb_htp = cdbfp->cdb_htab;
     372           0 :         cdbfp->cdb_httodo -= 8;
     373           0 :         if (n) {
     374           0 :           if (pos > cdbp->cdb_fsize - 8) {
     375           0 :             gpg_err_set_errno (EPROTO);
     376           0 :             return -1;
     377             :           }
     378           0 :           if (cdb_unpack(cdbp->cdb_mem + pos) == cdbfp->cdb_klen) {
     379           0 :             if (cdbp->cdb_fsize - cdbfp->cdb_klen < pos + 8) {
     380           0 :               gpg_err_set_errno (EPROTO);
     381           0 :               return -1;
     382             :             }
     383           0 :             if (memcmp(cdbfp->cdb_key,
     384           0 :                        cdbp->cdb_mem + pos + 8, cdbfp->cdb_klen) == 0) {
     385           0 :               n = cdb_unpack(cdbp->cdb_mem + pos + 4);
     386           0 :               pos += 8 + cdbfp->cdb_klen;
     387           0 :               if (cdbp->cdb_fsize < n || cdbp->cdb_fsize - n < pos) {
     388           0 :                 gpg_err_set_errno (EPROTO);
     389           0 :                 return -1;
     390             :               }
     391           0 :               cdbp->cdb_vpos = pos;
     392           0 :               cdbp->cdb_vlen = n;
     393           0 :               return 1;
     394             :             }
     395             :           }
     396             :         }
     397             :       }
     398             :     }
     399             :   else /* Walk over all entries. */
     400             :     {
     401             :       do
     402             :         {
     403           0 :           while (cdbfp->cdb_htp >= cdbfp->cdb_htend)
     404             :             {
     405           0 :               if (cdbfp->cdb_hval > 255)
     406           0 :                 return 0; /* No more items. */
     407             : 
     408           0 :               cdbfp->cdb_htp = cdbp->cdb_mem + cdbfp->cdb_hval * 8;
     409           0 :               cdbfp->cdb_hval++; /* Advance for next round. */
     410           0 :               pos = cdb_unpack (cdbfp->cdb_htp);     /* Offset of table. */
     411           0 :               n   = cdb_unpack (cdbfp->cdb_htp + 4); /* Number of entries. */
     412           0 :               cdbfp->cdb_httodo = n * 8;             /* Size of table. */
     413           0 :               if (n > (cdbp->cdb_fsize / 8)
     414           0 :                   || pos > cdbp->cdb_fsize
     415           0 :                   || cdbfp->cdb_httodo > cdbp->cdb_fsize - pos)
     416             :                 {
     417           0 :                   gpg_err_set_errno (EPROTO);
     418           0 :                   return -1;
     419             :                 }
     420             : 
     421           0 :               cdbfp->cdb_htab  = cdbp->cdb_mem + pos;
     422           0 :               cdbfp->cdb_htend = cdbfp->cdb_htab + cdbfp->cdb_httodo;
     423           0 :               cdbfp->cdb_htp   = cdbfp->cdb_htab;
     424             :             }
     425             : 
     426           0 :           pos = cdb_unpack (cdbfp->cdb_htp + 4); /* Offset of record. */
     427           0 :           cdbfp->cdb_htp += 8;
     428             :         }
     429           0 :       while (!pos);
     430           0 :       if (pos > cdbp->cdb_fsize - 8)
     431             :         {
     432           0 :           gpg_err_set_errno (EPROTO);
     433           0 :           return -1;
     434             :         }
     435             : 
     436           0 :       cdbp->cdb_kpos = pos + 8;
     437           0 :       cdbp->cdb_klen = cdb_unpack(cdbp->cdb_mem + pos);
     438           0 :       cdbp->cdb_vpos = pos + 8 + cdbp->cdb_klen;
     439           0 :       cdbp->cdb_vlen = cdb_unpack(cdbp->cdb_mem + pos + 4);
     440           0 :       n = 8 + cdbp->cdb_klen + cdbp->cdb_vlen;
     441           0 :       if ( pos > cdbp->cdb_fsize || pos > cdbp->cdb_fsize - n)
     442             :         {
     443           0 :           gpg_err_set_errno (EPROTO);
     444           0 :           return -1;
     445             :         }
     446           0 :       return 1; /* Found. */
     447             :     }
     448           0 :   return 0;
     449             : }
     450             : 
     451             : /* Read a chunk from file, ignoring interrupts (EINTR) */
     452             : int
     453           0 : cdb_bread(int fd, void *buf, int len)
     454             : {
     455             :   int l;
     456           0 :   while(len > 0) {
     457           0 :     do l = read(fd, buf, len);
     458           0 :     while(l < 0 && errno == EINTR);
     459           0 :     if (l <= 0) {
     460           0 :       if (!l)
     461           0 :         gpg_err_set_errno (EIO);
     462           0 :       return -1;
     463             :     }
     464           0 :     buf = (char*)buf + l;
     465           0 :     len -= l;
     466             :   }
     467           0 :   return 0;
     468             : }
     469             : 
     470             : /* Find a given key in cdb file, seek a file pointer to it's value and
     471             :    place data length to *dlenp. */
     472             : int
     473           0 : cdb_seek(int fd, const void *key, unsigned klen, cdbi_t *dlenp)
     474             : {
     475             :   cdbi_t htstart;               /* hash table start position */
     476             :   cdbi_t htsize;                /* number of elements in a hash table */
     477             :   cdbi_t httodo;                /* hash table elements left to look */
     478             :   cdbi_t hti;                   /* hash table index */
     479             :   cdbi_t pos;                   /* position in a file */
     480             :   cdbi_t hval;                  /* key's hash value */
     481             :   unsigned char rbuf[64];       /* read buffer */
     482           0 :   int needseek = 1;             /* if we should seek to a hash slot */
     483             : 
     484           0 :   hval = cdb_hash(key, klen);
     485           0 :   pos = (hval & 0xff) << 3; /* position in TOC */
     486             :   /* read the hash table parameters */
     487           0 :   if (lseek(fd, pos, SEEK_SET) < 0 || cdb_bread(fd, rbuf, 8) < 0)
     488           0 :     return -1;
     489           0 :   if ((htsize = cdb_unpack(rbuf + 4)) == 0)
     490           0 :     return 0;
     491           0 :   hti = (hval >> 8) % htsize;     /* start position in hash table */
     492           0 :   httodo = htsize;
     493           0 :   htstart = cdb_unpack(rbuf);
     494             : 
     495             :   for(;;) {
     496           0 :     if (needseek && lseek(fd, htstart + (hti << 3), SEEK_SET) < 0)
     497           0 :       return -1;
     498           0 :     if (cdb_bread(fd, rbuf, 8) < 0)
     499           0 :       return -1;
     500           0 :     if ((pos = cdb_unpack(rbuf + 4)) == 0) /* not found */
     501           0 :       return 0;
     502             : 
     503           0 :     if (cdb_unpack(rbuf) != hval) /* hash value not matched */
     504           0 :       needseek = 0;
     505             :     else { /* hash value matched */
     506           0 :       if (lseek(fd, pos, SEEK_SET) < 0 || cdb_bread(fd, rbuf, 8) < 0)
     507           0 :         return -1;
     508           0 :       if (cdb_unpack(rbuf) == klen) { /* key length matches */
     509             :         /* read the key from file and compare with wanted */
     510           0 :         cdbi_t l = klen, c;
     511           0 :         const char *k = (const char*)key;
     512           0 :         if (*dlenp)
     513           0 :           *dlenp = cdb_unpack(rbuf + 4); /* save value length */
     514             :         for(;;) {
     515           0 :           if (!l) /* the whole key read and matches, return */
     516           0 :             return 1;
     517           0 :           c = l > sizeof(rbuf) ? sizeof(rbuf) : l;
     518           0 :           if (cdb_bread(fd, rbuf, c) < 0)
     519           0 :             return -1;
     520           0 :           if (memcmp(rbuf, k, c) != 0) /* no, it differs, stop here */
     521           0 :             break;
     522           0 :           k += c; l -= c;
     523           0 :         }
     524             :       }
     525           0 :       needseek = 1; /* we're looked to other place, should seek back */
     526             :     }
     527           0 :     if (!--httodo)
     528           0 :       return 0;
     529           0 :     if (++hti == htsize) {
     530           0 :       hti = htstart;
     531           0 :       needseek = 1;
     532             :     }
     533           0 :   }
     534             : }
     535             : 
     536             : cdbi_t
     537           0 : cdb_unpack(const unsigned char buf[4])
     538             : {
     539           0 :   cdbi_t n = buf[3];
     540           0 :   n <<= 8; n |= buf[2];
     541           0 :   n <<= 8; n |= buf[1];
     542           0 :   n <<= 8; n |= buf[0];
     543           0 :   return n;
     544             : }
     545             : 
     546             : /* Add record with key (KEY,KLEN) and value (VAL,VLEN) to a database.
     547             :    Returns 0 on success or negative value on error.  Note that this
     548             :    routine does not checks if given key already exists, but cdb_find()
     549             :    will not see second record with the same key.  It is not possible
     550             :    to continue building a database if cdb_make_add() returned an error
     551             :    indicator. */
     552             : int
     553           0 : cdb_make_add(struct cdb_make *cdbmp,
     554             :              const void *key, cdbi_t klen,
     555             :              const void *val, cdbi_t vlen)
     556             : {
     557             :   unsigned char rlen[8];
     558             :   cdbi_t hval;
     559             :   struct cdb_rl *rl;
     560           0 :   if (klen > 0xffffffff - (cdbmp->cdb_dpos + 8) ||
     561           0 :       vlen > 0xffffffff - (cdbmp->cdb_dpos + klen + 8)) {
     562           0 :     gpg_err_set_errno (ENOMEM);
     563           0 :     return -1;
     564             :   }
     565           0 :   hval = cdb_hash(key, klen);
     566           0 :   rl = cdbmp->cdb_rec[hval&255];
     567           0 :   if (!rl || rl->cnt >= sizeof(rl->rec)/sizeof(rl->rec[0])) {
     568           0 :     rl = (struct cdb_rl*)malloc(sizeof(struct cdb_rl));
     569           0 :     if (!rl) {
     570           0 :       gpg_err_set_errno (ENOMEM);
     571           0 :       return -1;
     572             :     }
     573           0 :     rl->cnt = 0;
     574           0 :     rl->next = cdbmp->cdb_rec[hval&255];
     575           0 :     cdbmp->cdb_rec[hval&255] = rl;
     576             :   }
     577           0 :   rl->rec[rl->cnt].hval = hval;
     578           0 :   rl->rec[rl->cnt].rpos = cdbmp->cdb_dpos;
     579           0 :   ++rl->cnt;
     580           0 :   ++cdbmp->cdb_rcnt;
     581           0 :   cdb_pack(klen, rlen);
     582           0 :   cdb_pack(vlen, rlen + 4);
     583           0 :   if (make_write(cdbmp, rlen, 8) < 0 ||
     584           0 :       make_write(cdbmp, key, klen) < 0 ||
     585           0 :       make_write(cdbmp, val, vlen) < 0)
     586           0 :     return -1;
     587           0 :   return 0;
     588             : }
     589             : 
     590             : int
     591           0 : cdb_make_put(struct cdb_make *cdbmp,
     592             :              const void *key, cdbi_t klen,
     593             :              const void *val, cdbi_t vlen,
     594             :              int flags)
     595             : {
     596             :   unsigned char rlen[8];
     597           0 :   cdbi_t hval = cdb_hash(key, klen);
     598             :   struct cdb_rl *rl;
     599             :   int c, r;
     600             : 
     601           0 :   switch(flags) {
     602             :     case CDB_PUT_REPLACE:
     603             :     case CDB_PUT_INSERT:
     604             :     case CDB_PUT_WARN:
     605           0 :       c = make_find(cdbmp, key, klen, hval, &rl);
     606           0 :       if (c < 0)
     607           0 :         return -1;
     608           0 :       if (c) {
     609           0 :         if (flags == CDB_PUT_INSERT) {
     610           0 :           gpg_err_set_errno (EEXIST);
     611           0 :           return 1;
     612             :         }
     613           0 :         else if (flags == CDB_PUT_REPLACE) {
     614           0 :           --c;
     615           0 :           r = 1;
     616           0 :           break;
     617             :         }
     618             :         else
     619           0 :           r = 1;
     620             :       }
     621             :       /* fall */
     622             : 
     623             :     case CDB_PUT_ADD:
     624           0 :       rl = cdbmp->cdb_rec[hval&255];
     625           0 :       if (!rl || rl->cnt >= sizeof(rl->rec)/sizeof(rl->rec[0])) {
     626           0 :         rl = (struct cdb_rl*)malloc(sizeof(struct cdb_rl));
     627           0 :         if (!rl) {
     628           0 :           gpg_err_set_errno (ENOMEM);
     629           0 :           return -1;
     630             :         }
     631           0 :         rl->cnt = 0;
     632           0 :         rl->next = cdbmp->cdb_rec[hval&255];
     633           0 :         cdbmp->cdb_rec[hval&255] = rl;
     634             :       }
     635           0 :       c = rl->cnt;
     636           0 :       r = 0;
     637           0 :       break;
     638             : 
     639             :     default:
     640           0 :       gpg_err_set_errno (EINVAL);
     641           0 :       return -1;
     642             :   }
     643             : 
     644           0 :   if (klen > 0xffffffff - (cdbmp->cdb_dpos + 8) ||
     645           0 :       vlen > 0xffffffff - (cdbmp->cdb_dpos + klen + 8)) {
     646           0 :     gpg_err_set_errno (ENOMEM);
     647           0 :     return -1;
     648             :   }
     649           0 :   rl->rec[c].hval = hval;
     650           0 :   rl->rec[c].rpos = cdbmp->cdb_dpos;
     651           0 :   if (c == rl->cnt) {
     652           0 :     ++rl->cnt;
     653           0 :     ++cdbmp->cdb_rcnt;
     654             :   }
     655           0 :   cdb_pack(klen, rlen);
     656           0 :   cdb_pack(vlen, rlen + 4);
     657           0 :   if (make_write(cdbmp, rlen, 8) < 0 ||
     658           0 :       make_write(cdbmp, key, klen) < 0 ||
     659           0 :       make_write(cdbmp, val, vlen) < 0)
     660           0 :     return -1;
     661           0 :   return r;
     662             : }
     663             : 
     664             : 
     665             : static int
     666           0 : match(int fd, cdbi_t pos, const char *key, cdbi_t klen)
     667             : {
     668             :   unsigned char buf[64]; /*XXX cdb_buf may be used here instead */
     669           0 :   if (lseek(fd, pos, SEEK_SET) < 0 || read(fd, buf, 8) != 8)
     670           0 :     return -1;
     671           0 :   if (cdb_unpack(buf) != klen)
     672           0 :     return 0;
     673             : 
     674           0 :   while(klen > sizeof(buf)) {
     675           0 :     if (read(fd, buf, sizeof(buf)) != sizeof(buf))
     676           0 :       return -1;
     677           0 :     if (memcmp(buf, key, sizeof(buf)) != 0)
     678           0 :       return 0;
     679           0 :     key += sizeof(buf);
     680           0 :     klen -= sizeof(buf);
     681             :   }
     682           0 :   if (klen) {
     683           0 :     if (read(fd, buf, klen) != klen)
     684           0 :       return -1;
     685           0 :     if (memcmp(buf, key, klen) != 0)
     686           0 :       return 0;
     687             :   }
     688           0 :   return 1;
     689             : }
     690             : 
     691             : 
     692             : static int
     693           0 : make_find (struct cdb_make *cdbmp,
     694             :            const void *key, cdbi_t klen, cdbi_t hval,
     695             :            struct cdb_rl **rlp)
     696             : {
     697           0 :   struct cdb_rl *rl = cdbmp->cdb_rec[hval&255];
     698             :   int r, i;
     699           0 :   int seeked = 0;
     700           0 :   while(rl) {
     701           0 :     for(i = rl->cnt - 1; i >= 0; --i) { /* search backward */
     702           0 :       if (rl->rec[i].hval != hval)
     703           0 :         continue;
     704             :       /*XXX this explicit flush may be unnecessary having
     705             :        * smarter match() that looks to cdb_buf too, but
     706             :        * most of a time here spent in finding hash values
     707             :        * (above), not keys */
     708           0 :       if (cdbmp->cdb_bpos != cdbmp->cdb_buf) {
     709           0 :         if (write(cdbmp->cdb_fd, cdbmp->cdb_buf,
     710           0 :                   cdbmp->cdb_bpos - cdbmp->cdb_buf) < 0)
     711           0 :           return -1;
     712           0 :         cdbmp->cdb_bpos = cdbmp->cdb_buf;
     713             :       }
     714           0 :       seeked = 1;
     715           0 :       r = match(cdbmp->cdb_fd, rl->rec[i].rpos, key, klen);
     716           0 :       if (!r)
     717           0 :         continue;
     718           0 :       if (r < 0)
     719           0 :         return -1;
     720           0 :       if (lseek(cdbmp->cdb_fd, cdbmp->cdb_dpos, SEEK_SET) < 0)
     721           0 :         return -1;
     722           0 :       if (rlp)
     723           0 :         *rlp = rl;
     724           0 :       return i + 1;
     725             :     }
     726           0 :     rl = rl->next;
     727             :   }
     728           0 :   if (seeked && lseek(cdbmp->cdb_fd, cdbmp->cdb_dpos, SEEK_SET) < 0)
     729           0 :     return -1;
     730           0 :   return 0;
     731             : }
     732             : 
     733             : int
     734           0 : cdb_make_exists(struct cdb_make *cdbmp,
     735             :                 const void *key, cdbi_t klen)
     736             : {
     737           0 :   return make_find(cdbmp, key, klen, cdb_hash(key, klen), NULL);
     738             : }
     739             : 
     740             : 
     741             : void
     742           0 : cdb_pack(cdbi_t num, unsigned char buf[4])
     743             : {
     744           0 :   buf[0] = num & 255; num >>= 8;
     745           0 :   buf[1] = num & 255; num >>= 8;
     746           0 :   buf[2] = num & 255;
     747           0 :   buf[3] = num >> 8;
     748           0 : }
     749             : 
     750             : 
     751             : /* Initializes structure to create a database.  File FD should be
     752             :    opened read-write and should be seekable.  Returns 0 on success or
     753             :    negative value on error. */
     754             : int
     755           0 : cdb_make_start(struct cdb_make *cdbmp, int fd)
     756             : {
     757           0 :   memset (cdbmp, 0, sizeof *cdbmp);
     758           0 :   cdbmp->cdb_fd = fd;
     759           0 :   cdbmp->cdb_dpos = 2048;
     760           0 :   cdbmp->cdb_bpos = cdbmp->cdb_buf + 2048;
     761           0 :   return 0;
     762             : }
     763             : 
     764             : 
     765             : static int
     766           0 : ewrite(int fd, const char *buf, int len)
     767             : {
     768           0 :   while(len) {
     769           0 :     int l = write(fd, buf, len);
     770           0 :     if (l < 0 && errno != EINTR)
     771           0 :       return -1;
     772           0 :     if (l > 0)
     773             :       {
     774           0 :         len -= l;
     775           0 :         buf += l;
     776             :       }
     777             :   }
     778           0 :   return 0;
     779             : }
     780             : 
     781             : static int
     782           0 : make_write(struct cdb_make *cdbmp, const char *ptr, cdbi_t len)
     783             : {
     784           0 :   cdbi_t l = sizeof(cdbmp->cdb_buf) - (cdbmp->cdb_bpos - cdbmp->cdb_buf);
     785           0 :   cdbmp->cdb_dpos += len;
     786           0 :   if (len > l) {
     787           0 :     memcpy(cdbmp->cdb_bpos, ptr, l);
     788           0 :     if (ewrite(cdbmp->cdb_fd, cdbmp->cdb_buf, sizeof(cdbmp->cdb_buf)) < 0)
     789           0 :       return -1;
     790           0 :     ptr += l; len -= l;
     791           0 :     l = len / sizeof(cdbmp->cdb_buf);
     792           0 :     if (l) {
     793           0 :       l *= sizeof(cdbmp->cdb_buf);
     794           0 :       if (ewrite(cdbmp->cdb_fd, ptr, l) < 0)
     795           0 :         return -1;
     796           0 :       ptr += l; len -= l;
     797             :     }
     798           0 :     cdbmp->cdb_bpos = cdbmp->cdb_buf;
     799             :   }
     800           0 :   if (len) {
     801           0 :     memcpy(cdbmp->cdb_bpos, ptr, len);
     802           0 :     cdbmp->cdb_bpos += len;
     803             :   }
     804           0 :   return 0;
     805             : }
     806             : 
     807             : static int
     808           0 : cdb_make_finish_internal(struct cdb_make *cdbmp)
     809             : {
     810             :   cdbi_t hcnt[256];             /* hash table counts */
     811             :   cdbi_t hpos[256];             /* hash table positions */
     812             :   struct cdb_rec *htab;
     813             :   unsigned char *p;
     814             :   struct cdb_rl *rl;
     815             :   cdbi_t hsize;
     816             :   unsigned t, i;
     817             : 
     818           0 :   if (((0xffffffff - cdbmp->cdb_dpos) >> 3) < cdbmp->cdb_rcnt) {
     819           0 :     gpg_err_set_errno (ENOMEM);
     820           0 :     return -1;
     821             :   }
     822             : 
     823             :   /* count htab sizes and reorder reclists */
     824           0 :   hsize = 0;
     825           0 :   for (t = 0; t < 256; ++t) {
     826           0 :     struct cdb_rl *rlt = NULL;
     827           0 :     i = 0;
     828           0 :     rl = cdbmp->cdb_rec[t];
     829           0 :     while(rl) {
     830           0 :       struct cdb_rl *rln = rl->next;
     831           0 :       rl->next = rlt;
     832           0 :       rlt = rl;
     833           0 :       i += rl->cnt;
     834           0 :       rl = rln;
     835             :     }
     836           0 :     cdbmp->cdb_rec[t] = rlt;
     837           0 :     if (hsize < (hcnt[t] = i << 1))
     838           0 :       hsize = hcnt[t];
     839             :   }
     840             : 
     841             :   /* allocate memory to hold max htable */
     842           0 :   htab = (struct cdb_rec*)malloc((hsize + 2) * sizeof(struct cdb_rec));
     843           0 :   if (!htab) {
     844           0 :     gpg_err_set_errno (ENOENT);
     845           0 :     return -1;
     846             :   }
     847           0 :   p = (unsigned char *)htab;
     848           0 :   htab += 2;
     849             : 
     850             :   /* build hash tables */
     851           0 :   for (t = 0; t < 256; ++t) {
     852             :     cdbi_t len, hi;
     853           0 :     hpos[t] = cdbmp->cdb_dpos;
     854           0 :     if ((len = hcnt[t]) == 0)
     855           0 :       continue;
     856           0 :     for (i = 0; i < len; ++i)
     857           0 :       htab[i].hval = htab[i].rpos = 0;
     858           0 :     for (rl = cdbmp->cdb_rec[t]; rl; rl = rl->next)
     859           0 :       for (i = 0; i < rl->cnt; ++i) {
     860           0 :         hi = (rl->rec[i].hval >> 8) % len;
     861           0 :         while(htab[hi].rpos)
     862           0 :           if (++hi == len)
     863           0 :             hi = 0;
     864           0 :         htab[hi] = rl->rec[i];
     865             :       }
     866           0 :     for (i = 0; i < len; ++i) {
     867           0 :       cdb_pack(htab[i].hval, p + (i << 3));
     868           0 :       cdb_pack(htab[i].rpos, p + (i << 3) + 4);
     869             :     }
     870           0 :     if (make_write(cdbmp, p, len << 3) < 0) {
     871           0 :       free(p);
     872           0 :       return -1;
     873             :     }
     874             :   }
     875           0 :   free(p);
     876           0 :   if (cdbmp->cdb_bpos != cdbmp->cdb_buf &&
     877           0 :       ewrite(cdbmp->cdb_fd, cdbmp->cdb_buf,
     878           0 :              cdbmp->cdb_bpos - cdbmp->cdb_buf) != 0)
     879           0 :       return -1;
     880           0 :   p = cdbmp->cdb_buf;
     881           0 :   for (t = 0; t < 256; ++t) {
     882           0 :     cdb_pack(hpos[t], p + (t << 3));
     883           0 :     cdb_pack(hcnt[t], p + (t << 3) + 4);
     884             :   }
     885           0 :   if (lseek(cdbmp->cdb_fd, 0, 0) != 0 ||
     886           0 :       ewrite(cdbmp->cdb_fd, p, 2048) != 0)
     887           0 :     return -1;
     888             : 
     889           0 :   return 0;
     890             : }
     891             : 
     892             : static void
     893           0 : cdb_make_free(struct cdb_make *cdbmp)
     894             : {
     895             :   unsigned t;
     896           0 :   for(t = 0; t < 256; ++t) {
     897           0 :     struct cdb_rl *rl = cdbmp->cdb_rec[t];
     898           0 :     while(rl) {
     899           0 :       struct cdb_rl *tm = rl;
     900           0 :       rl = rl->next;
     901           0 :       free(tm);
     902             :     }
     903             :   }
     904           0 : }
     905             : 
     906             : 
     907             : 
     908             : /* Finalizes database file, constructing all needed indexes, and frees
     909             :    memory structures.  It does not close the file descriptor.  Returns
     910             :    0 on success or a negative value on error. */
     911             : int
     912           0 : cdb_make_finish(struct cdb_make *cdbmp)
     913             : {
     914           0 :   int r = cdb_make_finish_internal(cdbmp);
     915           0 :   cdb_make_free(cdbmp);
     916           0 :   return r;
     917             : }
     918             : 
     919             : 
     920             : cdbi_t
     921           0 : cdb_hash(const void *buf, cdbi_t len)
     922             : {
     923           0 :   register const unsigned char *p = (const unsigned char *)buf;
     924           0 :   register const unsigned char *end = p + len;
     925           0 :   register cdbi_t hash = 5381;  /* start value */
     926           0 :   while (p < end)
     927           0 :     hash = (hash + (hash << 5)) ^ *p++;
     928           0 :   return hash;
     929             : }

Generated by: LCOV version 1.11