File: | lib/backend/db3.c |
Warning: | line 188, column 2 Value stored to 'xx' is never read |
1 | /** \ingroup rpmdb |
2 | * \file lib/db3.c |
3 | */ |
4 | |
5 | static int _debug = 1; /* XXX if < 0 debugging, > 0 unusual error returns */ |
6 | |
7 | #include "system.h" |
8 | |
9 | #include <errno(*__errno_location ()).h> |
10 | #include <sys/wait.h> |
11 | #include <popt.h> |
12 | #include <db.h> |
13 | #include <signal.h> |
14 | |
15 | #include <rpm/rpmtypes.h> |
16 | #include <rpm/rpmmacro.h> |
17 | #include <rpm/rpmfileutil.h> |
18 | #include <rpm/rpmlog.h> |
19 | |
20 | #include "lib/rpmdb_internal.h" |
21 | |
22 | #include "debug.h" |
23 | |
24 | static const char * _errpfx = "rpmdb"; |
25 | |
26 | struct dbiCursor_s { |
27 | dbiIndex dbi; |
28 | const void *key; |
29 | unsigned int keylen; |
30 | int flags; |
31 | DBC *cursor; |
32 | }; |
33 | |
34 | static struct dbiConfig_s staticdbicfg; |
35 | static struct dbConfig_s staticcfg; |
36 | |
37 | /** \ingroup dbi |
38 | */ |
39 | static const struct poptOption rdbOptions[] = { |
40 | /* Environment options */ |
41 | |
42 | { "cdb", 0,POPT_BIT_SET(7U|0x08000000U), &staticcfg.db_eflags, DB_INIT_CDB0x00000080, |
43 | NULL((void*)0), NULL((void*)0) }, |
44 | { "lock", 0,POPT_BIT_SET(7U|0x08000000U), &staticcfg.db_eflags, DB_INIT_LOCK0x00000100, |
45 | NULL((void*)0), NULL((void*)0) }, |
46 | { "log", 0,POPT_BIT_SET(7U|0x08000000U), &staticcfg.db_eflags, DB_INIT_LOG0x00000200, |
47 | NULL((void*)0), NULL((void*)0) }, |
48 | { "txn", 0,POPT_BIT_SET(7U|0x08000000U), &staticcfg.db_eflags, DB_INIT_TXN0x00002000, |
49 | NULL((void*)0), NULL((void*)0) }, |
50 | { "recover", 0,POPT_BIT_SET(7U|0x08000000U), &staticcfg.db_eflags, DB_RECOVER0x00000002, |
51 | NULL((void*)0), NULL((void*)0) }, |
52 | { "recover_fatal", 0,POPT_BIT_SET(7U|0x08000000U), &staticcfg.db_eflags, DB_RECOVER_FATAL0x00020000, |
53 | NULL((void*)0), NULL((void*)0) }, |
54 | { "lockdown", 0,POPT_BIT_SET(7U|0x08000000U), &staticcfg.db_eflags, DB_LOCKDOWN0x00004000, |
55 | NULL((void*)0), NULL((void*)0) }, |
56 | { "private", 0,POPT_BIT_SET(7U|0x08000000U), &staticcfg.db_eflags, DB_PRIVATE0x00010000, |
57 | NULL((void*)0), NULL((void*)0) }, |
58 | |
59 | { "deadlock", 0,POPT_BIT_SET(7U|0x08000000U), &staticcfg.db_verbose, DB_VERB_DEADLOCK0x00000002, |
60 | NULL((void*)0), NULL((void*)0) }, |
61 | { "recovery", 0,POPT_BIT_SET(7U|0x08000000U), &staticcfg.db_verbose, DB_VERB_RECOVERY0x00000010, |
62 | NULL((void*)0), NULL((void*)0) }, |
63 | { "waitsfor", 0,POPT_BIT_SET(7U|0x08000000U), &staticcfg.db_verbose, DB_VERB_WAITSFOR0x00010000, |
64 | NULL((void*)0), NULL((void*)0) }, |
65 | { "verbose", 0,POPT_ARG_VAL7U, &staticcfg.db_verbose, -1, |
66 | NULL((void*)0), NULL((void*)0) }, |
67 | |
68 | { "cachesize", 0,POPT_ARG_INT2U, &staticcfg.db_cachesize, 0, |
69 | NULL((void*)0), NULL((void*)0) }, |
70 | { "mmapsize", 0,POPT_ARG_INT2U, &staticcfg.db_mmapsize, 0, |
71 | NULL((void*)0), NULL((void*)0) }, |
72 | { "mp_mmapsize", 0,POPT_ARG_INT2U, &staticcfg.db_mmapsize, 0, |
73 | NULL((void*)0), NULL((void*)0) }, |
74 | { "mp_size", 0,POPT_ARG_INT2U, &staticcfg.db_cachesize, 0, |
75 | NULL((void*)0), NULL((void*)0) }, |
76 | |
77 | { "nofsync", 0,POPT_ARG_NONE0U, &staticcfg.db_no_fsync, 0, |
78 | NULL((void*)0), NULL((void*)0) }, |
79 | |
80 | /* Per-dbi options */ |
81 | { "nommap", 0,POPT_BIT_SET(7U|0x08000000U), &staticdbicfg.dbi_oflags, DB_NOMMAP0x00000010, |
82 | NULL((void*)0), NULL((void*)0) }, |
83 | |
84 | { "nodbsync", 0,POPT_ARG_NONE0U, &staticdbicfg.dbi_no_dbsync, 0, |
85 | NULL((void*)0), NULL((void*)0) }, |
86 | { "lockdbfd", 0,POPT_ARG_NONE0U, &staticdbicfg.dbi_lockdbfd, 0, |
87 | NULL((void*)0), NULL((void*)0) }, |
88 | |
89 | POPT_TABLEEND{ ((void*)0), '\0', 0, ((void*)0), 0, ((void*)0), ((void*)0) } |
90 | }; |
91 | |
92 | |
93 | static int dbapi_err(rpmdb rdb, const char * msg, int error, int printit) |
94 | { |
95 | if (printit && error) { |
96 | if (msg) |
97 | rpmlog(RPMLOG_ERR, _("%s error(%d) from %s: %s\n")dcgettext ("rpm", "%s error(%d) from %s: %s\n", 5), |
98 | rdb->db_descr, error, msg, db_strerror(error)); |
99 | else |
100 | rpmlog(RPMLOG_ERR, _("%s error(%d): %s\n")dcgettext ("rpm", "%s error(%d): %s\n", 5), |
101 | rdb->db_descr, error, db_strerror(error)); |
102 | } |
103 | return error; |
104 | } |
105 | |
106 | static int cvtdberr(dbiIndex dbi, const char * msg, int error, int printit) |
107 | { |
108 | return dbapi_err(dbi->dbi_rpmdb, msg, error, printit); |
109 | } |
110 | |
111 | static void errlog(const DB_ENV * env, const char *errpfx, const char *msg) |
112 | { |
113 | rpmlog(RPMLOG_ERR, "%s: %s\n", errpfx, msg); |
114 | } |
115 | |
116 | static void warnlog(const DB_ENV *env, const char *msg) |
117 | { |
118 | rpmlog(RPMLOG_WARNING, "%s: %s\n", _errpfx, msg); |
119 | } |
120 | |
121 | static uint32_t db_envflags(DB * db) |
122 | { |
123 | DB_ENV * env = db->get_env(db); |
124 | uint32_t eflags = 0; |
125 | (void) env->get_open_flags(env, &eflags); |
126 | return eflags; |
127 | } |
128 | |
129 | /* |
130 | * Try to acquire db environment open/close serialization lock. |
131 | * Return the open, locked fd on success, -1 on failure. |
132 | */ |
133 | static int serialize_env(const char *dbhome) |
134 | { |
135 | char *lock_path = rstrscat(NULL((void*)0), dbhome, "/.dbenv.lock", NULL((void*)0)); |
136 | mode_t oldmask = umask(022); |
137 | int fd = open(lock_path, (O_RDWR02|O_CREAT0100), 0644); |
138 | umask(oldmask); |
139 | |
140 | if (fd >= 0) { |
141 | int rc; |
142 | struct flock info; |
143 | memset(&info, 0, sizeof(info)); |
144 | info.l_type = F_WRLCK1; |
145 | info.l_whence = SEEK_SET0; |
146 | do { |
147 | rc = fcntl(fd, F_SETLKW7, &info); |
148 | } while (rc == -1 && errno(*__errno_location ()) == EINTR4); |
149 | |
150 | if (rc == -1) { |
151 | close(fd); |
152 | fd = -1; |
153 | } |
154 | } |
155 | |
156 | free(lock_path); |
157 | return fd; |
158 | } |
159 | |
160 | static int db_fini(rpmdb rdb, const char * dbhome) |
161 | { |
162 | DB_ENV * dbenv = rdb->db_dbenv; |
163 | int rc; |
164 | int lockfd = -1; |
165 | uint32_t eflags = 0; |
166 | |
167 | if (dbenv == NULL((void*)0)) |
168 | return 0; |
169 | |
170 | if (rdb->db_opens > 1) { |
171 | rdb->db_opens--; |
172 | return 0; |
173 | } |
174 | |
175 | (void) dbenv->get_open_flags(dbenv, &eflags); |
176 | if (!(eflags & DB_PRIVATE0x00010000)) |
177 | lockfd = serialize_env(dbhome); |
178 | |
179 | rc = dbenv->close(dbenv, 0); |
180 | rc = dbapi_err(rdb, "dbenv->close", rc, _debug); |
181 | |
182 | rpmlog(RPMLOG_DEBUG, "closed db environment %s\n", dbhome); |
183 | |
184 | if (!(eflags & DB_PRIVATE0x00010000) && rdb->db_remove_env) { |
185 | int xx; |
186 | |
187 | xx = db_env_create(&dbenv, 0); |
188 | xx = dbapi_err(rdb, "db_env_create", xx, _debug); |
Value stored to 'xx' is never read | |
189 | xx = dbenv->remove(dbenv, dbhome, 0); |
190 | /* filter out EBUSY as it just means somebody else gets to clean it */ |
191 | xx = dbapi_err(rdb, "dbenv->remove", xx, (xx == EBUSY16 ? 0 : _debug)); |
192 | |
193 | rpmlog(RPMLOG_DEBUG, "removed db environment %s\n", dbhome); |
194 | |
195 | } |
196 | |
197 | if (lockfd >= 0) |
198 | close(lockfd); |
199 | |
200 | return rc; |
201 | } |
202 | |
203 | static int fsync_disable(int fd) |
204 | { |
205 | return 0; |
206 | } |
207 | |
208 | /* |
209 | * dbenv->failchk() callback method for determining is the given pid/tid |
210 | * is alive. We only care about pid's though. |
211 | */ |
212 | static int isalive(DB_ENV *dbenv, pid_t pid, db_threadid_t tid, uint32_t flags) |
213 | { |
214 | int alive = 0; |
215 | |
216 | if (pid == getpid()) { |
217 | alive = 1; |
218 | } else if (kill(pid, 0) == 0) { |
219 | alive = 1; |
220 | /* only existing processes can fail with EPERM */ |
221 | } else if (errno(*__errno_location ()) == EPERM1) { |
222 | alive = 1; |
223 | } |
224 | |
225 | return alive; |
226 | } |
227 | |
228 | |
229 | static void dbConfigure(rpmDbiTagVal rpmtag, struct dbConfig_s *cfg, struct dbiConfig_s *dbicfg) |
230 | { |
231 | char *dbOpts; |
232 | |
233 | dbOpts = rpmExpand("%{_dbi_config_", rpmTagGetName(rpmtag), "}", NULL((void*)0)); |
234 | |
235 | if (!(dbOpts && *dbOpts && *dbOpts != '%')) { |
236 | dbOpts = _free(dbOpts)rfree((dbOpts)); |
237 | dbOpts = rpmExpand("%{_dbi_config}", NULL((void*)0)); |
238 | if (!(dbOpts && *dbOpts && *dbOpts != '%')) { |
239 | dbOpts = _free(dbOpts)rfree((dbOpts)); |
240 | } |
241 | } |
242 | |
243 | /* Parse the options for the database element(s). */ |
244 | if (dbOpts && *dbOpts && *dbOpts != '%') { |
245 | char *o, *oe; |
246 | char *p, *pe; |
247 | |
248 | memset(&staticdbicfg, 0, sizeof(staticdbicfg)); |
249 | /*=========*/ |
250 | for (o = dbOpts; o && *o; o = oe) { |
251 | const struct poptOption *opt; |
252 | const char * tok; |
253 | unsigned int argInfo; |
254 | |
255 | /* Skip leading white space. */ |
256 | while (*o && risspace(*o)) |
257 | o++; |
258 | |
259 | /* Find and terminate next key=value pair. Save next start point. */ |
260 | for (oe = o; oe && *oe; oe++) { |
261 | if (risspace(*oe)) |
262 | break; |
263 | if (oe[0] == ':' && !(oe[1] == '/' && oe[2] == '/')) |
264 | break; |
265 | } |
266 | if (oe && *oe) |
267 | *oe++ = '\0'; |
268 | if (*o == '\0') |
269 | continue; |
270 | |
271 | /* Separate key from value, save value start (if any). */ |
272 | for (pe = o; pe && *pe && *pe != '='; pe++) |
273 | {}; |
274 | p = (pe ? *pe++ = '\0', pe : NULL((void*)0)); |
275 | |
276 | /* Skip over negation at start of token. */ |
277 | for (tok = o; *tok == '!'; tok++) |
278 | {}; |
279 | |
280 | /* Find key in option table. */ |
281 | for (opt = rdbOptions; opt->longName != NULL((void*)0); opt++) { |
282 | if (!rstreq(tok, opt->longName)) |
283 | continue; |
284 | break; |
285 | } |
286 | if (opt->longName == NULL((void*)0)) { |
287 | rpmlog(RPMLOG_ERR, |
288 | _("unrecognized db option: \"%s\" ignored.\n")dcgettext ("rpm", "unrecognized db option: \"%s\" ignored.\n" , 5), o); |
289 | continue; |
290 | } |
291 | |
292 | /* Toggle the flags for negated tokens, if necessary. */ |
293 | argInfo = opt->argInfo; |
294 | if (argInfo == POPT_BIT_SET(7U|0x08000000U) && *o == '!' && ((tok - o) % 2)) |
295 | argInfo = POPT_BIT_CLR(7U|0x05000000U); |
296 | |
297 | /* Save value in template as appropriate. */ |
298 | switch (argInfo & POPT_ARG_MASK0x000000FFU) { |
299 | |
300 | case POPT_ARG_NONE0U: |
301 | (void) poptSaveInt((int *)opt->arg, argInfo, 1L); |
302 | break; |
303 | case POPT_ARG_VAL7U: |
304 | (void) poptSaveInt((int *)opt->arg, argInfo, (long)opt->val); |
305 | break; |
306 | case POPT_ARG_STRING1U: |
307 | { char ** t = opt->arg; |
308 | if (t) { |
309 | /* FIX: opt->arg annotation in popt.h */ |
310 | *t = _free(*t)rfree((*t)); |
311 | *t = xstrdup( (p ? p : "") )rstrdup(((p ? p : ""))); |
312 | } |
313 | } break; |
314 | |
315 | case POPT_ARG_INT2U: |
316 | case POPT_ARG_LONG3U: |
317 | { long aLong = strtol(p, &pe, 0); |
318 | if (pe) { |
319 | if (!rstrncasecmp(pe, "Mb", 2)) |
320 | aLong *= 1024 * 1024; |
321 | else if (!rstrncasecmp(pe, "Kb", 2)) |
322 | aLong *= 1024; |
323 | else if (*pe != '\0') { |
324 | rpmlog(RPMLOG_ERR, |
325 | _("%s has invalid numeric value, skipped\n")dcgettext ("rpm", "%s has invalid numeric value, skipped\n", 5 ), |
326 | opt->longName); |
327 | continue; |
328 | } |
329 | } |
330 | |
331 | if ((argInfo & POPT_ARG_MASK0x000000FFU) == POPT_ARG_LONG3U) { |
332 | if (aLong == LONG_MIN(-9223372036854775807L -1L) || aLong == LONG_MAX9223372036854775807L) { |
333 | rpmlog(RPMLOG_ERR, |
334 | _("%s has too large or too small long value, skipped\n")dcgettext ("rpm", "%s has too large or too small long value, skipped\n" , 5), |
335 | opt->longName); |
336 | continue; |
337 | } |
338 | (void) poptSaveLong((long *)opt->arg, argInfo, aLong); |
339 | break; |
340 | } else { |
341 | if (aLong > INT_MAX2147483647 || aLong < INT_MIN(-2147483647 -1)) { |
342 | rpmlog(RPMLOG_ERR, |
343 | _("%s has too large or too small integer value, skipped\n")dcgettext ("rpm", "%s has too large or too small integer value, skipped\n" , 5), |
344 | opt->longName); |
345 | continue; |
346 | } |
347 | (void) poptSaveInt((int *)opt->arg, argInfo, aLong); |
348 | } |
349 | } break; |
350 | default: |
351 | break; |
352 | } |
353 | } |
354 | /*=========*/ |
355 | } |
356 | |
357 | dbOpts = _free(dbOpts)rfree((dbOpts)); |
358 | if (cfg) { |
359 | *cfg = staticcfg; /* structure assignment */ |
360 | /* Throw in some defaults if configuration didn't set any */ |
361 | if (!cfg->db_mmapsize) |
362 | cfg->db_mmapsize = 16 * 1024 * 1024; |
363 | if (!cfg->db_cachesize) |
364 | cfg->db_cachesize = 8 * 1024 * 1024; |
365 | } |
366 | if (dbicfg) { |
367 | *dbicfg = staticdbicfg; |
368 | } |
369 | } |
370 | |
371 | static char * prDbiOpenFlags(int dbflags, int print_dbenv_flags) |
372 | { |
373 | ARGV_t flags = NULL((void*)0); |
374 | const struct poptOption *opt; |
375 | char *buf; |
376 | |
377 | for (opt = rdbOptions; opt->longName != NULL((void*)0); opt++) { |
378 | if (opt->argInfo != POPT_BIT_SET(7U|0x08000000U)) |
379 | continue; |
380 | if (print_dbenv_flags) { |
381 | if (!(opt->arg == &staticcfg.db_eflags)) |
382 | continue; |
383 | } else { |
384 | if (!(opt->arg == &staticdbicfg.dbi_oflags)) |
385 | continue; |
386 | } |
387 | if ((dbflags & opt->val) != opt->val) |
388 | continue; |
389 | argvAdd(&flags, opt->longName); |
390 | dbflags &= ~opt->val; |
391 | } |
392 | if (dbflags) { |
393 | char *df = NULL((void*)0); |
394 | rasprintf(&df, "0x%x", (unsigned)dbflags); |
395 | argvAdd(&flags, df); |
396 | free(df); |
397 | } |
398 | buf = argvJoin(flags, ":"); |
399 | argvFree(flags); |
400 | |
401 | return buf ? buf : xstrdup("(none)")rstrdup(("(none)")); |
402 | } |
403 | |
404 | static int db_init(rpmdb rdb, const char * dbhome) |
405 | { |
406 | DB_ENV *dbenv = NULL((void*)0); |
407 | int rc, xx; |
408 | int retry_open = 2; |
409 | int lockfd = -1; |
410 | struct dbConfig_s * cfg = &rdb->cfg; |
411 | /* This is our setup, thou shall not have other setups before us */ |
412 | uint32_t eflags = (DB_CREATE0x00000001|DB_INIT_MPOOL0x00000400|DB_INIT_CDB0x00000080); |
413 | |
414 | if (rdb->db_dbenv != NULL((void*)0)) { |
415 | rdb->db_opens++; |
416 | return 0; |
417 | } else { |
418 | /* On first call, set backend description to something... */ |
419 | free(rdb->db_descr); |
420 | rasprintf(&rdb->db_descr, "db%u", DB_VERSION_MAJOR5); |
421 | } |
422 | |
423 | /* |
424 | * Both verify and rebuild are rather special, if for different reasons: |
425 | * On rebuild we dont want to be affected by eg paniced environment, and |
426 | * CDB only slows things down there. Verify is a quirky beast unlike |
427 | * anything else in BDB, and does not like shared env or CDB. |
428 | */ |
429 | if (rdb->db_flags & (RPMDB_FLAG_VERIFYONLY|RPMDB_FLAG_REBUILD)) { |
430 | eflags |= DB_PRIVATE0x00010000; |
431 | eflags &= ~DB_INIT_CDB0x00000080; |
432 | } |
433 | |
434 | rc = db_env_create(&dbenv, 0); |
435 | rc = dbapi_err(rdb, "db_env_create", rc, _debug); |
436 | if (dbenv == NULL((void*)0) || rc) |
437 | goto errxit; |
438 | |
439 | dbenv->set_alloc(dbenv, rmalloc, rrealloc, NULL((void*)0)); |
440 | dbenv->set_errcall(dbenv, NULL((void*)0)); |
441 | dbenv->set_errpfx(dbenv, _errpfx); |
442 | dbenv->set_msgcall(dbenv, warnlog); |
443 | |
444 | /* |
445 | * These enable automatic stale lock removal. |
446 | * thread_count 8 is some kind of "magic minimum" value... |
447 | */ |
448 | dbenv->set_thread_count(dbenv, 8); |
449 | dbenv->set_isalive(dbenv, isalive); |
450 | |
451 | dbenv->set_verbose(dbenv, DB_VERB_DEADLOCK0x00000002, |
452 | (cfg->db_verbose & DB_VERB_DEADLOCK0x00000002)); |
453 | dbenv->set_verbose(dbenv, DB_VERB_RECOVERY0x00000010, |
454 | (cfg->db_verbose & DB_VERB_RECOVERY0x00000010)); |
455 | dbenv->set_verbose(dbenv, DB_VERB_WAITSFOR0x00010000, |
456 | (cfg->db_verbose & DB_VERB_WAITSFOR0x00010000)); |
457 | |
458 | if (cfg->db_mmapsize) { |
459 | xx = dbenv->set_mp_mmapsize(dbenv, cfg->db_mmapsize); |
460 | xx = dbapi_err(rdb, "dbenv->set_mp_mmapsize", xx, _debug); |
461 | } |
462 | |
463 | if (cfg->db_cachesize) { |
464 | xx = dbenv->set_cachesize(dbenv, 0, cfg->db_cachesize, 0); |
465 | xx = dbapi_err(rdb, "dbenv->set_cachesize", xx, _debug); |
466 | } |
467 | |
468 | /* |
469 | * Serialize shared environment open (and clock) via fcntl() lock. |
470 | * Otherwise we can end up calling dbenv->failchk() while another |
471 | * process is joining the environment, leading to transient |
472 | * DB_RUNRECOVER errors. Also prevents races wrt removing the |
473 | * environment (eg chrooted operation). Silently fall back to |
474 | * private environment on failure to allow non-privileged queries |
475 | * to "work", broken as it might be. |
476 | */ |
477 | if (!(eflags & DB_PRIVATE0x00010000)) { |
478 | lockfd = serialize_env(dbhome); |
479 | if (lockfd < 0) { |
480 | eflags |= DB_PRIVATE0x00010000; |
481 | retry_open--; |
482 | rpmlog(RPMLOG_DEBUG, "serialize failed, using private dbenv\n"); |
483 | } |
484 | } |
485 | |
486 | /* |
487 | * Actually open the environment. Fall back to private environment |
488 | * if we dont have permission to join/create shared environment or |
489 | * system doesn't support it.. |
490 | */ |
491 | while (retry_open) { |
492 | char *fstr = prDbiOpenFlags(eflags, 1); |
493 | rpmlog(RPMLOG_DEBUG, "opening db environment %s %s\n", dbhome, fstr); |
494 | free(fstr); |
495 | |
496 | rc = (dbenv->open)(dbenv, dbhome, eflags, rdb->db_perms); |
497 | if ((rc == EACCES13 || rc == EROFS30) || (rc == EINVAL22 && errno(*__errno_location ()) == rc)) { |
498 | eflags |= DB_PRIVATE0x00010000; |
499 | retry_open--; |
500 | } else { |
501 | retry_open = 0; |
502 | } |
503 | } |
504 | rc = dbapi_err(rdb, "dbenv->open", rc, _debug); |
505 | if (rc) |
506 | goto errxit; |
507 | |
508 | dbenv->set_errcall(dbenv, errlog); |
509 | |
510 | /* stale lock removal */ |
511 | rc = dbenv->failchk(dbenv, 0); |
512 | rc = dbapi_err(rdb, "dbenv->failchk", rc, _debug); |
513 | if (rc) |
514 | goto errxit; |
515 | |
516 | rdb->db_dbenv = dbenv; |
517 | rdb->db_opens = 1; |
518 | |
519 | if (lockfd >= 0) |
520 | close(lockfd); |
521 | return 0; |
522 | |
523 | errxit: |
524 | if (dbenv) { |
525 | int xx; |
526 | xx = dbenv->close(dbenv, 0); |
527 | xx = dbapi_err(rdb, "dbenv->close", xx, _debug); |
528 | } |
529 | if (lockfd >= 0) |
530 | close(lockfd); |
531 | return rc; |
532 | } |
533 | |
534 | static void db3_dbSetFSync(rpmdb rdb, int enable) |
535 | { |
536 | #ifdef HAVE_FDATASYNC1 |
537 | db_env_set_func_fsync(enable ? fdatasync : fsync_disable); |
538 | #else |
539 | db_env_set_func_fsync(enable ? fsync : fsync_disable); |
540 | #endif |
541 | } |
542 | |
543 | static int db3_Ctrl(rpmdb rdb, dbCtrlOp ctrl) |
544 | { |
545 | return 0; |
546 | } |
547 | |
548 | static int dbiSync(dbiIndex dbi, unsigned int flags) |
549 | { |
550 | DB * db = dbi->dbi_db; |
551 | int rc = 0; |
552 | |
553 | if (db != NULL((void*)0) && !dbi->cfg.dbi_no_dbsync) { |
554 | rc = db->sync(db, flags); |
555 | rc = cvtdberr(dbi, "db->sync", rc, _debug); |
556 | } |
557 | return rc; |
558 | } |
559 | |
560 | static dbiCursor db3_dbiCursorInit(dbiIndex dbi, unsigned int flags) |
561 | { |
562 | dbiCursor dbc = NULL((void*)0); |
563 | |
564 | if (dbi && dbi->dbi_db) { |
565 | DB * db = dbi->dbi_db; |
566 | DBC * cursor; |
567 | int cflags; |
568 | int rc = 0; |
569 | uint32_t eflags = db_envflags(db); |
570 | |
571 | /* DB_WRITECURSOR requires CDB and writable db */ |
572 | if ((flags & DBC_WRITE) && |
573 | (eflags & DB_INIT_CDB0x00000080) && !(dbi->dbi_flags & DBI_RDONLY)) |
574 | { |
575 | cflags = DB_WRITECURSOR0x00000010; |
576 | } else |
577 | cflags = 0; |
578 | |
579 | /* |
580 | * Check for stale locks which could block writes "forever". |
581 | * XXX: Should we also do this on reads? Reads are less likely |
582 | * to get blocked so it seems excessive... |
583 | * XXX: On DB_RUNRECOVER, we should abort everything. Now |
584 | * we'll just fail to open a cursor again and again and again. |
585 | */ |
586 | if (cflags & DB_WRITECURSOR0x00000010) { |
587 | DB_ENV *dbenv = db->get_env(db); |
588 | rc = dbenv->failchk(dbenv, 0); |
589 | rc = cvtdberr(dbi, "dbenv->failchk", rc, _debug); |
590 | } |
591 | |
592 | if (rc == 0) { |
593 | rc = db->cursor(db, NULL((void*)0), &cursor, cflags); |
594 | rc = cvtdberr(dbi, "db->cursor", rc, _debug); |
595 | } |
596 | |
597 | if (rc == 0) { |
598 | dbc = xcalloc(1, sizeof(*dbc))rcalloc((1), (sizeof(*dbc))); |
599 | dbc->cursor = cursor; |
600 | dbc->dbi = dbi; |
601 | dbc->flags = flags; |
602 | } |
603 | } |
604 | |
605 | return dbc; |
606 | } |
607 | |
608 | static dbiCursor db3_dbiCursorFree(dbiIndex dbi, dbiCursor dbc) |
609 | { |
610 | if (dbc) { |
611 | /* Automatically sync on write-cursor close */ |
612 | if (dbc->flags & DBC_WRITE) |
613 | dbiSync(dbc->dbi, 0); |
614 | DBC * cursor = dbc->cursor; |
615 | int rc = cursor->c_close(cursor); |
616 | cvtdberr(dbc->dbi, "dbcursor->c_close", rc, _debug); |
617 | free(dbc); |
618 | } |
619 | return NULL((void*)0); |
620 | } |
621 | |
622 | static int dbiCursorPut(dbiCursor dbc, DBT * key, DBT * data, unsigned int flags) |
623 | { |
624 | int rc = EINVAL22; |
625 | int sane = (key->data != NULL((void*)0) && key->size > 0 && |
626 | data->data != NULL((void*)0) && data->size > 0); |
627 | |
628 | if (dbc && sane) { |
629 | DBC * cursor = dbc->cursor; |
630 | rpmdb rdb = dbc->dbi->dbi_rpmdb; |
631 | rpmswEnter(&rdb->db_putops, (ssize_t) 0); |
632 | |
633 | rc = cursor->c_put(cursor, key, data, DB_KEYLAST14); |
634 | rc = cvtdberr(dbc->dbi, "dbcursor->c_put", rc, _debug); |
635 | |
636 | rpmswExit(&rdb->db_putops, (ssize_t) data->size); |
637 | } |
638 | return rc; |
639 | } |
640 | |
641 | static int dbiCursorGet(dbiCursor dbc, DBT * key, DBT * data, unsigned int flags) |
642 | { |
643 | int rc = EINVAL22; |
644 | int sane = ((flags == DB_NEXT16) || (key->data != NULL((void*)0) && key->size > 0)); |
645 | |
646 | if (dbc && sane) { |
647 | DBC * cursor = dbc->cursor; |
648 | rpmdb rdb = dbc->dbi->dbi_rpmdb; |
649 | int _printit; |
650 | rpmswEnter(&rdb->db_getops, 0); |
651 | |
652 | /* XXX db4 does DB_FIRST on uninitialized cursor */ |
653 | rc = cursor->c_get(cursor, key, data, flags); |
654 | /* XXX DB_NOTFOUND can be returned */ |
655 | _printit = (rc == DB_NOTFOUND(-30988) ? 0 : _debug); |
656 | rc = cvtdberr(dbc->dbi, "dbcursor->c_get", rc, _printit); |
657 | |
658 | /* Remember the last key fetched */ |
659 | if (rc == 0) { |
660 | dbc->key = key->data; |
661 | dbc->keylen = key->size; |
662 | } else { |
663 | dbc->key = NULL((void*)0); |
664 | dbc->keylen = 0; |
665 | } |
666 | |
667 | rpmswExit(&rdb->db_getops, data->size); |
668 | } |
669 | return rc; |
670 | } |
671 | |
672 | static int dbiCursorDel(dbiCursor dbc, DBT * key, DBT * data, unsigned int flags) |
673 | { |
674 | int rc = EINVAL22; |
675 | int sane = (key->data != NULL((void*)0) && key->size > 0); |
676 | |
677 | if (dbc && sane) { |
678 | DBC * cursor = dbc->cursor; |
679 | int _printit; |
680 | rpmdb rdb = dbc->dbi->dbi_rpmdb; |
681 | rpmswEnter(&rdb->db_delops, 0); |
682 | |
683 | /* XXX TODO: insure that cursor is positioned with duplicates */ |
684 | rc = cursor->c_get(cursor, key, data, DB_SET26); |
685 | /* XXX DB_NOTFOUND can be returned */ |
686 | _printit = (rc == DB_NOTFOUND(-30988) ? 0 : _debug); |
687 | rc = cvtdberr(dbc->dbi, "dbcursor->c_get", rc, _printit); |
688 | |
689 | if (rc == 0) { |
690 | rc = cursor->c_del(cursor, flags); |
691 | rc = cvtdberr(dbc->dbi, "dbcursor->c_del", rc, _debug); |
692 | } |
693 | rpmswExit(&rdb->db_delops, data->size); |
694 | } |
695 | return rc; |
696 | } |
697 | |
698 | static int dbiByteSwapped(dbiIndex dbi) |
699 | { |
700 | DB * db = dbi->dbi_db; |
701 | int rc = 0; |
702 | |
703 | if (dbi->dbi_byteswapped != -1) |
704 | return dbi->dbi_byteswapped; |
705 | |
706 | if (db != NULL((void*)0)) { |
707 | int isswapped = 0; |
708 | rc = db->get_byteswapped(db, &isswapped); |
709 | if (rc == 0) |
710 | dbi->dbi_byteswapped = rc = isswapped; |
711 | } |
712 | return rc; |
713 | } |
714 | |
715 | static int db3_dbiVerify(dbiIndex dbi, unsigned int flags) |
716 | { |
717 | int rc = 0; |
718 | |
719 | if (dbi && dbi->dbi_db) { |
720 | DB * db = dbi->dbi_db; |
721 | |
722 | rc = db->verify(db, dbi->dbi_file, NULL((void*)0), NULL((void*)0), flags); |
723 | rc = cvtdberr(dbi, "db->verify", rc, _debug); |
724 | |
725 | rpmlog(RPMLOG_DEBUG, "verified db index %s\n", dbi->dbi_file); |
726 | |
727 | /* db->verify() destroys the handle, make sure nobody accesses it */ |
728 | dbi->dbi_db = NULL((void*)0); |
729 | } |
730 | return rc; |
731 | } |
732 | |
733 | static int db3_dbiClose(dbiIndex dbi, unsigned int flags) |
734 | { |
735 | rpmdb rdb = dbi->dbi_rpmdb; |
736 | const char * dbhome = rpmdbHome(rdb); |
737 | DB * db = dbi->dbi_db; |
738 | int _printit; |
739 | int rc = 0; |
740 | |
741 | if (db) { |
742 | rc = db->close(db, flags); |
743 | /* XXX ignore not found error messages. */ |
744 | _printit = (rc == ENOENT2 ? 0 : _debug); |
745 | rc = cvtdberr(dbi, "db->close", rc, _printit); |
746 | db = dbi->dbi_db = NULL((void*)0); |
747 | |
748 | rpmlog(RPMLOG_DEBUG, "closed db index %s/%s\n", |
749 | dbhome, dbi->dbi_file); |
750 | } |
751 | |
752 | db_fini(rdb, dbhome ? dbhome : ""); |
753 | |
754 | dbi->dbi_db = NULL((void*)0); |
755 | |
756 | dbi = dbiFree(dbi); |
757 | |
758 | return rc; |
759 | } |
760 | |
761 | /* |
762 | * Lock a file using fcntl(2). Traditionally this is Packages, |
763 | * the file used to store metadata of installed header(s), |
764 | * as Packages is always opened, and should be opened first, |
765 | * for any rpmdb access. |
766 | * |
767 | * If no DBENV is used, then access is protected with a |
768 | * shared/exclusive locking scheme, as always. |
769 | * |
770 | * With a DBENV, the fcntl(2) lock is necessary only to keep |
771 | * the riff-raff from playing where they don't belong, as |
772 | * the DBENV should provide it's own locking scheme. So try to |
773 | * acquire a lock, but permit failures, as some other |
774 | * DBENV player may already have acquired the lock. |
775 | * |
776 | * With NPTL posix mutexes, revert to fcntl lock on non-functioning |
777 | * glibc/kernel combinations. |
778 | */ |
779 | static int dbiFlock(dbiIndex dbi, int mode) |
780 | { |
781 | int fdno = -1; |
782 | int rc = 0; |
783 | DB * db = dbi->dbi_db; |
784 | |
785 | if (!(db->fd(db, &fdno) == 0 && fdno >= 0)) { |
786 | rc = 1; |
787 | } else { |
788 | const char *dbhome = rpmdbHome(dbi->dbi_rpmdb); |
789 | struct flock l; |
790 | memset(&l, 0, sizeof(l)); |
791 | l.l_whence = 0; |
792 | l.l_start = 0; |
793 | l.l_len = 0; |
794 | l.l_type = (mode & O_ACCMODE0003) == O_RDONLY00 |
795 | ? F_RDLCK0 : F_WRLCK1; |
796 | l.l_pid = 0; |
797 | |
798 | rc = fcntl(fdno, F_SETLK6, (void *) &l); |
799 | if (rc) { |
800 | uint32_t eflags = db_envflags(db); |
801 | /* Warning iff using non-private CDB locking. */ |
802 | rc = (((eflags & DB_INIT_CDB0x00000080) && !(eflags & DB_PRIVATE0x00010000)) ? 0 : 1); |
803 | rpmlog( (rc ? RPMLOG_ERR : RPMLOG_WARNING), |
804 | _("cannot get %s lock on %s/%s\n")dcgettext ("rpm", "cannot get %s lock on %s/%s\n", 5), |
805 | ((mode & O_ACCMODE0003) == O_RDONLY00) |
806 | ? _("shared")dcgettext ("rpm", "shared", 5) : _("exclusive")dcgettext ("rpm", "exclusive", 5), |
807 | dbhome, dbi->dbi_file); |
808 | } else { |
809 | rpmlog(RPMLOG_DEBUG, |
810 | "locked db index %s/%s\n", |
811 | dbhome, dbi->dbi_file); |
812 | } |
813 | } |
814 | return rc; |
815 | } |
816 | |
817 | static int db3_dbiOpen(rpmdb rdb, rpmDbiTagVal rpmtag, dbiIndex * dbip, int flags) |
818 | { |
819 | const char *dbhome = rpmdbHome(rdb); |
820 | dbiIndex dbi = NULL((void*)0); |
821 | int rc = 0; |
822 | int retry_open; |
823 | int verifyonly = (flags & RPMDB_FLAG_VERIFYONLY); |
824 | |
825 | DB * db = NULL((void*)0); |
826 | DBTYPE dbtype = DB_UNKNOWN; |
827 | uint32_t oflags; |
828 | static int _lockdbfd = 0; |
829 | |
830 | if (dbip) |
831 | *dbip = NULL((void*)0); |
832 | |
833 | if ((dbi = dbiNew(rdb, rpmtag)) == NULL((void*)0)) |
834 | return 1; |
835 | |
836 | /* |
837 | * Parse db configuration parameters. |
838 | */ |
839 | dbConfigure(rpmtag, rdb->db_dbenv == NULL((void*)0) ? &rdb->cfg : NULL((void*)0), &dbi->cfg); |
840 | |
841 | /* |
842 | * Map open mode flags onto configured database/environment flags. |
843 | */ |
844 | oflags = dbi->cfg.dbi_oflags; |
845 | if ((rdb->db_mode & O_ACCMODE0003) == O_RDONLY00) |
846 | oflags |= DB_RDONLY0x00000400; |
847 | |
848 | rc = db_init(rdb, dbhome); |
849 | |
850 | retry_open = (rc == 0) ? 2 : 0; |
851 | |
852 | while (retry_open) { |
853 | rc = db_create(&db, rdb->db_dbenv, 0); |
854 | rc = cvtdberr(dbi, "db_create", rc, _debug); |
855 | |
856 | /* For verify we only want the handle, not an open db */ |
857 | if (verifyonly) |
858 | break; |
859 | |
860 | if (rc == 0 && db != NULL((void*)0)) { |
861 | int _printit; |
862 | char *dbfs = prDbiOpenFlags(oflags, 0); |
863 | rpmlog(RPMLOG_DEBUG, "opening db index %s/%s %s mode=0x%x\n", |
864 | dbhome, dbi->dbi_file, dbfs, rdb->db_mode); |
865 | free(dbfs); |
866 | |
867 | rc = (db->open)(db, NULL((void*)0), dbi->dbi_file, NULL((void*)0), |
868 | dbtype, oflags, rdb->db_perms); |
869 | |
870 | /* Attempt to create if missing, discarding DB_RDONLY (!) */ |
871 | if (rc == ENOENT2) { |
872 | oflags |= DB_CREATE0x00000001; |
873 | oflags &= ~DB_RDONLY0x00000400; |
874 | dbtype = (rpmtag == RPMDBI_PACKAGES) ? DB_HASH : DB_BTREE; |
875 | retry_open--; |
876 | } else { |
877 | retry_open = 0; |
878 | } |
879 | |
880 | /* XXX return rc == errno without printing */ |
881 | _printit = (rc > 0 ? 0 : _debug); |
882 | rc = cvtdberr(dbi, "db->open", rc, _printit); |
883 | |
884 | /* Validate the index type is something we can support */ |
885 | if ((rc == 0) && (dbtype == DB_UNKNOWN)) { |
886 | db->get_type(db, &dbtype); |
887 | if (dbtype != DB_HASH && dbtype != DB_BTREE) { |
888 | rpmlog(RPMLOG_ERR, _("invalid index type %x on %s/%s\n")dcgettext ("rpm", "invalid index type %x on %s/%s\n", 5), |
889 | dbtype, dbhome, dbi->dbi_file); |
890 | rc = 1; |
891 | } |
892 | } |
893 | |
894 | if (rc != 0) { |
895 | db->close(db, 0); |
896 | db = NULL((void*)0); |
897 | } |
898 | } |
899 | } |
900 | |
901 | dbi->dbi_db = db; |
902 | |
903 | dbi->dbi_flags = 0; |
904 | if (oflags & DB_CREATE0x00000001) |
905 | dbi->dbi_flags |= DBI_CREATED; |
906 | if (oflags & DB_RDONLY0x00000400) |
907 | dbi->dbi_flags |= DBI_RDONLY; |
908 | |
909 | if (!verifyonly && rc == 0 && dbi->cfg.dbi_lockdbfd && _lockdbfd++ == 0) { |
910 | rc = dbiFlock(dbi, rdb->db_mode); |
911 | } |
912 | |
913 | if (rc == 0 && dbi->dbi_db != NULL((void*)0) && dbip != NULL((void*)0)) { |
914 | *dbip = dbi; |
915 | } else { |
916 | (void) dbiClose(dbi, 0); |
917 | } |
918 | |
919 | return rc; |
920 | } |
921 | |
922 | /** |
923 | * Convert retrieved data to index set. |
924 | * @param dbi index database handle |
925 | * @param data retrieved data |
926 | * @retval setp (malloc'ed) index set |
927 | * @return 0 on success |
928 | */ |
929 | static int dbt2set(dbiIndex dbi, DBT * data, dbiIndexSet * setp) |
930 | { |
931 | int _dbbyteswapped = dbiByteSwapped(dbi); |
932 | const char * sdbir; |
933 | dbiIndexSet set; |
934 | unsigned int i; |
935 | |
936 | if (dbi == NULL((void*)0) || data == NULL((void*)0) || setp == NULL((void*)0)) |
937 | return -1; |
938 | |
939 | if ((sdbir = data->data) == NULL((void*)0)) { |
940 | *setp = NULL((void*)0); |
941 | return 0; |
942 | } |
943 | |
944 | set = dbiIndexSetNew(data->size / (2 * sizeof(int32_t))); |
945 | set->count = data->size / (2 * sizeof(int32_t)); |
946 | |
947 | for (i = 0; i < set->count; i++) { |
948 | union _dbswap hdrNum, tagNum; |
949 | |
950 | memcpy(&hdrNum.ui, sdbir, sizeof(hdrNum.ui)); |
951 | sdbir += sizeof(hdrNum.ui); |
952 | memcpy(&tagNum.ui, sdbir, sizeof(tagNum.ui)); |
953 | sdbir += sizeof(tagNum.ui); |
954 | if (_dbbyteswapped) { |
955 | _DBSWAP(hdrNum){ unsigned char _b, *_c = (hdrNum).uc; _b = _c[3]; _c[3] = _c [0]; _c[0] = _b; _b = _c[2]; _c[2] = _c[1]; _c[1] = _b; }; |
956 | _DBSWAP(tagNum){ unsigned char _b, *_c = (tagNum).uc; _b = _c[3]; _c[3] = _c [0]; _c[0] = _b; _b = _c[2]; _c[2] = _c[1]; _c[1] = _b; }; |
957 | } |
958 | set->recs[i].hdrNum = hdrNum.ui; |
959 | set->recs[i].tagNum = tagNum.ui; |
960 | } |
961 | *setp = set; |
962 | return 0; |
963 | } |
964 | |
965 | /** |
966 | * Convert index set to database representation. |
967 | * @param dbi index database handle |
968 | * @param data retrieved data |
969 | * @param set index set |
970 | * @return 0 on success |
971 | */ |
972 | static int set2dbt(dbiIndex dbi, DBT * data, dbiIndexSet set) |
973 | { |
974 | int _dbbyteswapped = dbiByteSwapped(dbi); |
975 | char * tdbir; |
976 | unsigned int i; |
977 | |
978 | if (dbi == NULL((void*)0) || data == NULL((void*)0) || set == NULL((void*)0)) |
979 | return -1; |
980 | |
981 | data->size = set->count * (2 * sizeof(int32_t)); |
982 | if (data->size == 0) { |
983 | data->data = NULL((void*)0); |
984 | return 0; |
985 | } |
986 | tdbir = data->data = xmalloc(data->size)rmalloc((data->size)); |
987 | |
988 | for (i = 0; i < set->count; i++) { |
989 | union _dbswap hdrNum, tagNum; |
990 | |
991 | memset(&hdrNum, 0, sizeof(hdrNum)); |
992 | memset(&tagNum, 0, sizeof(tagNum)); |
993 | hdrNum.ui = set->recs[i].hdrNum; |
994 | tagNum.ui = set->recs[i].tagNum; |
995 | if (_dbbyteswapped) { |
996 | _DBSWAP(hdrNum){ unsigned char _b, *_c = (hdrNum).uc; _b = _c[3]; _c[3] = _c [0]; _c[0] = _b; _b = _c[2]; _c[2] = _c[1]; _c[1] = _b; }; |
997 | _DBSWAP(tagNum){ unsigned char _b, *_c = (tagNum).uc; _b = _c[3]; _c[3] = _c [0]; _c[0] = _b; _b = _c[2]; _c[2] = _c[1]; _c[1] = _b; }; |
998 | } |
999 | memcpy(tdbir, &hdrNum.ui, sizeof(hdrNum.ui)); |
1000 | tdbir += sizeof(hdrNum.ui); |
1001 | memcpy(tdbir, &tagNum.ui, sizeof(tagNum.ui)); |
1002 | tdbir += sizeof(tagNum.ui); |
1003 | } |
1004 | return 0; |
1005 | } |
1006 | |
1007 | static rpmRC db3_idxdbGet(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t keylen, |
1008 | dbiIndexSet *set, int searchType) |
1009 | { |
1010 | rpmRC rc = RPMRC_FAIL; /* assume failure */ |
1011 | if (dbi != NULL((void*)0) && dbc != NULL((void*)0) && set != NULL((void*)0)) { |
1012 | int cflags = DB_NEXT16; |
1013 | int dbrc; |
1014 | DBT data, key; |
1015 | memset(&data, 0, sizeof(data)); |
1016 | memset(&key, 0, sizeof(key)); |
1017 | |
1018 | if (keyp) { |
1019 | if (keylen == 0) { /* XXX "/" fixup */ |
1020 | keyp = ""; |
1021 | keylen = 1; |
1022 | } |
1023 | key.data = (void *) keyp; /* discards const */ |
1024 | key.size = keylen; |
1025 | cflags = searchType == DBC_PREFIX_SEARCH ? DB_SET_RANGE27 : DB_SET26; |
1026 | } |
1027 | |
1028 | for (;;) { |
1029 | dbiIndexSet newset = NULL((void*)0); |
1030 | dbrc = dbiCursorGet(dbc, &key, &data, cflags); |
1031 | if (dbrc != 0) |
1032 | break; |
1033 | if (searchType == DBC_PREFIX_SEARCH && |
1034 | (key.size < keylen || memcmp(key.data, keyp, keylen) != 0)) |
1035 | break; |
1036 | dbt2set(dbi, &data, &newset); |
1037 | if (*set == NULL((void*)0)) { |
1038 | *set = newset; |
1039 | } else { |
1040 | dbiIndexSetAppendSet(*set, newset, 0); |
1041 | dbiIndexSetFree(newset); |
1042 | } |
1043 | if (searchType != DBC_PREFIX_SEARCH) |
1044 | break; |
1045 | key.data = NULL((void*)0); |
1046 | key.size = 0; |
1047 | cflags = DB_NEXT16; |
1048 | } |
1049 | |
1050 | /* fixup result status for prefix search */ |
1051 | if (searchType == DBC_PREFIX_SEARCH) { |
1052 | if (dbrc == DB_NOTFOUND(-30988) && *set != NULL((void*)0) && (*set)->count > 0) |
1053 | dbrc = 0; |
1054 | else if (dbrc == 0 && (*set == NULL((void*)0) || (*set)->count == 0)) |
1055 | dbrc = DB_NOTFOUND(-30988); |
1056 | } |
1057 | |
1058 | if (dbrc == 0) { |
1059 | rc = RPMRC_OK; |
1060 | } else if (dbrc == DB_NOTFOUND(-30988)) { |
1061 | rc = RPMRC_NOTFOUND; |
1062 | } else { |
1063 | rpmlog(RPMLOG_ERR, |
1064 | _("error(%d) getting \"%s\" records from %s index: %s\n")dcgettext ("rpm", "error(%d) getting \"%s\" records from %s index: %s\n" , 5), |
1065 | dbrc, keyp ? keyp : "???", dbiName(dbi), db_strerror(dbrc)); |
1066 | } |
1067 | } |
1068 | return rc; |
1069 | } |
1070 | |
1071 | /* Update secondary index. NULL set deletes the key */ |
1072 | static rpmRC updateIndex(dbiCursor dbc, const char *keyp, unsigned int keylen, |
1073 | dbiIndexSet set) |
1074 | { |
1075 | rpmRC rc = RPMRC_FAIL; |
1076 | |
1077 | if (dbc && keyp) { |
1078 | dbiIndex dbi = dbc->dbi; |
1079 | int dbrc; |
1080 | DBT data, key; |
1081 | memset(&key, 0, sizeof(data)); |
1082 | memset(&data, 0, sizeof(data)); |
1083 | |
1084 | key.data = (void *) keyp; /* discards const */ |
1085 | key.size = keylen; |
1086 | |
1087 | if (set) |
1088 | set2dbt(dbi, &data, set); |
1089 | |
1090 | if (dbiIndexSetCount(set) > 0) { |
1091 | dbrc = dbiCursorPut(dbc, &key, &data, DB_KEYLAST14); |
1092 | if (dbrc) { |
1093 | rpmlog(RPMLOG_ERR, |
1094 | _("error(%d) storing record \"%s\" into %s\n")dcgettext ("rpm", "error(%d) storing record \"%s\" into %s\n" , 5), |
1095 | dbrc, (char*)key.data, dbiName(dbi)); |
1096 | } |
1097 | free(data.data); |
1098 | } else { |
1099 | dbrc = dbiCursorDel(dbc, &key, &data, 0); |
1100 | if (dbrc) { |
1101 | rpmlog(RPMLOG_ERR, |
1102 | _("error(%d) removing record \"%s\" from %s\n")dcgettext ("rpm", "error(%d) removing record \"%s\" from %s\n" , 5), |
1103 | dbrc, (char*)key.data, dbiName(dbi)); |
1104 | } |
1105 | } |
1106 | |
1107 | if (dbrc == 0) |
1108 | rc = RPMRC_OK; |
1109 | } |
1110 | |
1111 | return rc; |
1112 | } |
1113 | |
1114 | static rpmRC db3_idxdbPut(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t keylen, |
1115 | dbiIndexItem rec) |
1116 | { |
1117 | dbiIndexSet set = NULL((void*)0); |
1118 | rpmRC rc; |
1119 | |
1120 | if (keyp && keylen == 0) { /* XXX "/" fixup */ |
1121 | keyp = ""; |
1122 | keylen++; |
1123 | } |
1124 | rc = idxdbGet(dbi, dbc, keyp, keylen, &set, DBC_NORMAL_SEARCH); |
1125 | |
1126 | /* Not found means a new key and is not an error. */ |
1127 | if (rc && rc != RPMRC_NOTFOUND) |
1128 | return rc; |
1129 | |
1130 | if (set == NULL((void*)0)) |
1131 | set = dbiIndexSetNew(1); |
1132 | dbiIndexSetAppend(set, rec, 1, 0); |
1133 | |
1134 | rc = updateIndex(dbc, keyp, keylen, set); |
1135 | |
1136 | dbiIndexSetFree(set); |
1137 | return rc; |
1138 | } |
1139 | |
1140 | static rpmRC db3_idxdbDel(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t keylen, |
1141 | dbiIndexItem rec) |
1142 | { |
1143 | dbiIndexSet set = NULL((void*)0); |
1144 | rpmRC rc; |
1145 | |
1146 | if (keyp && keylen == 0) { /* XXX "/" fixup */ |
1147 | keyp = ""; |
1148 | keylen++; |
1149 | } |
1150 | rc = idxdbGet(dbi, dbc, keyp, keylen, &set, DBC_NORMAL_SEARCH); |
1151 | if (rc) |
1152 | return rc; |
1153 | |
1154 | if (dbiIndexSetPrune(set, rec, 1, 1)) { |
1155 | /* Nothing was pruned. XXX: Can this actually happen? */ |
1156 | rc = RPMRC_OK; |
1157 | } else { |
1158 | /* If there's data left, update data. Otherwise delete the key. */ |
1159 | if (dbiIndexSetCount(set) > 0) { |
1160 | rc = updateIndex(dbc, keyp, keylen, set); |
1161 | } else { |
1162 | rc = updateIndex(dbc, keyp, keylen, NULL((void*)0)); |
1163 | } |
1164 | }; |
1165 | dbiIndexSetFree(set); |
1166 | |
1167 | return rc; |
1168 | } |
1169 | |
1170 | static const void * db3_idxdbKey(dbiIndex dbi, dbiCursor dbc, unsigned int *keylen) |
1171 | { |
1172 | const void *key = NULL((void*)0); |
1173 | if (dbc) { |
1174 | key = dbc->key; |
1175 | if (key && keylen) |
1176 | *keylen = dbc->keylen; |
1177 | } |
1178 | return key; |
1179 | } |
1180 | |
1181 | |
1182 | /* Update primary Packages index. NULL hdr means remove */ |
1183 | static rpmRC updatePackages(dbiCursor dbc, unsigned int hdrNum, DBT *hdr) |
1184 | { |
1185 | union _dbswap mi_offset; |
1186 | int rc = 0; |
1187 | DBT key; |
1188 | |
1189 | if (dbc == NULL((void*)0) || hdrNum == 0) |
1190 | return RPMRC_FAIL; |
1191 | |
1192 | memset(&key, 0, sizeof(key)); |
1193 | |
1194 | mi_offset.ui = hdrNum; |
1195 | if (dbiByteSwapped(dbc->dbi) == 1) |
1196 | _DBSWAP(mi_offset){ unsigned char _b, *_c = (mi_offset).uc; _b = _c[3]; _c[3] = _c[0]; _c[0] = _b; _b = _c[2]; _c[2] = _c[1]; _c[1] = _b; }; |
1197 | key.data = (void *) &mi_offset; |
1198 | key.size = sizeof(mi_offset.ui); |
1199 | |
1200 | if (hdr) { |
1201 | rc = dbiCursorPut(dbc, &key, hdr, DB_KEYLAST14); |
1202 | if (rc) { |
1203 | rpmlog(RPMLOG_ERR, |
1204 | _("error(%d) adding header #%d record\n")dcgettext ("rpm", "error(%d) adding header #%d record\n", 5), rc, hdrNum); |
1205 | } |
1206 | } else { |
1207 | DBT data; |
1208 | |
1209 | memset(&data, 0, sizeof(data)); |
1210 | rc = dbiCursorGet(dbc, &key, &data, DB_SET26); |
1211 | if (rc) { |
1212 | rpmlog(RPMLOG_ERR, |
1213 | _("error(%d) removing header #%d record\n")dcgettext ("rpm", "error(%d) removing header #%d record\n", 5 ), rc, hdrNum); |
1214 | } else |
1215 | rc = dbiCursorDel(dbc, &key, &data, 0); |
1216 | } |
1217 | |
1218 | return rc == 0 ? RPMRC_OK : RPMRC_FAIL; |
1219 | } |
1220 | |
1221 | /* Get current header instance number or try to allocate a new one */ |
1222 | static unsigned int pkgInstance(dbiIndex dbi, int alloc) |
1223 | { |
1224 | unsigned int hdrNum = 0; |
1225 | |
1226 | if (dbi != NULL((void*)0) && dbi->dbi_type == DBI_PRIMARY) { |
1227 | dbiCursor dbc; |
1228 | DBT key, data; |
1229 | unsigned int firstkey = 0; |
1230 | union _dbswap mi_offset; |
1231 | int ret; |
1232 | |
1233 | memset(&key, 0, sizeof(key)); |
1234 | memset(&data, 0, sizeof(data)); |
1235 | |
1236 | dbc = dbiCursorInit(dbi, alloc ? DBC_WRITE : 0); |
1237 | |
1238 | /* Key 0 holds the current largest instance, fetch it */ |
1239 | key.data = &firstkey; |
1240 | key.size = sizeof(firstkey); |
1241 | ret = dbiCursorGet(dbc, &key, &data, DB_SET26); |
1242 | |
1243 | if (ret == 0 && data.data) { |
1244 | memcpy(&mi_offset, data.data, sizeof(mi_offset.ui)); |
1245 | if (dbiByteSwapped(dbi) == 1) |
1246 | _DBSWAP(mi_offset){ unsigned char _b, *_c = (mi_offset).uc; _b = _c[3]; _c[3] = _c[0]; _c[0] = _b; _b = _c[2]; _c[2] = _c[1]; _c[1] = _b; }; |
1247 | hdrNum = mi_offset.ui; |
1248 | } |
1249 | |
1250 | if (alloc) { |
1251 | /* Rather complicated "increment by one", bswapping as needed */ |
1252 | ++hdrNum; |
1253 | mi_offset.ui = hdrNum; |
1254 | if (dbiByteSwapped(dbi) == 1) |
1255 | _DBSWAP(mi_offset){ unsigned char _b, *_c = (mi_offset).uc; _b = _c[3]; _c[3] = _c[0]; _c[0] = _b; _b = _c[2]; _c[2] = _c[1]; _c[1] = _b; }; |
1256 | if (ret == 0 && data.data) { |
1257 | memcpy(data.data, &mi_offset, sizeof(mi_offset.ui)); |
1258 | } else { |
1259 | data.data = &mi_offset; |
1260 | data.size = sizeof(mi_offset.ui); |
1261 | } |
1262 | |
1263 | /* Unless we manage to insert the new instance number, we failed */ |
1264 | ret = dbiCursorPut(dbc, &key, &data, DB_KEYLAST14); |
1265 | if (ret) { |
1266 | hdrNum = 0; |
1267 | rpmlog(RPMLOG_ERR, |
1268 | _("error(%d) allocating new package instance\n")dcgettext ("rpm", "error(%d) allocating new package instance\n" , 5), ret); |
1269 | } |
1270 | } |
1271 | dbiCursorFree(dbi, dbc); |
1272 | } |
1273 | |
1274 | return hdrNum; |
1275 | } |
1276 | |
1277 | static rpmRC db3_pkgdbPut(dbiIndex dbi, dbiCursor dbc, unsigned int hdrNum, |
1278 | unsigned char *hdrBlob, unsigned int hdrLen) |
1279 | { |
1280 | DBT hdr; |
1281 | memset(&hdr, 0, sizeof(hdr)); |
1282 | hdr.data = hdrBlob; |
1283 | hdr.size = hdrLen; |
1284 | return updatePackages(dbc, hdrNum, &hdr); |
1285 | } |
1286 | |
1287 | static rpmRC db3_pkgdbDel(dbiIndex dbi, dbiCursor dbc, unsigned int hdrNum) |
1288 | { |
1289 | return updatePackages(dbc, hdrNum, NULL((void*)0)); |
1290 | } |
1291 | |
1292 | static rpmRC db3_pkgdbGet(dbiIndex dbi, dbiCursor dbc, unsigned int hdrNum, |
1293 | unsigned char **hdrBlob, unsigned int *hdrLen) |
1294 | { |
1295 | DBT key, data; |
1296 | union _dbswap mi_offset; |
1297 | int rc; |
1298 | |
1299 | if (dbc == NULL((void*)0)) |
1300 | return RPMRC_FAIL; |
1301 | |
1302 | memset(&key, 0, sizeof(key)); |
1303 | memset(&data, 0, sizeof(data)); |
1304 | |
1305 | if (hdrNum) { |
1306 | mi_offset.ui = hdrNum; |
1307 | if (dbiByteSwapped(dbc->dbi) == 1) |
1308 | _DBSWAP(mi_offset){ unsigned char _b, *_c = (mi_offset).uc; _b = _c[3]; _c[3] = _c[0]; _c[0] = _b; _b = _c[2]; _c[2] = _c[1]; _c[1] = _b; }; |
1309 | key.data = (void *) &mi_offset; |
1310 | key.size = sizeof(mi_offset.ui); |
1311 | } |
1312 | |
1313 | #if !defined(_USE_COPY_LOAD) |
1314 | data.flags |= DB_DBT_MALLOC0x010; |
1315 | #endif |
1316 | rc = dbiCursorGet(dbc, &key, &data, hdrNum ? DB_SET26 : DB_NEXT16); |
1317 | if (rc == 0) { |
1318 | if (hdrBlob) |
1319 | *hdrBlob = data.data; |
1320 | if (hdrLen) |
1321 | *hdrLen = data.size; |
1322 | return RPMRC_OK; |
1323 | } else if (rc == DB_NOTFOUND(-30988)) |
1324 | return RPMRC_NOTFOUND; |
1325 | else |
1326 | return RPMRC_FAIL; |
1327 | } |
1328 | |
1329 | static unsigned int db3_pkgdbKey(dbiIndex dbi, dbiCursor dbc) |
1330 | { |
1331 | union _dbswap mi_offset; |
1332 | |
1333 | if (dbc == NULL((void*)0) || dbc->key == NULL((void*)0)) |
1334 | return 0; |
1335 | memcpy(&mi_offset, dbc->key, sizeof(mi_offset.ui)); |
1336 | if (dbiByteSwapped(dbc->dbi) == 1) |
1337 | _DBSWAP(mi_offset){ unsigned char _b, *_c = (mi_offset).uc; _b = _c[3]; _c[3] = _c[0]; _c[0] = _b; _b = _c[2]; _c[2] = _c[1]; _c[1] = _b; }; |
1338 | return mi_offset.ui; |
1339 | } |
1340 | |
1341 | static rpmRC db3_pkgdbNew(dbiIndex dbi, dbiCursor dbc, unsigned int *hdrNum) |
1342 | { |
1343 | unsigned int num; |
1344 | if (dbc == NULL((void*)0)) |
1345 | return RPMRC_FAIL; |
1346 | num = pkgInstance(dbc->dbi, 1); |
1347 | if (!num) |
1348 | return RPMRC_FAIL; |
1349 | *hdrNum = num; |
1350 | return RPMRC_OK; |
1351 | } |
1352 | |
1353 | struct rpmdbOps_s db3_dbops = { |
1354 | .open = db3_dbiOpen, |
1355 | .close = db3_dbiClose, |
1356 | .verify = db3_dbiVerify, |
1357 | |
1358 | .setFSync = db3_dbSetFSync, |
1359 | .ctrl = db3_Ctrl, |
1360 | |
1361 | .cursorInit = db3_dbiCursorInit, |
1362 | .cursorFree = db3_dbiCursorFree, |
1363 | |
1364 | .pkgdbGet = db3_pkgdbGet, |
1365 | .pkgdbPut = db3_pkgdbPut, |
1366 | .pkgdbDel = db3_pkgdbDel, |
1367 | .pkgdbNew = db3_pkgdbNew, |
1368 | .pkgdbKey = db3_pkgdbKey, |
1369 | |
1370 | .idxdbGet = db3_idxdbGet, |
1371 | .idxdbPut = db3_idxdbPut, |
1372 | .idxdbDel = db3_idxdbDel, |
1373 | .idxdbKey = db3_idxdbKey |
1374 | }; |
1375 |