- fix: segfault on headerFree given malicious data.
[platform/upstream/rpm.git] / rpmdb / db3.c
1 /** \ingroup db3
2  * \file rpmdb/db3.c
3  */
4
5 static int _debug = 1;  /* XXX if < 0 debugging, > 0 unusual error returns */
6
7 #include "system.h"
8
9 #if defined(HAVE_FTOK) && defined(HAVE_SYS_IPC_H)
10 #include <sys/ipc.h>
11 #endif
12
13 #include <db3/db.h>
14
15 #include <rpmlib.h>
16 #include <rpmmacro.h>
17 #include <rpmurl.h>     /* XXX urlPath proto */
18
19 #include "rpmdb.h"
20
21 #include "debug.h"
22
23 /*@access rpmdb@*/
24 /*@access dbiIndex@*/
25 /*@access dbiIndexSet@*/
26
27 /** \ingroup dbi
28  * Hash database statistics.
29  */
30 /*@-fielduse@*/
31 struct dbiHStats_s {
32     unsigned int hash_magic;    /*!< hash database magic number. */
33     unsigned int hash_version;  /*!< version of the hash database. */
34     unsigned int hash_nkeys;    /*!< no. of unique keys in the database. */
35     unsigned int hash_ndata;    /*!< no. of key/data pairs in the database. */
36     unsigned int hash_pagesize; /*!< db page (and bucket) size, in bytes. */
37     unsigned int hash_nelem;    /*!< estimated size of the hash table. */
38     unsigned int hash_ffactor;  /*!< no. of items per bucket. */
39     unsigned int hash_buckets;  /*!< no. of hash buckets. */
40     unsigned int hash_free;     /*!< no. of pages on the free list. */
41     unsigned int hash_bfree;    /*!< no. of bytes free on bucket pages. */
42     unsigned int hash_bigpages; /*!< no. of big key/data pages. */
43     unsigned int hash_big_bfree;/*!< no. of bytes free on big item pages. */
44     unsigned int hash_overflows;/*!< no. of overflow pages. */
45     unsigned int hash_ovfl_free;/*!< no. of bytes free on overflow pages. */
46     unsigned int hash_dup;      /*!< no. of duplicate pages. */
47     unsigned int hash_dup_free; /*!< no. bytes free on duplicate pages. */
48 };
49
50 /** \ingroup dbi
51  * B-tree database statistics.
52  */
53 struct dbiBStats_s {
54     unsigned int bt_magic;      /*!< btree database magic. */
55     unsigned int bt_version;    /*!< version of the btree database. */
56     unsigned int bt_nkeys;      /*!< no. of unique keys in the database. */
57     unsigned int bt_ndata;      /*!< no. of key/data pairs in the database. */
58     unsigned int bt_pagesize;   /*!< database page size, in bytes. */
59     unsigned int bt_minkey;     /*!< minimum keys per page. */
60     unsigned int bt_re_len;     /*!< length of fixed-length records. */
61     unsigned int bt_re_pad;     /*!< padding byte for fixed-length records. */
62     unsigned int bt_levels;     /*!< no. of levels in the database. */
63     unsigned int bt_int_pg;     /*!< no. of database internal pages. */
64     unsigned int bt_leaf_pg;    /*!< no. of database leaf pages. */
65     unsigned int bt_dup_pg;     /*!< no. of database duplicate pages. */
66     unsigned int bt_over_pg;    /*!< no. of database overflow pages. */
67     unsigned int bt_free;       /*!< no. of pages on the free list. */
68     unsigned int bt_int_pgfree; /*!< no. of bytes free in internal pages. */
69     unsigned int bt_leaf_pgfree;/*!< no. of bytes free in leaf pages. */
70     unsigned int bt_dup_pgfree; /*!< no. of bytes free in duplicate pages. */
71     unsigned int bt_over_pgfree;/*!< no. of bytes free in overflow pages. */
72 };
73 /*@=fielduse@*/
74
75 static int cvtdberr(dbiIndex dbi, const char * msg, int error, int printit)
76         /*@modifies fileSystem @*/
77 {
78     int rc = 0;
79
80     rc = error;
81
82     if (printit && rc) {
83         if (msg)
84             rpmError(RPMERR_DBERR, _("db%d error(%d) from %s: %s\n"),
85                 dbi->dbi_api, rc, msg, db_strerror(error));
86         else
87             rpmError(RPMERR_DBERR, _("db%d error(%d): %s\n"),
88                 dbi->dbi_api, rc, db_strerror(error));
89     }
90
91     return rc;
92 }
93
94 static int db_fini(dbiIndex dbi, const char * dbhome,
95                 /*@null@*/ const char * dbfile,
96                 /*@unused@*/ /*@null@*/ const char * dbsubfile)
97         /*@modifies dbi, fileSystem @*/
98 {
99     rpmdb rpmdb = dbi->dbi_rpmdb;
100     DB_ENV * dbenv = dbi->dbi_dbenv;
101     int rc;
102
103     if (dbenv == NULL) {
104         dbi->dbi_dbenv = NULL;
105         return 0;
106     }
107
108     rc = dbenv->close(dbenv, 0);
109     rc = cvtdberr(dbi, "dbenv->close", rc, _debug);
110
111     if (dbfile)
112         rpmMessage(RPMMESS_DEBUG, _("closed   db environment %s/%s\n"),
113                         dbhome, dbfile);
114
115     if (rpmdb->db_remove_env || dbi->dbi_tear_down) {
116         int xx;
117
118         xx = db_env_create(&dbenv, 0);
119         xx = cvtdberr(dbi, "db_env_create", rc, _debug);
120 #if DB_VERSION_MAJOR == 3 && DB_VERSION_MINOR != 0
121         xx = dbenv->remove(dbenv, dbhome, 0);
122 #else
123         xx = dbenv->remove(dbenv, dbhome, NULL, 0);
124 #endif
125         xx = cvtdberr(dbi, "dbenv->remove", rc, _debug);
126
127         if (dbfile)
128             rpmMessage(RPMMESS_DEBUG, _("removed  db environment %s/%s\n"),
129                         dbhome, dbfile);
130
131     }
132     dbi->dbi_dbenv = NULL;
133     return rc;
134 }
135
136 static int db3_fsync_disable(/*@unused@*/ int fd)
137         /*@*/
138 {
139     return 0;
140 }
141
142 static int db_init(dbiIndex dbi, const char * dbhome,
143                 /*@null@*/ const char * dbfile,
144                 /*@unused@*/ /*@null@*/ const char * dbsubfile,
145                 /*@out@*/ DB_ENV ** dbenvp)
146         /*@modifies dbi, *dbenvp, fileSystem @*/
147 {
148     rpmdb rpmdb = dbi->dbi_rpmdb;
149     DB_ENV *dbenv = NULL;
150     int eflags;
151     int rc;
152
153     if (dbenvp == NULL)
154         return 1;
155
156     /* XXX HACK */
157     /*@-assignexpose@*/
158     if (rpmdb->db_errfile == NULL)
159         rpmdb->db_errfile = stderr;
160     /*@=assignexpose@*/
161
162     eflags = (dbi->dbi_oeflags | dbi->dbi_eflags);
163     if (eflags & DB_JOINENV) eflags &= DB_JOINENV;
164
165     if (dbfile)
166         rpmMessage(RPMMESS_DEBUG, _("opening  db environment %s/%s %s\n"),
167                 dbhome, dbfile, prDbiOpenFlags(eflags, 1));
168
169     /* XXX Can't do RPC w/o host. */
170     if (dbi->dbi_host == NULL)
171         dbi->dbi_ecflags &= ~DB_CLIENT;
172
173     /* XXX Set a default shm_key. */
174     if ((dbi->dbi_eflags & DB_SYSTEM_MEM) && dbi->dbi_shmkey == 0) {
175 #if defined(HAVE_FTOK)
176         dbi->dbi_shmkey = ftok(dbhome, 0);
177 #else
178         dbi->dbi_shmkey = 0x44631380;
179 #endif
180     }
181
182     rc = db_env_create(&dbenv, dbi->dbi_ecflags);
183     rc = cvtdberr(dbi, "db_env_create", rc, _debug);
184     if (rc)
185         goto errxit;
186
187     if (dbenv == NULL)
188         return 1;
189
190   { int xx;
191     dbenv->set_errcall(dbenv, rpmdb->db_errcall);
192     dbenv->set_errfile(dbenv, rpmdb->db_errfile);
193     dbenv->set_errpfx(dbenv, rpmdb->db_errpfx);
194  /* dbenv->set_paniccall(???) */
195     (void) dbenv->set_verbose(dbenv, DB_VERB_CHKPOINT,
196                 (dbi->dbi_verbose & DB_VERB_CHKPOINT));
197     (void) dbenv->set_verbose(dbenv, DB_VERB_DEADLOCK,
198                 (dbi->dbi_verbose & DB_VERB_DEADLOCK));
199     (void) dbenv->set_verbose(dbenv, DB_VERB_RECOVERY,
200                 (dbi->dbi_verbose & DB_VERB_RECOVERY));
201     (void) dbenv->set_verbose(dbenv, DB_VERB_WAITSFOR,
202                 (dbi->dbi_verbose & DB_VERB_WAITSFOR));
203  /* dbenv->set_lg_max(???) */
204  /* dbenv->set_lk_conflicts(???) */
205  /* dbenv->set_lk_detect(???) */
206  /* dbenv->set_lk_max(???) */
207     xx = dbenv->set_mp_mmapsize(dbenv, dbi->dbi_mp_mmapsize);
208     xx = cvtdberr(dbi, "dbenv->set_mp_mmapsize", xx, _debug);
209     xx = dbenv->set_cachesize(dbenv, 0, dbi->dbi_mp_size, 0);
210     xx = cvtdberr(dbi, "dbenv->set_cachesize", xx, _debug);
211  /* dbenv->set_tx_max(???) */
212  /* dbenv->set_tx_recover(???) */
213     if (dbi->dbi_no_fsync) {
214 #if DB_VERSION_MAJOR == 3 && DB_VERSION_MINOR != 0
215         xx = db_env_set_func_fsync(db3_fsync_disable);
216 #else
217         xx = dbenv->set_func_fsync(dbenv, db3_fsync_disable);
218 #endif
219         xx = cvtdberr(dbi, "db_env_set_func_fsync", xx, _debug);
220     }
221
222 /* XXX 3.3.4 change. */
223 #if DB_VERSION_MAJOR == 3 && DB_VERSION_MINOR == 3
224     if ((dbi->dbi_ecflags & DB_CLIENT) && dbi->dbi_host) {
225         xx = dbenv->set_rpc_server(dbenv, NULL, dbi->dbi_host,
226                 dbi->dbi_cl_timeout, dbi->dbi_sv_timeout, 0);
227         xx = cvtdberr(dbi, "dbenv->set_server", xx, _debug);
228     }
229 #else
230     if ((dbi->dbi_ecflags & DB_CLIENT) && dbi->dbi_host) {
231         xx = dbenv->set_server(dbenv, dbi->dbi_host,
232                 dbi->dbi_cl_timeout, dbi->dbi_sv_timeout, 0);
233         xx = cvtdberr(dbi, "dbenv->set_server", xx, _debug);
234     }
235 #endif
236     if (dbi->dbi_shmkey) {
237         xx = dbenv->set_shm_key(dbenv, dbi->dbi_shmkey);
238         xx = cvtdberr(dbi, "dbenv->set_shm_key", xx, _debug);
239     }
240     if (dbi->dbi_tmpdir) {
241         const char * root;
242         const char * tmpdir;
243
244         root = (dbi->dbi_root ? dbi->dbi_root : rpmdb->db_root);
245         if ((root[0] == '/' && root[1] == '\0') || rpmdb->db_chrootDone)
246             root = NULL;
247         tmpdir = rpmGenPath(root, dbi->dbi_tmpdir, NULL);
248         xx = dbenv->set_tmp_dir(dbenv, tmpdir);
249         xx = cvtdberr(dbi, "dbenv->set_tmp_dir", rc, _debug);
250         tmpdir = _free(tmpdir);
251     }
252   }
253
254 #if DB_VERSION_MAJOR == 3 && DB_VERSION_MINOR != 0
255     rc = dbenv->open(dbenv, dbhome, eflags, dbi->dbi_perms);
256 #else
257     rc = dbenv->open(dbenv, dbhome, NULL, eflags, dbi->dbi_perms);
258 #endif
259     rc = cvtdberr(dbi, "dbenv->open", rc, _debug);
260     if (rc)
261         goto errxit;
262
263     *dbenvp = dbenv;
264
265     return 0;
266
267 errxit:
268     if (dbenv) {
269         int xx;
270         xx = dbenv->close(dbenv, 0);
271         xx = cvtdberr(dbi, "dbenv->close", xx, _debug);
272     }
273     return rc;
274 }
275
276 static int db3sync(dbiIndex dbi, unsigned int flags)
277         /*@modifies fileSystem @*/
278 {
279     DB * db = dbi->dbi_db;
280     int rc = 0;
281     int _printit;
282
283     if (db != NULL)
284         rc = db->sync(db, flags);
285     /* XXX DB_INCOMPLETE is returned occaisionally with multiple access. */
286     _printit = (rc == DB_INCOMPLETE ? 0 : _debug);
287     rc = cvtdberr(dbi, "db->sync", rc, _printit);
288     return rc;
289 }
290
291 static int db3c_del(dbiIndex dbi, DBC * dbcursor, u_int32_t flags)
292         /*@modifies fileSystem @*/
293 {
294     int rc;
295
296     rc = dbcursor->c_del(dbcursor, flags);
297     rc = cvtdberr(dbi, "dbcursor->c_del", rc, _debug);
298     return rc;
299 }
300
301 /*@unused@*/ static int db3c_dup(dbiIndex dbi, DBC * dbcursor, DBC ** dbcp,
302                 u_int32_t flags)
303         /*@modifies *dbcp, fileSystem @*/
304 {
305     int rc;
306
307     if (dbcp) *dbcp = NULL;
308     rc = dbcursor->c_dup(dbcursor, dbcp, flags);
309     rc = cvtdberr(dbi, "dbcursor->c_dup", rc, _debug);
310     return rc;
311 }
312
313 static int db3c_get(dbiIndex dbi, DBC * dbcursor,
314                 DBT * key, DBT * data, u_int32_t flags)
315         /*@modifies fileSystem @*/
316 {
317     int _printit;
318     int rc;
319     int rmw;
320
321 #ifdef  NOTYET
322     if ((dbi->dbi_eflags & DB_INIT_CDB) && !(dbi->dbi_oflags & DB_RDONLY))
323         rmw = DB_RMW;
324     else
325 #endif
326         rmw = 0;
327
328     rc = dbcursor->c_get(dbcursor, key, data, rmw | flags);
329
330     /* XXX DB_NOTFOUND can be returned */
331     _printit = (rc == DB_NOTFOUND ? 0 : _debug);
332     rc = cvtdberr(dbi, "dbcursor->c_get", rc, _printit);
333     return rc;
334 }
335
336 static int db3c_put(dbiIndex dbi, DBC * dbcursor,
337                 DBT * key, DBT * data, u_int32_t flags)
338         /*@modifies fileSystem @*/
339 {
340     int rc;
341
342     rc = dbcursor->c_put(dbcursor, key, data, flags);
343
344     rc = cvtdberr(dbi, "dbcursor->c_put", rc, _debug);
345     return rc;
346 }
347
348 static inline int db3c_close(dbiIndex dbi, /*@only@*/ /*@null@*/ DBC * dbcursor)
349         /*@modifies fileSystem @*/
350 {
351     int rc;
352
353     if (dbcursor == NULL) return -2;
354
355     rc = dbcursor->c_close(dbcursor);
356     rc = cvtdberr(dbi, "dbcursor->c_close", rc, _debug);
357     return rc;
358 }
359
360 static inline int db3c_open(dbiIndex dbi, /*@null@*/ /*@out@*/ DBC ** dbcp,
361                 int dbiflags)
362         /*@modifies *dbcp, fileSystem @*/
363 {
364     DB * db = dbi->dbi_db;
365     DB_TXN * txnid = NULL;
366     int flags;
367     int rc;
368
369     if (db == NULL) return -2;
370     if ((dbiflags & DBI_WRITECURSOR) &&
371         (dbi->dbi_eflags & DB_INIT_CDB) && !(dbi->dbi_oflags & DB_RDONLY))
372     {
373         flags = DB_WRITECURSOR;
374     } else
375         flags = 0;
376     if (dbcp) *dbcp = NULL;
377     rc = db->cursor(db, txnid, dbcp, flags);
378     rc = cvtdberr(dbi, "db3c_open", rc, _debug);
379
380     return rc;
381 }
382
383 static int db3cclose(dbiIndex dbi, /*@only@*/ /*@null@*/ DBC * dbcursor,
384                 unsigned int flags)
385         /*@modifies dbi, fileSystem @*/
386 {
387     int rc = 0;
388
389     /* XXX per-iterator cursors */
390     if (flags & DBI_ITERATOR)
391         return db3c_close(dbi, dbcursor);
392
393     if (!dbi->dbi_use_cursors)
394         return 0;
395
396     if (dbcursor == NULL)
397         dbcursor = dbi->dbi_rmw;
398     if (dbcursor) {
399         if (dbcursor == dbi->dbi_rmw)
400             dbi->dbi_rmw = NULL;
401         rc = db3c_close(dbi, dbcursor);
402     }
403     /*@-usereleased -compdef@*/ return rc; /*@=usereleased =compdef@*/
404 }
405
406 static int db3copen(dbiIndex dbi,
407                 /*@null@*/ /*@out@*/ DBC ** dbcp, unsigned int flags)
408         /*@modifies dbi, *dbcp, fileSystem @*/
409 {
410     DBC * dbcursor;
411     int rc = 0;
412
413     /* XXX per-iterator cursors */
414     if (flags & DBI_ITERATOR)
415         return db3c_open(dbi, dbcp, flags);
416
417     if (!dbi->dbi_use_cursors) {
418         if (dbcp) *dbcp = NULL;
419         return 0;
420     }
421
422     if ((dbcursor = dbi->dbi_rmw) == NULL) {
423         if ((rc = db3c_open(dbi, &dbcursor, flags)) == 0)
424             dbi->dbi_rmw = dbcursor;
425     }
426
427     if (dbcp)
428         /*@-onlytrans@*/ *dbcp = dbi->dbi_rmw; /*@=onlytrans@*/
429
430     return rc;
431 }
432
433 static int db3cput(dbiIndex dbi, DBC * dbcursor,
434                 const void * keyp, size_t keylen,
435                 const void * datap, size_t datalen,
436                 /*@unused@*/ unsigned int flags)
437         /*@modifies fileSystem @*/
438 {
439     DB * db = dbi->dbi_db;
440     DB_TXN * txnid = NULL;
441     DBT key, data;
442     int rc;
443
444     memset(&key, 0, sizeof(key));
445     memset(&data, 0, sizeof(data));
446     key.data = (void *)keyp;
447     key.size = keylen;
448     data.data = (void *)datap;
449     data.size = datalen;
450
451     if (dbcursor == NULL) {
452         if (db == NULL) return -2;
453         rc = db->put(db, txnid, &key, &data, 0);
454         rc = cvtdberr(dbi, "db->put", rc, _debug);
455     } else {
456
457         rc = db3c_put(dbi, dbcursor, &key, &data, DB_KEYLAST);
458
459     }
460
461     return rc;
462 }
463
464 static int db3cdel(dbiIndex dbi, DBC * dbcursor,
465                 const void * keyp, size_t keylen,
466                 /*@unused@*/ unsigned int flags)
467         /*@modifies fileSystem @*/
468 {
469     DB * db = dbi->dbi_db;
470     DB_TXN * txnid = NULL;
471     DBT key, data;
472     int rc;
473
474     memset(&key, 0, sizeof(key));
475     memset(&data, 0, sizeof(data));
476
477     key.data = (void *)keyp;
478     key.size = keylen;
479
480     if (dbcursor == NULL) {
481         if (db == NULL) return -2;
482         rc = db->del(db, txnid, &key, 0);
483         rc = cvtdberr(dbi, "db->del", rc, _debug);
484     } else {
485
486         rc = db3c_get(dbi, dbcursor, &key, &data, DB_SET);
487
488         if (rc == 0) {
489             /* XXX TODO: loop over duplicates */
490             rc = db3c_del(dbi, dbcursor, 0);
491         }
492
493     }
494
495     return rc;
496 }
497
498 static int db3cget(dbiIndex dbi, DBC * dbcursor,
499                 /*@null@*/ void ** keyp, /*@null@*/ size_t * keylen,
500                 /*@null@*/ void ** datap, /*@null@*/ size_t * datalen,
501                 /*@unused@*/ unsigned int flags)
502         /*@modifies *keyp, *keylen, *datap, *datalen, fileSystem @*/
503 {
504     DB * db = dbi->dbi_db;
505     DB_TXN * txnid = NULL;
506     DBT key, data;
507     int rc;
508
509     memset(&key, 0, sizeof(key));
510     memset(&data, 0, sizeof(data));
511     /*@-unqualifiedtrans@*/
512     if (keyp)           key.data = *keyp;
513     if (keylen)         key.size = *keylen;
514     if (datap)          data.data = *datap;
515     if (datalen)        data.size = *datalen;
516     /*@=unqualifiedtrans@*/
517
518     if (dbcursor == NULL) {
519         int _printit;
520         /*@-compmempass@*/
521         if (db == NULL) return -2;
522         /*@=compmempass@*/
523         rc = db->get(db, txnid, &key, &data, 0);
524         /* XXX DB_NOTFOUND can be returned */
525         _printit = (rc == DB_NOTFOUND ? 0 : _debug);
526         rc = cvtdberr(dbi, "db->get", rc, _printit);
527     } else {
528
529         /* XXX db3 does DB_FIRST on uninitialized cursor */
530         rc = db3c_get(dbi, dbcursor, &key, &data,
531                 key.data == NULL ? DB_NEXT : DB_SET);
532
533     }
534
535     if (rc == 0) {
536         /*@-onlytrans@*/
537         if (keyp)       *keyp = key.data;
538         if (keylen)     *keylen = key.size;
539         if (datap)      *datap = data.data;
540         if (datalen)    *datalen = data.size;
541         /*@=onlytrans@*/
542     }
543
544     /*@-compmempass -nullstate@*/
545     return rc;
546     /*@=compmempass =nullstate@*/
547 }
548
549 static int db3ccount(dbiIndex dbi, DBC * dbcursor,
550                 /*@null@*/ /*@out@*/ unsigned int * countp,
551                 /*@unused@*/ unsigned int flags)
552         /*@modifies *countp, fileSystem @*/
553 {
554     db_recno_t count = 0;
555     int rc = 0;
556
557     flags = 0;
558     rc = dbcursor->c_count(dbcursor, &count, flags);
559     rc = cvtdberr(dbi, "dbcursor->c_count", rc, _debug);
560     if (rc) return rc;
561     if (countp) *countp = count;
562
563     return rc;
564 }
565
566 static int db3byteswapped(dbiIndex dbi) /*@*/
567 {
568     DB * db = dbi->dbi_db;
569     int rc = 0;
570
571     if (db != NULL) {
572 #if DB_VERSION_MAJOR == 3 && DB_VERSION_MINOR == 3 && DB_VERSION_PATCH == 11
573         int isswapped = 0;
574         rc = db->get_byteswapped(db, &isswapped);
575         if (rc == 0)
576             rc = isswapped;
577 #else
578         rc = db->get_byteswapped(db);
579 #endif
580     }
581
582     return rc;
583 }
584
585 static int db3stat(dbiIndex dbi, unsigned int flags)
586         /*@modifies dbi, fileSystem @*/
587 {
588     DB * db = dbi->dbi_db;
589     int rc = 0;
590
591     if (db == NULL) return -2;
592 #if defined(DB_FAST_STAT)
593     if (flags)
594         flags = DB_FAST_STAT;
595     else
596 #endif
597         flags = 0;
598     dbi->dbi_stats = _free(dbi->dbi_stats);
599 /* XXX 3.3.4 change. */
600 #if DB_VERSION_MAJOR == 3 && DB_VERSION_MINOR == 3
601     rc = db->stat(db, &dbi->dbi_stats, flags);
602 #else
603     rc = db->stat(db, &dbi->dbi_stats, NULL, flags);
604 #endif
605     rc = cvtdberr(dbi, "db->stat", rc, _debug);
606     return rc;
607 }
608
609 /*
610  * XXX Doing a db->del followed by a db->put for the same record
611  * causes the nelem/page count to go awry, causing db->verify failure.
612  * Turn off the message for now.
613  */
614
615 /*@-redecl -exportheadervar@*/
616 extern int db_hash_nelem_debug;
617 /*@=redecl =exportheadervar@*/
618
619 static int db3close(/*@only@*/ dbiIndex dbi, /*@unused@*/ unsigned int flags)
620         /*@modifies dbi, fileSystem @*/
621 {
622     rpmdb rpmdb = dbi->dbi_rpmdb;
623     const char * urlfn = NULL;
624     const char * root;
625     const char * home;
626     const char * dbhome;
627     const char * dbfile;
628     const char * dbsubfile;
629     DB * db = dbi->dbi_db;
630     int rc = 0, xx;
631
632     flags = 0;  /* XXX unused */
633
634     /*
635      * Get the prefix/root component and directory path.
636      */
637     root = (dbi->dbi_root ? dbi->dbi_root : rpmdb->db_root);
638     if ((root[0] == '/' && root[1] == '\0') || rpmdb->db_chrootDone)
639         root = NULL;
640     home = (dbi->dbi_home ? dbi->dbi_home : rpmdb->db_home);
641
642     /*
643      * Either the root or directory components may be a URL. Concatenate,
644      * convert the URL to a path, and add the name of the file.
645      */
646     urlfn = rpmGenPath(root, home, NULL);
647     (void) urlPath(urlfn, &dbhome);
648     if (dbi->dbi_temporary) {
649         dbfile = NULL;
650         dbsubfile = NULL;
651     } else {
652 #ifdef  HACK
653         dbfile = (dbi->dbi_file ? dbi->dbi_file : db3basename);
654         dbsubfile = (dbi->dbi_subfile ? dbi->dbi_subfile : tagName(dbi->dbi_rpmtag));
655 #else
656         dbfile = (dbi->dbi_file ? dbi->dbi_file : tagName(dbi->dbi_rpmtag));
657         dbsubfile = NULL;
658 #endif
659     }
660
661     if (dbi->dbi_rmw)
662         rc = db3cclose(dbi, NULL, 0);
663
664     if (db) {
665         rc = db->close(db, 0);
666         rc = cvtdberr(dbi, "db->close", rc, _debug);
667         db = dbi->dbi_db = NULL;
668
669         rpmMessage(RPMMESS_DEBUG, _("closed   db index       %s/%s\n"),
670                 dbhome, (dbfile ? dbfile : tagName(dbi->dbi_rpmtag)));
671
672     }
673
674     dbi->dbi_dbinfo = _free(dbi->dbi_dbinfo);
675
676     if (dbi->dbi_use_dbenv) {
677         /*@-nullstate@*/
678         xx = db_fini(dbi, (dbhome ? dbhome : ""), dbfile, dbsubfile);
679         /*@=nullstate@*/
680     }
681
682     if (dbi->dbi_verify_on_close && !dbi->dbi_temporary) {
683         DB_ENV * dbenv = NULL;
684
685         rc = db_env_create(&dbenv, 0);
686         rc = cvtdberr(dbi, "db_env_create", rc, _debug);
687         if (rc || dbenv == NULL) goto exit;
688
689         dbenv->set_errcall(dbenv, rpmdb->db_errcall);
690         dbenv->set_errfile(dbenv, rpmdb->db_errfile);
691         dbenv->set_errpfx(dbenv, rpmdb->db_errpfx);
692  /*     dbenv->set_paniccall(???) */
693         (void) dbenv->set_verbose(dbenv, DB_VERB_CHKPOINT,
694                 (dbi->dbi_verbose & DB_VERB_CHKPOINT));
695         (void) dbenv->set_verbose(dbenv, DB_VERB_DEADLOCK,
696                 (dbi->dbi_verbose & DB_VERB_DEADLOCK));
697         (void) dbenv->set_verbose(dbenv, DB_VERB_RECOVERY,
698                 (dbi->dbi_verbose & DB_VERB_RECOVERY));
699         (void) dbenv->set_verbose(dbenv, DB_VERB_WAITSFOR,
700                 (dbi->dbi_verbose & DB_VERB_WAITSFOR));
701
702         if (dbi->dbi_tmpdir) {
703             const char * tmpdir = rpmGenPath(root, dbi->dbi_tmpdir, NULL);
704             rc = dbenv->set_tmp_dir(dbenv, tmpdir);
705             rc = cvtdberr(dbi, "dbenv->set_tmp_dir", rc, _debug);
706             tmpdir = _free(tmpdir);
707             if (rc) goto exit;
708         }
709             
710         rc = dbenv->open(dbenv, dbhome,
711             DB_CREATE | DB_INIT_MPOOL | DB_PRIVATE | DB_USE_ENVIRON, 0);
712         rc = cvtdberr(dbi, "dbenv->open", rc, _debug);
713         if (rc) goto exit;
714
715         rc = db_create(&db, dbenv, 0);
716         rc = cvtdberr(dbi, "db_create", rc, _debug);
717
718         if (db != NULL) {
719                 const char * dbf = rpmGetPath(dbhome, "/", dbfile, NULL);
720
721                 rc = db->verify(db, dbf, NULL, NULL, flags);
722                 rc = cvtdberr(dbi, "db->verify", rc, _debug);
723
724                 rpmMessage(RPMMESS_DEBUG, _("verified db index       %s/%s\n"),
725                         (dbhome ? dbhome : ""),
726                         (dbfile ? dbfile : tagName(dbi->dbi_rpmtag)));
727
728                 xx = db->close(db, 0);
729                 xx = cvtdberr(dbi, "db->close", xx, _debug);
730                 db = NULL;
731                 if (rc == 0 && xx) rc = xx;
732
733                 dbf = _free(dbf);
734         }
735         xx = dbenv->close(dbenv, 0);
736         xx = cvtdberr(dbi, "dbenv->close", xx, _debug);
737         if (rc == 0 && xx) rc = xx;
738     }
739
740 exit:
741     dbi->dbi_db = NULL;
742
743     urlfn = _free(urlfn);
744
745     dbi = db3Free(dbi);
746
747     return rc;
748 }
749
750 static int db3open(/*@keep@*/ rpmdb rpmdb, int rpmtag, dbiIndex * dbip)
751         /*@modifies *dbip, fileSystem @*/
752 {
753     /*@-nestedextern@*/
754     extern struct _dbiVec db3vec;
755     /*@=nestedextern@*/
756     const char * urlfn = NULL;
757     const char * root;
758     const char * home;
759     const char * dbhome;
760     const char * dbfile;
761     const char * dbsubfile;
762     dbiIndex dbi = NULL;
763     int rc = 0;
764     int xx;
765
766     DB * db = NULL;
767     DB_ENV * dbenv = NULL;
768     DB_TXN * txnid = NULL;
769     u_int32_t oflags;
770     int _printit;
771
772     if (dbip)
773         *dbip = NULL;
774
775     /*
776      * Parse db configuration parameters.
777      */
778     if ((dbi = db3New(rpmdb, rpmtag)) == NULL)
779         /*@-nullstate@*/
780         return 1;
781         /*@=nullstate@*/
782     dbi->dbi_api = DB_VERSION_MAJOR;
783
784     /*
785      * Get the prefix/root component and directory path.
786      */
787     root = (dbi->dbi_root ? dbi->dbi_root : rpmdb->db_root);
788     if ((root[0] == '/' && root[1] == '\0') || rpmdb->db_chrootDone)
789         root = NULL;
790     home = (dbi->dbi_home ? dbi->dbi_home : rpmdb->db_home);
791
792     /*
793      * Either the root or directory components may be a URL. Concatenate,
794      * convert the URL to a path, and add the name of the file.
795      */
796     urlfn = rpmGenPath(root, home, NULL);
797     (void) urlPath(urlfn, &dbhome);
798     if (dbi->dbi_temporary) {
799         dbfile = NULL;
800         dbsubfile = NULL;
801     } else {
802 #ifdef  HACK
803         dbfile = (dbi->dbi_file ? dbi->dbi_file : db3basename);
804         dbsubfile = (dbi->dbi_subfile ? dbi->dbi_subfile : tagName(dbi->dbi_rpmtag));
805 #else
806         dbfile = (dbi->dbi_file ? dbi->dbi_file : tagName(dbi->dbi_rpmtag));
807         dbsubfile = NULL;
808 #endif
809     }
810
811     oflags = (dbi->dbi_oeflags | dbi->dbi_oflags);
812     oflags &= ~DB_TRUNCATE;     /* XXX this is dangerous */
813
814 #if 0   /* XXX rpmdb: illegal flag combination specified to DB->open */
815     if ( dbi->dbi_mode & O_EXCL) oflags |= DB_EXCL;
816 #endif
817
818     /*
819      * Map open mode flags onto configured database/environment flags.
820      */
821     if (dbi->dbi_temporary) {
822         oflags |= DB_CREATE;
823         dbi->dbi_oeflags |= DB_CREATE;
824         oflags &= ~DB_RDONLY;
825         dbi->dbi_oflags &= ~DB_RDONLY;
826     } else {
827         if (!(dbi->dbi_mode & (O_RDWR|O_WRONLY))) oflags |= DB_RDONLY;
828         if (dbi->dbi_mode & O_CREAT) {
829             oflags |= DB_CREATE;
830             dbi->dbi_oeflags |= DB_CREATE;
831         }
832 #ifdef  DANGEROUS
833         if ( dbi->dbi_mode & O_TRUNC) oflags |= DB_TRUNCATE;
834 #endif
835     }
836
837     /*
838      * Avoid incompatible DB_CREATE/DB_RDONLY flags on DBENV->open.
839      */
840     if (dbi->dbi_use_dbenv) {
841         if (access(dbhome, W_OK) == -1) {
842
843             /* dbhome is unwritable, don't attempt DB_CREATE on DB->open ... */
844             oflags &= ~DB_CREATE;
845
846             /* ... but DBENV->open might still need DB_CREATE ... */
847             if (dbi->dbi_eflags & DB_PRIVATE) {
848                 dbi->dbi_eflags &= ~DB_JOINENV;
849             } else {
850                 dbi->dbi_eflags |= DB_JOINENV;
851                 dbi->dbi_oeflags &= ~DB_CREATE;
852                 dbi->dbi_oeflags &= ~DB_THREAD;
853                 /* ... but, unless DB_PRIVATE is used, skip DBENV. */
854                 dbi->dbi_use_dbenv = 0;
855             }
856
857             /* ... DB_RDONLY maps dphome perms across files ...  */
858             if (dbi->dbi_temporary) {
859                 oflags |= DB_CREATE;
860                 dbi->dbi_oeflags |= DB_CREATE;
861                 oflags &= ~DB_RDONLY;
862                 dbi->dbi_oflags &= ~DB_RDONLY;
863             } else {
864                 oflags |= DB_RDONLY;
865                 /* ... and DB_WRITECURSOR won't be needed ...  */
866                 dbi->dbi_oflags |= DB_RDONLY;
867             }
868
869         } else {        /* dbhome is writable, check for persistent dbenv. */
870             const char * dbf = rpmGetPath(dbhome, "/__db.001", NULL);
871
872             if (access(dbf, F_OK) == -1) {
873                 /* ... non-existent (or unwritable) DBENV, will create ... */
874                 dbi->dbi_oeflags |= DB_CREATE;
875                 dbi->dbi_eflags &= ~DB_JOINENV;
876             } else {
877                 /* ... pre-existent (or bogus) DBENV, will join ... */
878                 dbi->dbi_oeflags &= ~DB_CREATE;
879                 dbi->dbi_eflags |= DB_JOINENV;
880             }
881             dbf = _free(dbf);
882         }
883     }
884
885     /*
886      * Avoid incompatible DB_CREATE/DB_RDONLY flags on DB->open.
887      */
888     if ((oflags & DB_CREATE) && (oflags & DB_RDONLY)) {
889         /* dbhome is writable, and DB->open flags may conflict. */
890         const char * dbfn = (dbfile ? dbfile : tagName(dbi->dbi_rpmtag));
891         const char * dbf = rpmGetPath(dbhome, "/", dbfn, NULL);
892
893         if (access(dbf, F_OK) == -1) {
894             /* File does not exist, DB->open might create ... */
895             oflags &= ~DB_RDONLY;
896         } else {
897             /* File exists, DB->open need not create ... */
898             oflags &= ~DB_CREATE;
899         }
900
901         /* Only writers need DB_WRITECURSOR ... */
902         if (!(oflags & DB_RDONLY) && access(dbf, W_OK) == 0) {
903             dbi->dbi_oflags &= ~DB_RDONLY;
904         } else {
905             dbi->dbi_oflags |= DB_RDONLY;
906         }
907         dbf = _free(dbf);
908     }
909
910     /*
911      * Turn off verify-on-close if opening read-only.
912      */
913     if (oflags & DB_RDONLY)
914         dbi->dbi_verify_on_close = 0;
915
916     dbi->dbi_dbinfo = NULL;
917
918     if (dbi->dbi_use_dbenv)
919         rc = db_init(dbi, dbhome, dbfile, dbsubfile, &dbenv);
920
921     rpmMessage(RPMMESS_DEBUG, _("opening  db index       %s/%s %s mode=0x%x\n"),
922                 dbhome, (dbfile ? dbfile : tagName(dbi->dbi_rpmtag)),
923                 prDbiOpenFlags(oflags, 0), dbi->dbi_mode);
924
925     if (rc == 0) {
926         static int _lockdbfd = 0;
927
928         rc = db_create(&db, dbenv, dbi->dbi_cflags);
929         rc = cvtdberr(dbi, "db_create", rc, _debug);
930         if (rc == 0 && db != NULL) {
931             if (rc == 0 && dbi->dbi_lorder) {
932                 rc = db->set_lorder(db, dbi->dbi_lorder);
933                 rc = cvtdberr(dbi, "db->set_lorder", rc, _debug);
934             }
935             if (rc == 0 && dbi->dbi_cachesize) {
936                 rc = db->set_cachesize(db, 0, dbi->dbi_cachesize, 0);
937                 rc = cvtdberr(dbi, "db->set_cachesize", rc, _debug);
938             }
939             if (rc == 0 && dbi->dbi_pagesize) {
940                 rc = db->set_pagesize(db, dbi->dbi_pagesize);
941                 rc = cvtdberr(dbi, "db->set_pagesize", rc, _debug);
942             }
943 /* XXX 3.3.4 change. */
944 #if DB_VERSION_MAJOR == 3 && DB_VERSION_MINOR == 3
945             if (rc == 0 &&
946                         rpmdb->db_malloc && rpmdb->db_realloc && rpmdb->db_free)
947             {
948                 rc = db->set_alloc(db,
949                         rpmdb->db_malloc, rpmdb->db_realloc, rpmdb->db_free);
950                 rc = cvtdberr(dbi, "db->set_alloc", rc, _debug);
951             }
952 #else
953             if (rc == 0 && rpmdb->db_malloc) {
954                 rc = db->set_malloc(db, rpmdb->db_malloc);
955                 rc = cvtdberr(dbi, "db->set_malloc", rc, _debug);
956             }
957 #endif
958             if (rc == 0 && oflags & DB_CREATE) {
959                 switch(dbi->dbi_type) {
960                 default:
961                 case DB_HASH:
962                     if (dbi->dbi_h_ffactor) {
963                         rc = db->set_h_ffactor(db, dbi->dbi_h_ffactor);
964                         rc = cvtdberr(dbi, "db->set_h_ffactor", rc, _debug);
965                         if (rc) break;
966                     }
967                     if (dbi->dbi_h_nelem) {
968                         rc = db->set_h_nelem(db, dbi->dbi_h_nelem);
969                         rc = cvtdberr(dbi, "db->set_h_nelem", rc, _debug);
970                         if (rc) break;
971                     }
972                     if (dbi->dbi_h_flags) {
973                         rc = db->set_flags(db, dbi->dbi_h_flags);
974                         rc = cvtdberr(dbi, "db->set_h_flags", rc, _debug);
975                         if (rc) break;
976                     }
977 /* XXX db-3.2.9 has added a DB arg to the call. */
978 #if DB_VERSION_MAJOR == 3 && DB_VERSION_MINOR > 2
979                     if (dbi->dbi_h_hash_fcn) {
980                         rc = db->set_h_hash(db, dbi->dbi_h_hash_fcn);
981                         rc = cvtdberr(dbi, "db->set_h_hash", rc, _debug);
982                         if (rc) break;
983                     }
984                     if (dbi->dbi_h_dup_compare_fcn) {
985                         rc = db->set_dup_compare(db, dbi->dbi_h_dup_compare_fcn);
986                         rc = cvtdberr(dbi, "db->set_dup_compare", rc, _debug);
987                         if (rc) break;
988                     }
989 #endif
990                     break;
991                 case DB_BTREE:
992                     if (dbi->dbi_bt_flags) {
993                         rc = db->set_flags(db, dbi->dbi_bt_flags);
994                         rc = cvtdberr(dbi, "db->set_bt_flags", rc, _debug);
995                         if (rc) break;
996                     }
997                     if (dbi->dbi_bt_minkey) {
998                         rc = db->set_bt_minkey(db, dbi->dbi_bt_minkey);
999                         rc = cvtdberr(dbi, "db->set_bt_minkey", rc, _debug);
1000                         if (rc) break;
1001                     }
1002 /* XXX db-3.2.9 has added a DB arg to the call. */
1003 #if DB_VERSION_MAJOR == 3 && DB_VERSION_MINOR > 2
1004                     if (dbi->dbi_bt_compare_fcn) {
1005                         rc = db->set_bt_compare(db, dbi->dbi_bt_compare_fcn);
1006                         rc = cvtdberr(dbi, "db->set_bt_compare", rc, _debug);
1007                         if (rc) break;
1008                     }
1009                     if (dbi->dbi_bt_dup_compare_fcn) {
1010                         rc = db->set_dup_compare(db, dbi->dbi_bt_dup_compare_fcn);
1011                         rc = cvtdberr(dbi, "db->set_dup_compare", rc, _debug);
1012                         if (rc) break;
1013                     }
1014                     if (dbi->dbi_bt_prefix_fcn) {
1015                         rc = db->set_bt_prefix(db, dbi->dbi_bt_prefix_fcn);
1016                         rc = cvtdberr(dbi, "db->set_bt_prefix", rc, _debug);
1017                         if (rc) break;
1018                     }
1019 #endif
1020                     break;
1021                 case DB_RECNO:
1022                     if (dbi->dbi_re_delim) {
1023                         rc = db->set_re_delim(db, dbi->dbi_re_delim);
1024                         rc = cvtdberr(dbi, "db->set_re_selim", rc, _debug);
1025                         if (rc) break;
1026                     }
1027                     if (dbi->dbi_re_len) {
1028                         rc = db->set_re_len(db, dbi->dbi_re_len);
1029                         rc = cvtdberr(dbi, "db->set_re_len", rc, _debug);
1030                         if (rc) break;
1031                     }
1032                     if (dbi->dbi_re_pad) {
1033                         rc = db->set_re_pad(db, dbi->dbi_re_pad);
1034                         rc = cvtdberr(dbi, "db->set_re_pad", rc, _debug);
1035                         if (rc) break;
1036                     }
1037                     if (dbi->dbi_re_source) {
1038                         rc = db->set_re_source(db, dbi->dbi_re_source);
1039                         rc = cvtdberr(dbi, "db->set_re_source", rc, _debug);
1040                         if (rc) break;
1041                     }
1042                     break;
1043                 case DB_QUEUE:
1044                     if (dbi->dbi_q_extentsize) {
1045                         rc = db->set_q_extentsize(db, dbi->dbi_q_extentsize);
1046                         rc = cvtdberr(dbi, "db->set_q_extentsize", rc, _debug);
1047                         if (rc) break;
1048                     }
1049                     break;
1050                 }
1051             }
1052             dbi->dbi_dbinfo = NULL;
1053
1054             if (rc == 0) {
1055                 const char * dbfullpath;
1056                 const char * dbpath;
1057                 char * t;
1058                 int nb;
1059
1060                 nb = strlen(dbhome);
1061                 if (dbfile)     nb += 1 + strlen(dbfile);
1062                 dbfullpath = t = alloca(nb + 1);
1063
1064                 t = stpcpy(t, dbhome);
1065                 if (dbfile)
1066                     t = stpcpy( stpcpy( t, "/"), dbfile);
1067                 dbpath = (!dbi->dbi_use_dbenv && !dbi->dbi_temporary)
1068                         ? dbfullpath : dbfile;
1069
1070                 rc = db->open(db, dbpath, dbsubfile,
1071                     dbi->dbi_type, oflags, dbi->dbi_perms);
1072
1073                 if (rc == 0 && dbi->dbi_type == DB_UNKNOWN) {
1074 #if DB_VERSION_MAJOR == 3 && DB_VERSION_MINOR == 3 && DB_VERSION_PATCH == 11
1075                     DBTYPE dbi_type = DB_UNKNOWN;
1076                     xx = db->get_type(db, &dbi_type);
1077                     if (xx == 0)
1078                         dbi->dbi_type = dbi_type;
1079 #else
1080                     dbi->dbi_type = db->get_type(db);
1081 #endif
1082                 }
1083             }
1084
1085             /* XXX return rc == errno without printing */
1086             _printit = (rc > 0 ? 0 : _debug);
1087             xx = cvtdberr(dbi, "db->open", rc, _printit);
1088
1089             if (rc == 0 && dbi->dbi_use_dbenv
1090             && (dbi->dbi_eflags & DB_INIT_CDB) && dbi->dbi_get_rmw_cursor)
1091             {
1092                 DBC * dbcursor = NULL;
1093                 xx = db->cursor(db, txnid, &dbcursor,
1094                         ((oflags & DB_RDONLY) ? 0 : DB_WRITECURSOR));
1095                 xx = cvtdberr(dbi, "db->cursor", xx, _debug);
1096                 dbi->dbi_rmw = dbcursor;
1097             } else
1098                 dbi->dbi_rmw = NULL;
1099
1100             /*
1101              * Lock a file using fcntl(2). Traditionally this is Packages,
1102              * the file used * to store metadata of installed header(s),
1103              * as Packages is always opened, and should be opened first,
1104              * for any rpmdb access.
1105              *
1106              * If no DBENV is used, then access is protected with a
1107              * shared/exclusive locking scheme, as always.
1108              *
1109              * With a DBENV, the fcntl(2) lock is necessary only to keep
1110              * the riff-raff from playing where they don't belong, as
1111              * the DBENV should provide it's own locking scheme. So try to
1112              * acquire a lock, but permit failures, as some other
1113              * DBENV player may already have acquired the lock.
1114              */
1115             if (rc == 0 && dbi->dbi_lockdbfd &&
1116                 (!dbi->dbi_use_dbenv || _lockdbfd++ == 0))
1117             {
1118                 int fdno = -1;
1119
1120                 if (!(db->fd(db, &fdno) == 0 && fdno >= 0)) {
1121                     rc = 1;
1122                 } else {
1123                     struct flock l;
1124                     memset(&l, 0, sizeof(l));
1125                     l.l_whence = 0;
1126                     l.l_start = 0;
1127                     l.l_len = 0;
1128                     l.l_type = (dbi->dbi_mode & (O_RDWR|O_WRONLY))
1129                                 ? F_WRLCK : F_RDLCK;
1130                     l.l_pid = 0;
1131
1132                     rc = fcntl(fdno, F_SETLK, (void *) &l);
1133                     if (rc) {
1134                         /* Warning only if using CDB locking. */
1135                         rc = ((dbi->dbi_use_dbenv &&
1136                                 (dbi->dbi_eflags & DB_INIT_CDB))
1137                             ? 0 : 1);
1138                         rpmError( (rc ? RPMERR_FLOCK : RPMWARN_FLOCK),
1139                                 _("cannot get %s lock on %s/%s\n"),
1140                                 ((dbi->dbi_mode & (O_RDWR|O_WRONLY))
1141                                         ? _("exclusive") : _("shared")),
1142                                 dbhome, (dbfile ? dbfile : ""));
1143                     } else if (dbfile) {
1144                         rpmMessage(RPMMESS_DEBUG,
1145                                 _("locked   db index       %s/%s\n"),
1146                                 dbhome, dbfile);
1147                     }
1148                 }
1149             }
1150         }
1151     }
1152
1153     dbi->dbi_db = db;
1154     dbi->dbi_dbenv = dbenv;
1155
1156     if (rc == 0 && dbi->dbi_db != NULL && dbip != NULL) {
1157         dbi->dbi_vec = &db3vec;
1158         *dbip = dbi;
1159     } else {
1160         dbi->dbi_verify_on_close = 0;
1161         (void) db3close(dbi, 0);
1162     }
1163
1164     urlfn = _free(urlfn);
1165
1166     /*@-nullstate@*/
1167     return rc;
1168     /*@=nullstate@*/
1169 }
1170
1171 /** \ingroup db3
1172  */
1173 /*@-exportheadervar@*/
1174 struct _dbiVec db3vec = {
1175     DB_VERSION_MAJOR, DB_VERSION_MINOR, DB_VERSION_PATCH,
1176     db3open, db3close, db3sync, db3copen, db3cclose, db3cdel, db3cget, db3cput,
1177     db3ccount, db3byteswapped, db3stat
1178 };
1179 /*@=exportheadervar@*/