1 /*************************************************************************************************
2  * Implementation of Curia
3  *                                                      Copyright (C) 2000-2006 Mikio Hirabayashi
4  * This file is part of QDBM, Quick Database Manager.
5  * QDBM is free software; you can redistribute it and/or modify it under the terms of the GNU
6  * Lesser General Public License as published by the Free Software Foundation; either version
7  * 2.1 of the License or any later version.  QDBM is distributed in the hope that it will be
8  * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
9  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
10  * details.
11  * You should have received a copy of the GNU Lesser General Public License along with QDBM; if
12  * not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
13  * 02111-1307 USA.
14  *************************************************************************************************/
15 
16 
17 #define QDBM_INTERNAL  1
18 
19 #include "curia.h"
20 #include "myconf.h"
21 
22 #define CR_NAMEMAX     512               /* max size of a database name */
23 #define CR_DPMAX       512               /* max number of division of a database */
24 #define CR_DIRMODE     00755             /* permission of a creating directory */
25 #define CR_FILEMODE    00644             /* permission of a creating file */
26 #define CR_PATHBUFSIZ  1024              /* size of a path buffer */
27 #define CR_DEFDNUM     5                 /* default number of division of a database */
28 #define CR_ATTRBNUM    16                /* bucket number of attrubute database */
29 #define CR_DPNAME      "depot"           /* name of each sub database */
30 #define CR_KEYDNUM     "dnum"            /* key of division number */
31 #define CR_KEYLRNUM    "lrnum"           /* key of the number of large objects */
32 #define CR_TMPFSUF     MYEXTSTR "crtmp"  /* suffix of a temporary directory */
33 #define CR_LOBDIR      "lob"             /* name of the directory of large objects */
34 #define CR_LOBDDEPTH   2                 /* depth of the directories of large objects */
35 #define CR_NUMBUFSIZ   32                /* size of a buffer for a number */
36 #define CR_IOBUFSIZ    8192              /* size of an I/O buffer */
37 
38 
39 /* private function prototypes */
40 static char *crstrdup(const char *str);
41 static int crdpgetnum(DEPOT *depot, const char *kbuf, int ksiz);
42 static char *crgetlobpath(CURIA *curia, const char *kbuf, int ksiz);
43 static int crmklobdir(const char *path);
44 static int crrmlobdir(const char *path);
45 static int crcplobdir(CURIA *curia, const char *path);
46 static int crwrite(int fd, const void *buf, int size);
47 static int crread(int fd, void *buf, int size);
48 
49 
50 
51 /*************************************************************************************************
52  * public objects
53  *************************************************************************************************/
54 
55 
56 /* Get a database handle. */
cropen(const char * name,int omode,int bnum,int dnum)57 CURIA *cropen(const char *name, int omode, int bnum, int dnum){
58   DEPOT *attr, **depots;
59   char path[CR_PATHBUFSIZ], *tname;
60   int i, j, dpomode, inode, lrnum;
61   struct stat sbuf;
62   CURIA *curia;
63   assert(name);
64   if(dnum < 1) dnum = CR_DEFDNUM;
65   if(dnum > CR_DPMAX) dnum = CR_DPMAX;
66   if(strlen(name) > CR_NAMEMAX){
67     dpecodeset(DP_EMISC, __FILE__, __LINE__);
68     return NULL;
69   }
70   dpomode = DP_OREADER;
71   if(omode & CR_OWRITER){
72     dpomode = DP_OWRITER;
73     if(omode & CR_OCREAT) dpomode |= DP_OCREAT;
74     if(omode & CR_OTRUNC) dpomode |= DP_OTRUNC;
75     if(omode & CR_OSPARSE) dpomode |= DP_OSPARSE;
76   }
77   if(omode & CR_ONOLCK) dpomode |= DP_ONOLCK;
78   if(omode & CR_OLCKNB) dpomode |= DP_OLCKNB;
79   attr = NULL;
80   lrnum = 0;
81   if((omode & CR_OWRITER) && (omode & CR_OCREAT)){
82     if(mkdir(name, CR_DIRMODE) == -1 && errno != EEXIST){
83       dpecodeset(DP_EMKDIR, __FILE__, __LINE__);
84       return NULL;
85     }
86     for(i = 0; i < dnum; i++){
87       sprintf(path, "%s%c%04d", name, MYPATHCHR, i + 1);
88       if(mkdir(path, CR_DIRMODE) == -1 && errno != EEXIST){
89         dpecodeset(DP_EMKDIR, __FILE__, __LINE__);
90         return NULL;
91       }
92     }
93     sprintf(path, "%s%c%s", name, MYPATHCHR, CR_DPNAME);
94     if(!(attr = dpopen(path, dpomode, CR_ATTRBNUM))) return NULL;
95     if(dprnum(attr) > 0){
96       if((dnum = crdpgetnum(attr, CR_KEYDNUM, -1)) < 1 ||
97          (lrnum = crdpgetnum(attr, CR_KEYLRNUM, -1)) < 0){
98         dpclose(attr);
99         dpecodeset(DP_EBROKEN, __FILE__, __LINE__);
100         return NULL;
101       }
102     } else {
103       if(!dpput(attr, CR_KEYDNUM, -1, (char *)&dnum, sizeof(int), DP_DOVER)){
104         dpclose(attr);
105         return NULL;
106       }
107     }
108   }
109   if(!attr){
110     sprintf(path, "%s%c%s", name, MYPATHCHR, CR_DPNAME);
111     if(!(attr = dpopen(path, dpomode, 1))) return NULL;
112     if(!(omode & CR_OTRUNC)){
113       if((dnum = crdpgetnum(attr, CR_KEYDNUM, -1)) < 1 ||
114          (lrnum = crdpgetnum(attr, CR_KEYLRNUM, -1)) < 0){
115         dpclose(attr);
116         dpecodeset(DP_EBROKEN, __FILE__, __LINE__);
117         return NULL;
118       }
119     }
120   }
121   if(omode & CR_OTRUNC){
122     for(i = 0; i < CR_DPMAX; i++){
123       sprintf(path, "%s%c%04d%c%s", name, MYPATHCHR, i + 1, MYPATHCHR, CR_DPNAME);
124       if(unlink(path) == -1 && errno != ENOENT){
125         dpclose(attr);
126         dpecodeset(DP_EUNLINK, __FILE__, __LINE__);
127         return NULL;
128       }
129       sprintf(path, "%s%c%04d%c%s", name, MYPATHCHR, i + 1, MYPATHCHR, CR_LOBDIR);
130       if(!crrmlobdir(path)){
131         dpclose(attr);
132         return NULL;
133       }
134       if(i >= dnum){
135         sprintf(path, "%s%c%04d", name, MYPATHCHR, i + 1);
136         if(rmdir(path) == -1 && errno != ENOENT){
137           dpclose(attr);
138           dpecodeset(DP_ERMDIR, __FILE__, __LINE__);
139           return NULL;
140         }
141       }
142     }
143     errno = 0;
144   }
145   if(lstat(name, &sbuf) == -1){
146     dpclose(attr);
147     dpecodeset(DP_ESTAT, __FILE__, __LINE__);
148     return NULL;
149   }
150   inode = sbuf.st_ino;
151   if(!(depots = malloc(dnum * sizeof(DEPOT *)))){
152     dpclose(attr);
153     dpecodeset(DP_EALLOC, __FILE__, __LINE__);
154     return NULL;
155   }
156   for(i = 0; i < dnum; i++){
157     sprintf(path, "%s%c%04d%c%s", name, MYPATHCHR, i + 1, MYPATHCHR, CR_DPNAME);
158     if(!(depots[i] = dpopen(path, dpomode, bnum))){
159       for(j = 0; j < i; j++){
160         dpclose(depots[j]);
161       }
162       free(depots);
163       dpclose(attr);
164       return NULL;
165     }
166   }
167   curia = malloc(sizeof(CURIA));
168   tname = crstrdup(name);
169   if(!curia || !tname){
170     free(curia);
171     free(tname);
172     for(i = 0; i < dnum; i++){
173       dpclose(depots[i]);
174     }
175     free(depots);
176     dpclose(attr);
177     dpecodeset(DP_EALLOC, __FILE__, __LINE__);
178     return NULL;
179   }
180   curia->name = tname;
181   curia->wmode = (omode & CR_OWRITER);
182   curia->inode = inode;
183   curia->attr = attr;
184   curia->depots = depots;
185   curia->dnum = dnum;
186   curia->inum = 0;
187   curia->lrnum = lrnum;
188   return curia;
189 }
190 
191 
192 /* Close a database handle. */
crclose(CURIA * curia)193 int crclose(CURIA *curia){
194   int i, err;
195   assert(curia);
196   err = FALSE;
197   for(i = 0; i < curia->dnum; i++){
198     if(!dpclose(curia->depots[i])) err = TRUE;
199   }
200   free(curia->depots);
201   if(curia->wmode){
202     if(!dpput(curia->attr, CR_KEYLRNUM, -1, (char *)&(curia->lrnum), sizeof(int), DP_DOVER))
203       err = TRUE;
204   }
205   if(!dpclose(curia->attr)) err = TRUE;
206   free(curia->name);
207   free(curia);
208   return err ? FALSE : TRUE;
209 }
210 
211 
212 /* Store a record. */
crput(CURIA * curia,const char * kbuf,int ksiz,const char * vbuf,int vsiz,int dmode)213 int crput(CURIA *curia, const char *kbuf, int ksiz, const char *vbuf, int vsiz, int dmode){
214   int dpdmode;
215   int tnum;
216   assert(curia && kbuf && vbuf);
217   if(!curia->wmode){
218     dpecodeset(DP_EMODE, __FILE__, __LINE__);
219     return FALSE;
220   }
221   switch(dmode){
222   case CR_DKEEP: dpdmode = DP_DKEEP; break;
223   case CR_DCAT: dpdmode = DP_DCAT; break;
224   default: dpdmode = DP_DOVER; break;
225   }
226   tnum = dpouterhash(kbuf, ksiz) % curia->dnum;
227   return dpput(curia->depots[tnum], kbuf, ksiz, vbuf, vsiz, dpdmode);
228 }
229 
230 
231 /* Delete a record. */
crout(CURIA * curia,const char * kbuf,int ksiz)232 int crout(CURIA *curia, const char *kbuf, int ksiz){
233   int tnum;
234   assert(curia && kbuf);
235   if(!curia->wmode){
236     dpecodeset(DP_EMODE, __FILE__, __LINE__);
237     return FALSE;
238   }
239   tnum = dpouterhash(kbuf, ksiz) % curia->dnum;
240   return dpout(curia->depots[tnum], kbuf, ksiz);
241 }
242 
243 
244 /* Retrieve a record. */
crget(CURIA * curia,const char * kbuf,int ksiz,int start,int max,int * sp)245 char *crget(CURIA *curia, const char *kbuf, int ksiz, int start, int max, int *sp){
246   int tnum;
247   assert(curia && kbuf && start >= 0);
248   tnum = dpouterhash(kbuf, ksiz) % curia->dnum;
249   return dpget(curia->depots[tnum], kbuf, ksiz, start, max, sp);
250 }
251 
252 
253 /* Retrieve a record and write the value into a buffer. */
crgetwb(CURIA * curia,const char * kbuf,int ksiz,int start,int max,char * vbuf)254 int crgetwb(CURIA *curia, const char *kbuf, int ksiz, int start, int max, char *vbuf){
255   int tnum;
256   assert(curia && kbuf && start >= 0 && max >= 0 && vbuf);
257   tnum = dpouterhash(kbuf, ksiz) % curia->dnum;
258   return dpgetwb(curia->depots[tnum], kbuf, ksiz, start, max, vbuf);
259 }
260 
261 
262 /* Get the size of the value of a record. */
crvsiz(CURIA * curia,const char * kbuf,int ksiz)263 int crvsiz(CURIA *curia, const char *kbuf, int ksiz){
264   int tnum;
265   assert(curia && kbuf);
266   tnum = dpouterhash(kbuf, ksiz) % curia->dnum;
267   return dpvsiz(curia->depots[tnum], kbuf, ksiz);
268 }
269 
270 
271 /* Initialize the iterator of a database handle. */
criterinit(CURIA * curia)272 int criterinit(CURIA *curia){
273   int i, err;
274   assert(curia);
275   err = FALSE;
276   for(i = 0; i < curia->dnum; i++){
277     if(!dpiterinit(curia->depots[i])){
278       err = TRUE;
279       break;
280     }
281   }
282   curia->inum = 0;
283   return err ? FALSE : TRUE;
284 }
285 
286 
287 /* Get the next key of the iterator. */
criternext(CURIA * curia,int * sp)288 char *criternext(CURIA *curia, int *sp){
289   char *kbuf;
290   assert(curia);
291   kbuf = NULL;
292   while(curia->inum < curia->dnum && !(kbuf = dpiternext(curia->depots[curia->inum], sp))){
293     if(dpecode != DP_ENOITEM) return NULL;
294     (curia->inum)++;
295   }
296   return kbuf;
297 }
298 
299 
300 /* Set alignment of a database handle. */
crsetalign(CURIA * curia,int align)301 int crsetalign(CURIA *curia, int align){
302   int i, err;
303   assert(curia);
304   if(!curia->wmode){
305     dpecodeset(DP_EMODE, __FILE__, __LINE__);
306     return FALSE;
307   }
308   err = FALSE;
309   for(i = 0; i < curia->dnum; i++){
310     if(!dpsetalign(curia->depots[i], align)){
311       err = TRUE;
312       break;
313     }
314   }
315   return err ? FALSE : TRUE;
316 }
317 
318 
319 /* Set the size of the free block pool of a database handle. */
crsetfbpsiz(CURIA * curia,int size)320 int crsetfbpsiz(CURIA *curia, int size){
321   int i, err;
322   assert(curia && size >= 0);
323   if(!curia->wmode){
324     dpecodeset(DP_EMODE, __FILE__, __LINE__);
325     return FALSE;
326   }
327   err = FALSE;
328   for(i = 0; i < curia->dnum; i++){
329     if(!dpsetfbpsiz(curia->depots[i], size)){
330       err = TRUE;
331       break;
332     }
333   }
334   return err ? FALSE : TRUE;
335 }
336 
337 
338 /* Synchronize contents of updating a database with the files and the devices. */
crsync(CURIA * curia)339 int crsync(CURIA *curia){
340   int i, err;
341   assert(curia);
342   if(!curia->wmode){
343     dpecodeset(DP_EMODE, __FILE__, __LINE__);
344     return FALSE;
345   }
346   err = FALSE;
347   if(!dpput(curia->attr, CR_KEYLRNUM, -1, (char *)&(curia->lrnum), sizeof(int), DP_DOVER) ||
348      !dpsync(curia->attr)) err = TRUE;
349   for(i = 0; i < curia->dnum; i++){
350     if(!dpsync(curia->depots[i])){
351       err = TRUE;
352       break;
353     }
354   }
355   return err ? FALSE : TRUE;
356 }
357 
358 
359 /* Optimize a database. */
croptimize(CURIA * curia,int bnum)360 int croptimize(CURIA *curia, int bnum){
361   int i, err;
362   assert(curia);
363   if(!curia->wmode){
364     dpecodeset(DP_EMODE, __FILE__, __LINE__);
365     return FALSE;
366   }
367   err = FALSE;
368   for(i = 0; i < curia->dnum; i++){
369     if(!dpoptimize(curia->depots[i], bnum)){
370       err = TRUE;
371       break;
372     }
373   }
374   curia->inum = 0;
375   return err ? FALSE : TRUE;
376 }
377 
378 
379 /* Get the name of a database. */
crname(CURIA * curia)380 char *crname(CURIA *curia){
381   char *name;
382   assert(curia);
383   if(!(name = crstrdup(curia->name))){
384     dpecodeset(DP_EALLOC, __FILE__, __LINE__);
385     return NULL;
386   }
387   return name;
388 }
389 
390 
391 /* Get the total size of database files. */
crfsiz(CURIA * curia)392 int crfsiz(CURIA *curia){
393   int i, sum, rv;
394   assert(curia);
395   sum = 0;
396   for(i = 0; i < curia->dnum; i++){
397     rv = dpfsiz(curia->depots[i]);
398     if(rv == -1) return -1;
399     sum += rv;
400   }
401   return sum;
402 }
403 
404 
405 /* Get the total size of database files as double-precision value. */
crfsizd(CURIA * curia)406 double crfsizd(CURIA *curia){
407   double sum;
408   int i, rv;
409   assert(curia);
410   sum = 0.0;
411   for(i = 0; i < curia->dnum; i++){
412     rv = dpfsiz(curia->depots[i]);
413     if(rv == -1) return -1.0;
414     sum += rv;
415   }
416   return sum;
417 }
418 
419 
420 /* Get the total number of the elements of each bucket array. */
crbnum(CURIA * curia)421 int crbnum(CURIA *curia){
422   int i, sum, rv;
423   assert(curia);
424   sum = 0;
425   for(i = 0; i < curia->dnum; i++){
426     rv = dpbnum(curia->depots[i]);
427     if(rv == -1) return -1;
428     sum += rv;
429   }
430   return sum;
431 }
432 
433 
434 /* Get the total number of the used elements of each bucket array. */
crbusenum(CURIA * curia)435 int crbusenum(CURIA *curia){
436   int i, sum, rv;
437   assert(curia);
438   sum = 0;
439   for(i = 0; i < curia->dnum; i++){
440     rv = dpbusenum(curia->depots[i]);
441     if(rv == -1) return -1;
442     sum += rv;
443   }
444   return sum;
445 }
446 
447 
448 /* Get the number of the records stored in a database. */
crrnum(CURIA * curia)449 int crrnum(CURIA *curia){
450   int i, sum, rv;
451   assert(curia);
452   sum = 0;
453   for(i = 0; i < curia->dnum; i++){
454     rv = dprnum(curia->depots[i]);
455     if(rv == -1) return -1;
456     sum += rv;
457   }
458   return sum;
459 }
460 
461 
462 /* Check whether a database handle is a writer or not. */
crwritable(CURIA * curia)463 int crwritable(CURIA *curia){
464   assert(curia);
465   return curia->wmode;
466 }
467 
468 
469 /* Check whether a database has a fatal error or not. */
crfatalerror(CURIA * curia)470 int crfatalerror(CURIA *curia){
471   int i;
472   assert(curia);
473   if(dpfatalerror(curia->attr)) return TRUE;
474   for(i = 0; i < curia->dnum; i++){
475     if(dpfatalerror(curia->depots[i])) return TRUE;
476   }
477   return FALSE;
478 }
479 
480 
481 /* Get the inode number of a database directory. */
crinode(CURIA * curia)482 int crinode(CURIA *curia){
483   assert(curia);
484   return curia->inode;
485 }
486 
487 
488 /* Get the last modified time of a database. */
crmtime(CURIA * curia)489 time_t crmtime(CURIA *curia){
490   assert(curia);
491   return dpmtime(curia->attr);
492 }
493 
494 
495 /* Remove a database directory. */
crremove(const char * name)496 int crremove(const char *name){
497   struct stat sbuf;
498   CURIA *curia;
499   char path[CR_PATHBUFSIZ];
500   assert(name);
501   if(lstat(name, &sbuf) == -1){
502     dpecodeset(DP_ESTAT, __FILE__, __LINE__);
503     return FALSE;
504   }
505   if((curia = cropen(name, CR_OWRITER | CR_OTRUNC, 1, 1)) != NULL) crclose(curia);
506   sprintf(path, "%s%c0001%c%s", name, MYPATHCHR, MYPATHCHR, CR_DPNAME);
507   dpremove(path);
508   sprintf(path, "%s%c0001", name, MYPATHCHR);
509   if(rmdir(path) == -1){
510     dpecodeset(DP_ERMDIR, __FILE__, __LINE__);
511     return FALSE;
512   }
513   sprintf(path, "%s%c%s", name, MYPATHCHR, CR_DPNAME);
514   if(!dpremove(path)) return FALSE;
515   if(rmdir(name) == -1){
516     dpecodeset(DP_ERMDIR, __FILE__, __LINE__);
517     return FALSE;
518   }
519   return TRUE;
520 }
521 
522 
523 /* Repair a broken database directory. */
crrepair(const char * name)524 int crrepair(const char *name){
525   CURIA *tcuria;
526   DEPOT *tdepot;
527   char path[CR_PATHBUFSIZ], *kbuf, *vbuf;
528   struct stat sbuf;
529   int i, j, err, bnum, dnum, ksiz, vsiz;
530   assert(name);
531   err = FALSE;
532   bnum = 0;
533   dnum = 0;
534   for(i = 1; i <= CR_DPMAX; i++){
535     sprintf(path, "%s%c%04d%c%s", name, MYPATHCHR, i, MYPATHCHR, CR_DPNAME);
536     if(lstat(path, &sbuf) != -1){
537       dnum++;
538       if(!dprepair(path)) err = TRUE;
539       if((tdepot = dpopen(path, DP_OREADER, -1)) != NULL){
540         bnum += dpbnum(tdepot);
541         dpclose(tdepot);
542       }
543     }
544   }
545   if(dnum < CR_DEFDNUM) dnum = CR_DEFDNUM;
546   bnum /= dnum;
547   sprintf(path, "%s%s", name, CR_TMPFSUF);
548   if((tcuria = cropen(path, CR_OWRITER | CR_OCREAT | CR_OTRUNC, bnum, dnum)) != NULL){
549     for(i = 1; i <= CR_DPMAX; i++){
550       sprintf(path, "%s%c%04d%c%s", name, MYPATHCHR, i, MYPATHCHR, CR_DPNAME);
551       if(lstat(path, &sbuf) != -1){
552         if((tdepot = dpopen(path, DP_OREADER, -1)) != NULL){
553           if(!dpiterinit(tdepot)) err = TRUE;
554           while((kbuf = dpiternext(tdepot, &ksiz)) != NULL){
555             if((vbuf = dpget(tdepot, kbuf, ksiz, 0, -1, &vsiz)) != NULL){
556               if(!crput(tcuria, kbuf, ksiz, vbuf, vsiz, CR_DKEEP)) err = TRUE;
557               free(vbuf);
558             }
559             free(kbuf);
560           }
561           dpclose(tdepot);
562         } else {
563           err = TRUE;
564         }
565       }
566       for(j = 0; j <= CR_DPMAX; j++){
567         sprintf(path, "%s%c%04d%c%s", name, MYPATHCHR, j, MYPATHCHR, CR_LOBDIR);
568         if(lstat(path, &sbuf) != -1){
569           if(!crcplobdir(tcuria, path)) err = TRUE;
570         }
571       }
572     }
573     if(!crclose(tcuria)) err = TRUE;
574     if(!crremove(name)) err = TRUE;
575     sprintf(path, "%s%s", name, CR_TMPFSUF);
576     if(rename(path, name) == -1){
577       if(!err) dpecodeset(DP_EMISC, __FILE__, __LINE__);
578       err = TRUE;
579     }
580   } else {
581     err = TRUE;
582   }
583   return err ? FALSE : TRUE;
584 }
585 
586 
587 /* Dump all records as endian independent data. */
crexportdb(CURIA * curia,const char * name)588 int crexportdb(CURIA *curia, const char *name){
589   char path[CR_PATHBUFSIZ], *kbuf, *vbuf, *pbuf;
590   int i, err, *fds, ksiz, vsiz, psiz;
591   assert(curia && name);
592   if(!(criterinit(curia))) return FALSE;
593   if(mkdir(name, CR_DIRMODE) == -1 && errno != EEXIST){
594     dpecodeset(DP_EMKDIR, __FILE__, __LINE__);
595     return FALSE;
596   }
597   err = FALSE;
598   fds = malloc(sizeof(int) * curia->dnum);
599   for(i = 0; i < curia->dnum; i++){
600     sprintf(path, "%s%c%04d", name, MYPATHCHR, i + 1);
601     if((fds[i] = open(path, O_RDWR | O_CREAT | O_TRUNC, CR_FILEMODE)) == -1){
602       if(!err) dpecodeset(DP_EOPEN, __FILE__, __LINE__);
603       err = TRUE;
604       break;
605     }
606   }
607   while(!err && (kbuf = criternext(curia, &ksiz)) != NULL){
608     if((vbuf = crget(curia, kbuf, ksiz, 0, -1, &vsiz)) != NULL){
609       if((pbuf = malloc(ksiz + vsiz + CR_NUMBUFSIZ * 2)) != NULL){
610         psiz = 0;
611         psiz += sprintf(pbuf + psiz, "%X\n%X\n", ksiz, vsiz);
612         memcpy(pbuf + psiz, kbuf, ksiz);
613         psiz += ksiz;
614         pbuf[psiz++] = '\n';
615         memcpy(pbuf + psiz, vbuf, vsiz);
616         psiz += vsiz;
617         pbuf[psiz++] = '\n';
618         if(!crwrite(fds[curia->inum], pbuf, psiz)){
619           dpecodeset(DP_EWRITE, __FILE__, __LINE__);
620           err = TRUE;
621         }
622         free(pbuf);
623       } else {
624         dpecodeset(DP_EALLOC, __FILE__, __LINE__);
625         err = TRUE;
626       }
627       free(vbuf);
628     } else {
629       err = TRUE;
630     }
631     free(kbuf);
632   }
633   for(i = 0; i < curia->dnum; i++){
634     if(fds[i] != -1 && close(fds[i]) == -1){
635       if(!err) dpecodeset(DP_ECLOSE, __FILE__, __LINE__);
636       err = TRUE;
637     }
638   }
639   free(fds);
640   return !err && !crfatalerror(curia);
641 }
642 
643 
644 /* Load all records from endian independent data. */
crimportdb(CURIA * curia,const char * name)645 int crimportdb(CURIA *curia, const char *name){
646   DEPOT *depot;
647   char ipath[CR_PATHBUFSIZ], opath[CR_PATHBUFSIZ], *kbuf, *vbuf;
648   int i, err, ksiz, vsiz;
649   struct stat sbuf;
650   assert(curia && name);
651   if(!curia->wmode){
652     dpecodeset(DP_EMODE, __FILE__, __LINE__);
653     return FALSE;
654   }
655   if(crrnum(curia) > 0){
656     dpecodeset(DP_EMISC, __FILE__, __LINE__);
657     return FALSE;
658   }
659   err = FALSE;
660   for(i = 0; !err && i < CR_DPMAX; i++){
661     sprintf(ipath, "%s%c%04d", name, MYPATHCHR, i + 1);
662     if(lstat(ipath, &sbuf) == -1 || !S_ISREG(sbuf.st_mode)) break;
663     sprintf(opath, "%s%c%04d%s", curia->name, MYPATHCHR, i + 1, CR_TMPFSUF);
664     if((depot = dpopen(opath, DP_OWRITER | DP_OCREAT | DP_OTRUNC, -1)) != NULL){
665       if(dpimportdb(depot, ipath)){
666         dpiterinit(depot);
667         while((kbuf = dpiternext(depot, &ksiz)) != NULL){
668           if((vbuf = dpget(depot, kbuf, ksiz, 0, -1, &vsiz)) != NULL){
669             if(!crput(curia, kbuf, ksiz, vbuf, vsiz, CR_DKEEP)) err = TRUE;
670             free(vbuf);
671           } else {
672             err = TRUE;
673           }
674           free(kbuf);
675         }
676       } else {
677         err = TRUE;
678       }
679       if(!dpclose(depot)) err = TRUE;
680       if(!dpremove(opath)) err = TRUE;
681     } else {
682       err = TRUE;
683     }
684   }
685   return !err && !crfatalerror(curia);
686 }
687 
688 
689 /* Store a large object. */
crputlob(CURIA * curia,const char * kbuf,int ksiz,const char * vbuf,int vsiz,int dmode)690 int crputlob(CURIA *curia, const char *kbuf, int ksiz, const char *vbuf, int vsiz, int dmode){
691   char *path;
692   int mode, fd, err, be;
693   struct stat sbuf;
694   assert(curia && kbuf && vbuf);
695   if(!curia->wmode){
696     dpecodeset(DP_EMODE, __FILE__, __LINE__);
697     return FALSE;
698   }
699   if(ksiz < 0) ksiz = strlen(kbuf);
700   if(vsiz < 0) vsiz = strlen(vbuf);
701   if(!(path = crgetlobpath(curia, kbuf, ksiz))) return FALSE;
702   if(!crmklobdir(path)){
703     free(path);
704     return FALSE;
705   }
706   be = lstat(path, &sbuf) != -1 && S_ISREG(sbuf.st_mode);
707   mode = O_RDWR | O_CREAT;
708   if(dmode & CR_DKEEP) mode |= O_EXCL;
709   if(dmode & CR_DCAT){
710     mode |= O_APPEND;
711   } else {
712     mode |= O_TRUNC;
713   }
714   if((fd = open(path, mode, CR_FILEMODE)) == -1){
715     free(path);
716     dpecodeset(DP_EOPEN, __FILE__, __LINE__);
717     if(dmode == CR_DKEEP) dpecodeset(DP_EKEEP, __FILE__, __LINE__);
718     return FALSE;
719   }
720   free(path);
721   err = FALSE;
722   if(crwrite(fd, vbuf, vsiz) == -1){
723     err = TRUE;
724     dpecodeset(DP_EWRITE, __FILE__, __LINE__);
725   }
726   if(close(fd) == -1){
727     err = TRUE;
728     dpecodeset(DP_ECLOSE, __FILE__, __LINE__);
729   }
730   if(!err && !be) (curia->lrnum)++;
731   return err ? FALSE : TRUE;
732 }
733 
734 
735 /* Delete a large object. */
croutlob(CURIA * curia,const char * kbuf,int ksiz)736 int croutlob(CURIA *curia, const char *kbuf, int ksiz){
737   char *path;
738   int err, be;
739   struct stat sbuf;
740   assert(curia && kbuf);
741   if(!curia->wmode){
742     dpecodeset(DP_EMODE, __FILE__, __LINE__);
743     return FALSE;
744   }
745   if(ksiz < 0) ksiz = strlen(kbuf);
746   if(!(path = crgetlobpath(curia, kbuf, ksiz))) return FALSE;
747   be = lstat(path, &sbuf) != -1 && S_ISREG(sbuf.st_mode);
748   err = FALSE;
749   if(unlink(path) == -1){
750     err = TRUE;
751     dpecodeset(DP_ENOITEM, __FILE__, __LINE__);
752   }
753   free(path);
754   if(!err && be) (curia->lrnum)--;
755   return err ? FALSE : TRUE;
756 }
757 
758 
759 /* Retrieve a large object. */
crgetlob(CURIA * curia,const char * kbuf,int ksiz,int start,int max,int * sp)760 char *crgetlob(CURIA *curia, const char *kbuf, int ksiz, int start, int max, int *sp){
761   char *path, *buf;
762   struct stat sbuf;
763   int fd, size;
764   assert(curia && kbuf && start >= 0);
765   if(ksiz < 0) ksiz = strlen(kbuf);
766   if(!(path = crgetlobpath(curia, kbuf, ksiz))) return NULL;
767   if((fd = open(path, O_RDONLY, CR_FILEMODE)) == -1){
768     free(path);
769     dpecodeset(DP_ENOITEM, __FILE__, __LINE__);
770     return NULL;
771   }
772   free(path);
773   if(fstat(fd, &sbuf) == -1){
774     close(fd);
775     dpecodeset(DP_ESTAT, __FILE__, __LINE__);
776     return NULL;
777   }
778   if(start > sbuf.st_size){
779     close(fd);
780     dpecodeset(DP_ENOITEM, __FILE__, __LINE__);
781     return NULL;
782   }
783   if(lseek(fd, start, SEEK_SET) == -1){
784     close(fd);
785     dpecodeset(DP_ESEEK, __FILE__, __LINE__);
786     return NULL;
787   }
788   if(max < 0) max = sbuf.st_size;
789   if(!(buf = malloc(max + 1))){
790     close(fd);
791     dpecodeset(DP_EALLOC, __FILE__, __LINE__);
792     return NULL;
793   }
794   size = crread(fd, buf, max);
795   close(fd);
796   if(size == -1){
797     free(buf);
798     dpecodeset(DP_EREAD, __FILE__, __LINE__);
799     return NULL;
800   }
801   buf[size] = '\0';
802   if(sp) *sp = size;
803   return buf;
804 }
805 
806 
807 /* Get the file descriptor of a large object. */
crgetlobfd(CURIA * curia,const char * kbuf,int ksiz)808 int crgetlobfd(CURIA *curia, const char *kbuf, int ksiz){
809   char *path;
810   int fd;
811   assert(curia && kbuf);
812   if(ksiz < 0) ksiz = strlen(kbuf);
813   if(!(path = crgetlobpath(curia, kbuf, ksiz))) return -1;
814   if((fd = open(path, curia->wmode ? O_RDWR: O_RDONLY, CR_FILEMODE)) == -1){
815     free(path);
816     dpecodeset(DP_ENOITEM, __FILE__, __LINE__);
817     return -1;
818   }
819   free(path);
820   return fd;
821 }
822 
823 
824 /* Get the size of the value of a large object. */
crvsizlob(CURIA * curia,const char * kbuf,int ksiz)825 int crvsizlob(CURIA *curia, const char *kbuf, int ksiz){
826   char *path;
827   struct stat sbuf;
828   assert(curia && kbuf);
829   if(ksiz < 0) ksiz = strlen(kbuf);
830   if(!(path = crgetlobpath(curia, kbuf, ksiz))) return -1;
831   if(lstat(path, &sbuf) == -1){
832     free(path);
833     dpecodeset(DP_ENOITEM, __FILE__, __LINE__);
834     return -1;
835   }
836   free(path);
837   return sbuf.st_size;
838 }
839 
840 
841 /* Get the number of the large objects stored in a database. */
crrnumlob(CURIA * curia)842 int crrnumlob(CURIA *curia){
843   assert(curia);
844   return curia->lrnum;
845 }
846 
847 
848 
849 /*************************************************************************************************
850  * features for experts
851  *************************************************************************************************/
852 
853 
854 /* Synchronize updating contents on memory. */
crmemsync(CURIA * curia)855 int crmemsync(CURIA *curia){
856   int i, err;
857   assert(curia);
858   if(!curia->wmode){
859     dpecodeset(DP_EMODE, __FILE__, __LINE__);
860     return FALSE;
861   }
862   err = FALSE;
863   if(!dpput(curia->attr, CR_KEYLRNUM, -1, (char *)&(curia->lrnum), sizeof(int), DP_DOVER) ||
864      !dpmemsync(curia->attr)) err = TRUE;
865   for(i = 0; i < curia->dnum; i++){
866     if(!dpmemsync(curia->depots[i])){
867       err = TRUE;
868       break;
869     }
870   }
871   return err ? FALSE : TRUE;
872 }
873 
874 
875 /* Get flags of a database. */
crgetflags(CURIA * curia)876 int crgetflags(CURIA *curia){
877   assert(curia);
878   return dpgetflags(curia->attr);
879 }
880 
881 
882 /* Set flags of a database. */
crsetflags(CURIA * curia,int flags)883 int crsetflags(CURIA *curia, int flags){
884   assert(curia);
885   if(!curia->wmode){
886     dpecodeset(DP_EMODE, __FILE__, __LINE__);
887     return FALSE;
888   }
889   return dpsetflags(curia->attr, flags);
890 }
891 
892 
893 
894 /*************************************************************************************************
895  * private objects
896  *************************************************************************************************/
897 
898 
899 /* Get a copied string.
900    `str' specifies an original string.
901    The return value is a copied string whose region is allocated by `malloc'. */
crstrdup(const char * str)902 static char *crstrdup(const char *str){
903   int len;
904   char *buf;
905   assert(str);
906   len = strlen(str);
907   if(!(buf = malloc(len + 1))) return NULL;
908   memcpy(buf, str, len + 1);
909   return buf;
910 }
911 
912 
913 /* Get an integer from a database.
914    `depot' specifies an inner database handle.
915    `kbuf' specifies the pointer to the region of a key.
916    `ksiz' specifies the size of the key.
917    The return value is the integer of the corresponding record. */
crdpgetnum(DEPOT * depot,const char * kbuf,int ksiz)918 static int crdpgetnum(DEPOT *depot, const char *kbuf, int ksiz){
919   char *vbuf;
920   int vsiz, rv;
921   if(!(vbuf = dpget(depot, kbuf, ksiz, 0, -1, &vsiz)) || vsiz != sizeof(int)){
922     free(vbuf);
923     return INT_MIN;
924   }
925   rv = *(int *)vbuf;
926   free(vbuf);
927   return rv;
928 }
929 
930 
931 /* Get the path of a large object.
932    `curia' specifies a database handle.
933    `kbuf' specifies the pointer to the region of a key.
934    `ksiz' specifies the size of the key.
935    The return value is a path string whose region is allocated by `malloc'. */
crgetlobpath(CURIA * curia,const char * kbuf,int ksiz)936 static char *crgetlobpath(CURIA *curia, const char *kbuf, int ksiz){
937   char prefix[CR_PATHBUFSIZ], *wp, *path;
938   int i, hash;
939   assert(curia && kbuf && ksiz >= 0);
940   wp = prefix;
941   wp += sprintf(wp, "%s%c%04d%c%s%c",
942                 curia->name, MYPATHCHR, dpouterhash(kbuf, ksiz) % curia->dnum + 1,
943                 MYPATHCHR, CR_LOBDIR, MYPATHCHR);
944   hash = dpinnerhash(kbuf, ksiz);
945   for(i = 0; i < CR_LOBDDEPTH; i++){
946     wp += sprintf(wp, "%02X%c", hash % 0x100, MYPATHCHR);
947     hash /= 0x100;
948   }
949   if(!(path = malloc(strlen(prefix) + ksiz * 2 + 1))){
950     dpecodeset(DP_EALLOC, __FILE__, __LINE__);
951     return NULL;
952   }
953   wp = path;
954   wp += sprintf(path, "%s", prefix);
955   for(i = 0; i < ksiz; i++){
956     wp += sprintf(wp, "%02X", ((unsigned char *)kbuf)[i]);
957   }
958   return path;
959 }
960 
961 
962 /* Create directories included in a path.
963    `path' specifies a path.
964    The return value is true if successful, else, it is false. */
crmklobdir(const char * path)965 static int crmklobdir(const char *path){
966   char elem[CR_PATHBUFSIZ], *wp;
967   const char *dp;
968   int err, len;
969   wp = elem;
970   err = FALSE;
971   while(*path != '\0' && (dp = strchr(path, MYPATHCHR)) != NULL){
972     len = dp - path;
973     if(wp != elem) wp += sprintf(wp, "%c", MYPATHCHR);
974     memcpy(wp, path, len);
975     wp[len] = '\0';
976     wp += len;
977     if(mkdir(elem, CR_DIRMODE) == -1 && errno != EEXIST) err = TRUE;
978     path = dp + 1;
979   }
980   if(err) dpecodeset(DP_EMKDIR, __FILE__, __LINE__);
981   return err ? FALSE : TRUE;
982 }
983 
984 
985 /* Remove file and directories under a directory.
986    `path' specifies a path.
987    The return value is true if successful, else, it is false. */
crrmlobdir(const char * path)988 static int crrmlobdir(const char *path){
989   char elem[CR_PATHBUFSIZ];
990   DIR *DD;
991   struct dirent *dp;
992   assert(path);
993   if(unlink(path) != -1){
994     return TRUE;
995   } else {
996     if(errno == ENOENT) return TRUE;
997     if(!(DD = opendir(path))){
998       dpecodeset(DP_EMISC, __FILE__, __LINE__);
999       return FALSE;
1000     }
1001     while((dp = readdir(DD)) != NULL){
1002       if(!strcmp(dp->d_name, MYCDIRSTR) || !strcmp(dp->d_name, MYPDIRSTR)) continue;
1003       sprintf(elem, "%s%c%s", path, MYPATHCHR, dp->d_name);
1004       if(!crrmlobdir(elem)){
1005         closedir(DD);
1006         return FALSE;
1007       }
1008     }
1009   }
1010   if(closedir(DD) == -1){
1011     dpecodeset(DP_EMISC, __FILE__, __LINE__);
1012     return FALSE;
1013   }
1014   if(rmdir(path) == -1){
1015     dpecodeset(DP_ERMDIR, __FILE__, __LINE__);
1016     return FALSE;
1017   }
1018   return TRUE;
1019 }
1020 
1021 
1022 /* Copy file and directories under a directory for repairing.
1023    `path' specifies a path.
1024    The return value is true if successful, else, it is false. */
crcplobdir(CURIA * curia,const char * path)1025 static int crcplobdir(CURIA *curia, const char *path){
1026   char elem[CR_PATHBUFSIZ], numbuf[3], *rp, *kbuf, *vbuf;
1027   DIR *DD;
1028   struct dirent *dp;
1029   struct stat sbuf;
1030   int i, ksiz, vsiz, fd;
1031   assert(curia && path);
1032   if(lstat(path, &sbuf) == -1){
1033     dpecodeset(DP_ESTAT, __FILE__, __LINE__);
1034     return FALSE;
1035   }
1036   if(S_ISREG(sbuf.st_mode)){
1037     rp = strrchr(path, MYPATHCHR) + 1;
1038     for(i = 0; rp[i] != '\0'; i += 2){
1039       numbuf[0] = rp[i];
1040       numbuf[1] = rp[i+1];
1041       numbuf[2] = '\0';
1042       elem[i/2] = (int)strtol(numbuf, NULL, 16);
1043     }
1044     kbuf = elem;
1045     ksiz = i / 2;
1046     vsiz = sbuf.st_size;
1047     if(!(vbuf = malloc(vsiz + 1))){
1048       dpecodeset(DP_EALLOC, __FILE__, __LINE__);
1049       return FALSE;
1050     }
1051     if((fd = open(path, O_RDONLY, CR_FILEMODE)) == -1){
1052       free(vbuf);
1053       dpecodeset(DP_EOPEN, __FILE__, __LINE__);
1054       return FALSE;
1055     }
1056     if(crread(fd, vbuf, vsiz) == -1){
1057       close(fd);
1058       free(vbuf);
1059       dpecodeset(DP_EOPEN, __FILE__, __LINE__);
1060       return FALSE;
1061     }
1062     if(!crputlob(curia, kbuf, ksiz, vbuf, vsiz, DP_DOVER)){
1063       close(fd);
1064       free(vbuf);
1065       return FALSE;
1066     }
1067     close(fd);
1068     free(vbuf);
1069     return TRUE;
1070   }
1071   if(!(DD = opendir(path))){
1072     dpecodeset(DP_EMISC, __FILE__, __LINE__);
1073     return FALSE;
1074   }
1075   while((dp = readdir(DD)) != NULL){
1076     if(!strcmp(dp->d_name, MYCDIRSTR) || !strcmp(dp->d_name, MYPDIRSTR)) continue;
1077     sprintf(elem, "%s%c%s", path, MYPATHCHR, dp->d_name);
1078     if(!crcplobdir(curia, elem)){
1079       closedir(DD);
1080       return FALSE;
1081     }
1082   }
1083   if(closedir(DD) == -1){
1084     dpecodeset(DP_EMISC, __FILE__, __LINE__);
1085     return FALSE;
1086   }
1087   return TRUE;
1088 }
1089 
1090 
1091 /* Write into a file.
1092    `fd' specifies a file descriptor.
1093    `buf' specifies a buffer to write.
1094    `size' specifies the size of the buffer.
1095    The return value is the size of the written buffer, or, -1 on failure. */
crwrite(int fd,const void * buf,int size)1096 static int crwrite(int fd, const void *buf, int size){
1097   char *lbuf;
1098   int rv, wb;
1099   assert(fd >= 0 && buf && size >= 0);
1100   lbuf = (char *)buf;
1101   rv = 0;
1102   do {
1103     wb = write(fd, lbuf, size);
1104     switch(wb){
1105     case -1: if(errno != EINTR) return -1;
1106     case 0: break;
1107     default:
1108       lbuf += wb;
1109       size -= wb;
1110       rv += wb;
1111       break;
1112     }
1113   } while(size > 0);
1114   return rv;
1115 }
1116 
1117 
1118 /* Read from a file and store the data into a buffer.
1119    `fd' specifies a file descriptor.
1120    `buffer' specifies a buffer to store into.
1121    `size' specifies the size to read with.
1122    The return value is the size read with, or, -1 on failure. */
crread(int fd,void * buf,int size)1123 static int crread(int fd, void *buf, int size){
1124   char *lbuf;
1125   int i, bs;
1126   assert(fd >= 0 && buf && size >= 0);
1127   lbuf = buf;
1128   for(i = 0; i < size && (bs = read(fd, lbuf + i, size - i)) != 0; i += bs){
1129     if(bs == -1 && errno != EINTR) return -1;
1130   }
1131   return i;
1132 }
1133 
1134 
1135 
1136 /* END OF FILE */
1137