1/* 2 * Copyright (C) Joerg Lenneis 2003 3 * Copyright (C) Frank Lahm 2009 4 * All Rights Reserved. See COPYING. 5 */ 6 7#ifdef HAVE_CONFIG_H 8#include "config.h" 9#endif /* HAVE_CONFIG_H */ 10 11#include <stdio.h> 12#include <errno.h> 13#include <stdlib.h> 14#include <string.h> 15#include <sys/types.h> 16#include <sys/stat.h> 17#include <sys/cdefs.h> 18#include <unistd.h> 19 20#include <db.h> 21 22#include <atalk/logger.h> 23#include <atalk/util.h> 24 25#include "db_param.h" 26#include "dbif.h" 27#include "pack.h" 28 29#define DB_ERRLOGFILE "db_errlog" 30 31/*! 32 * Get the db stamp which is the st_ctime of the file "cnid2.db" and store it in buffer 33 */ 34static int dbif_stamp(DBD *dbd, void *buffer, int size) 35{ 36 struct stat st; 37 int rc,cwd; 38 39 if (size < 8) 40 return -1; 41 42 /* Remember cwd */ 43 if ((cwd = open(".", O_RDONLY)) < 0) { 44 LOG(log_error, logtype_cnid, "error opening cwd: %s", strerror(errno)); 45 return -1; 46 } 47 48 /* chdir to db_envhome */ 49 if ((chdir(dbd->db_envhome)) != 0) { 50 LOG(log_error, logtype_cnid, "error chdiring to db_env '%s': %s", dbd->db_envhome, strerror(errno)); 51 return -1; 52 } 53 54 if ((rc = stat(dbd->db_table[DBIF_CNID].name, &st)) < 0) { 55 LOG(log_error, logtype_cnid, "error stating database %s: %s", dbd->db_table[DBIF_CNID].name, db_strerror(errno)); 56 return -1; 57 } 58 59 LOG(log_maxdebug, logtype_cnid,"stamp: %s", asctime(localtime(&st.st_ctime))); 60 61 memset(buffer, 0, size); 62 memcpy(buffer, &st.st_ctime, sizeof(st.st_ctime)); 63 64 if ((fchdir(cwd)) != 0) { 65 LOG(log_error, logtype_cnid, "error chdiring back: %s", strerror(errno)); 66 return -1; 67 } 68 69 return 0; 70} 71 72/*! 73 * Inititialize rootinfo key (which has CNID 0 as key) 74 * 75 * This also "stamps" the database, which means storing st.st_ctime of the 76 * "cnid2.db" file in the rootinfo data at the DEV offset 77 * 78 * @param dbd (rw) database handle 79 * @param version (r) database version number 80 * 81 * @returns -1 on error, 0 on success 82 */ 83static int dbif_init_rootinfo(DBD *dbd, int version) 84{ 85 DBT key, data; 86 uint32_t v; 87 char buf[ROOTINFO_DATALEN]; 88 89 LOG(log_debug, logtype_cnid, "Setting CNID database version to %u", version); 90 91 v = version; 92 v = htonl(v); 93 94 memset(&key, 0, sizeof(key)); 95 memset(&data, 0, sizeof(data)); 96 key.data = ROOTINFO_KEY; 97 key.size = ROOTINFO_KEYLEN; 98 data.data = buf; 99 data.size = ROOTINFO_DATALEN; 100 101 memcpy(buf, ROOTINFO_DATA, ROOTINFO_DATALEN); 102 memcpy(buf + CNID_DID_OFS, &v, sizeof(v)); 103 if (dbif_stamp(dbd, buf + CNID_DEV_OFS, CNID_DEV_LEN) < 0) 104 return -1; 105 106 if (dbif_put(dbd, DBIF_CNID, &key, &data, 0) < 0) 107 return -1; 108 if (dbif_txn_commit(dbd) != 1) { 109 LOG(log_error, logtype_cnid, "dbif_init_rootinfo: cant commit txn"); 110 return -1; 111 } 112 113 return 0; 114} 115 116/*! 117 * Return CNID database version number 118 * 119 * Returns version in *version 120 * 121 * @returns -1 on error, 0 if theres no rootinfo key yet, 1 if *version is returned 122 */ 123static int dbif_getversion(DBD *dbd, uint32_t *version) 124{ 125 DBT key, data; 126 int ret; 127 128 LOG(log_maxdebug, logtype_cnid, "dbif_getversion: reading version info"); 129 130 *version = -1; 131 memset(&key, 0, sizeof(key)); 132 memset(&data, 0, sizeof(data)); 133 key.data = ROOTINFO_KEY; 134 key.size = ROOTINFO_KEYLEN; 135 136 switch (dbif_get(dbd, DBIF_CNID, &key, &data, 0)) { 137 case 1: /* found */ 138 memcpy(version, (char *)data.data + CNID_DID_OFS, sizeof(uint32_t)); 139 *version = ntohl(*version); 140 LOG(log_debug, logtype_cnid, "CNID database version %u", *version); 141 ret = 1; 142 break; 143 case 0: /* not found */ 144 LOG(log_debug, logtype_cnid, "dbif_getversion: no version info found"); 145 ret = 0; 146 break; 147 default: 148 LOG(log_error, logtype_cnid, "dbif_getversion: database error"); 149 ret = -1; 150 break; 151 } 152 153 return ret; 154} 155 156/*! 157 * Set CNID database version number 158 * 159 * Initializes rootinfo key as neccessary 160 * @returns -1 on error, 0 on success 161 */ 162static int dbif_setversion(DBD *dbd, uint32_t version) 163{ 164 int ret; 165 DBT key, data; 166 uint32_t v; 167 168 LOG(log_debug, logtype_cnid, "Setting CNID database version to %u", version); 169 170 v = version; 171 v = htonl(v); 172 173 memset(&key, 0, sizeof(key)); 174 memset(&data, 0, sizeof(data)); 175 key.data = ROOTINFO_KEY; 176 key.size = ROOTINFO_KEYLEN; 177 178 if ((ret = dbif_get(dbd, DBIF_CNID, &key, &data, 0)) == -1) 179 return -1; 180 if (ret == 0) { 181 /* No rootinfo key yet, init it */ 182 if (dbif_init_rootinfo(dbd, CNID_VERSION) != 0) 183 return -1; 184 /* Now try again */ 185 if (dbif_get(dbd, DBIF_CNID, &key, &data, 0) == -1) 186 return -1; 187 } 188 memcpy((char *)data.data + CNID_DID_OFS, &v, sizeof(v)); 189 data.size = ROOTINFO_DATALEN; 190 if (dbif_put(dbd, DBIF_CNID, &key, &data, 0) < 0) 191 return -1; 192 193 return 0; 194} 195 196/*! 197 * Upgrade CNID database versions, initialize rootinfo key as as necessary in dbif_setversion() 198 * 199 * For now this does nothing, as upgrading from ver. 0 to 1 is done in dbif_open 200 */ 201#define UNINTIALIZED_DB UINT32_MAX 202static int dbif_upgrade(DBD *dbd) 203{ 204 uint32_t version = CNID_VERSION_UNINTIALIZED_DB; 205 206 if (dbif_getversion(dbd, &version) == -1) 207 return -1; 208 if (version == CNID_VERSION_UNINTIALIZED_DB) { 209 version = CNID_VERSION; 210 if (dbif_setversion(dbd, CNID_VERSION) != 0) 211 return -1; 212 } 213 214 /* 215 * Do upgrade stuff ... 216 */ 217 218 /* Write current version to database */ 219 if (version != CNID_VERSION) { 220 if (dbif_setversion(dbd, CNID_VERSION) != 0) 221 return -1; 222 } 223 224 LOG(log_debug, logtype_cnid, "Finished CNID database version upgrade check"); 225 226 return 0; 227} 228 229/* --------------- */ 230static int dbif_openlog(DBD *dbd) 231{ 232 int ret = 0; 233 int cwd = -1; 234 235 if ( ! dbd->db_filename) 236 /* in memory db */ 237 return 0; 238 239 /* Remember cwd */ 240 if ((cwd = open(".", O_RDONLY)) < 0) { 241 LOG(log_error, logtype_cnid, "error opening cwd: %s", strerror(errno)); 242 return -1; 243 } 244 245 /* chdir to db_envhome */ 246 if ((chdir(dbd->db_envhome)) != 0) { 247 LOG(log_error, logtype_cnid, "error chdiring to db_env '%s': %s", dbd->db_envhome, strerror(errno)); 248 ret = -1; 249 goto exit; 250 } 251 252 if ((dbd->db_errlog = fopen(DB_ERRLOGFILE, "a")) == NULL) 253 LOG(log_warning, logtype_cnid, "error creating/opening DB errlogfile: %s", strerror(errno)); 254 255 if (dbd->db_errlog != NULL) { 256 dbd->db_env->set_errfile(dbd->db_env, dbd->db_errlog); 257 dbd->db_env->set_msgfile(dbd->db_env, dbd->db_errlog); 258 } 259 260exit: 261 if (cwd != -1) { 262 if ((fchdir(cwd)) != 0) { 263 LOG(log_error, logtype_cnid, "error chdiring back: %s", strerror(errno)); 264 ret = -1; 265 } 266 close(cwd); 267 } 268 return ret; 269} 270 271/* --------------- */ 272static int dbif_logautorem(DBD *dbd) 273{ 274 int ret = 0; 275 int cwd = -1; 276 char **logfiles = NULL; 277 char **file; 278 279 if ( ! dbd->db_filename) 280 /* in memory db */ 281 return 0; 282 283 /* Remember cwd */ 284 if ((cwd = open(".", O_RDONLY)) < 0) { 285 LOG(log_error, logtype_cnid, "error opening cwd: %s", strerror(errno)); 286 return -1; 287 } 288 289 /* chdir to db_envhome */ 290 if ((chdir(dbd->db_envhome)) != 0) { 291 LOG(log_error, logtype_cnid, "error chdiring to db_env '%s': %s", dbd->db_envhome, strerror(errno)); 292 ret = -1; 293 goto exit; 294 } 295 296 if ((ret = dbd->db_env->log_archive(dbd->db_env, &logfiles, 0)) != 0) { 297 LOG(log_error, logtype_cnid, "error getting list of stale logfiles: %s", 298 db_strerror(ret)); 299 dbd->db_env->close(dbd->db_env, 0); 300 dbd->db_env = NULL; 301 ret = -1; 302 goto exit; 303 } 304 305 if (logfiles != NULL) { 306 for (file = logfiles; *file != NULL; file++) { 307 if (unlink(*file) < 0) 308 LOG(log_warning, logtype_cnid, "Error removing stale logfile %s: %s", *file, strerror(errno)); 309 } 310 free(logfiles); 311 } 312 313exit: 314 if (cwd != -1) { 315 if ((fchdir(cwd)) != 0) { 316 LOG(log_error, logtype_cnid, "error chdiring back: %s", strerror(errno)); 317 ret = -1; 318 } 319 close(cwd); 320 } 321 return ret; 322} 323 324/*! 325 * Get lock on db lock file 326 * 327 * @args cmd (r) lock command: 328 * LOCK_FREE: close lockfd 329 * LOCK_UNLOCK: unlock lockm keep lockfd open 330 * LOCK_EXCL: F_WRLCK on lockfd 331 * LOCK_SHRD: F_RDLCK on lockfd 332 * @args dbpath (r) path to lockfile, only used on first call, 333 * later the stored fd is used 334 * @returns LOCK_FREE/LOCK_UNLOCK return 0 on success, -1 on error 335 * LOCK_EXCL/LOCK_SHRD return LOCK_EXCL or LOCK_SHRD respectively on 336 * success, 0 if the lock couldn't be acquired, -1 on other errors 337 */ 338int get_lock(int cmd, const char *dbpath) 339{ 340 static int lockfd = -1; 341 int ret; 342 char lockpath[PATH_MAX]; 343 struct stat st; 344 345 switch (cmd) { 346 case LOCK_FREE: 347 if (lockfd == -1) 348 return -1; 349 close(lockfd); 350 lockfd = -1; 351 return 0; 352 353 case LOCK_UNLOCK: 354 if (lockfd == -1) 355 return -1; 356 return unlock(lockfd, 0, SEEK_SET, 0); 357 358 case LOCK_EXCL: 359 case LOCK_SHRD: 360 if (lockfd == -1) { 361 if ( (strlen(dbpath) + strlen(LOCKFILENAME+1)) > (PATH_MAX - 1) ) { 362 LOG(log_error, logtype_cnid, ".AppleDB pathname too long"); 363 return -1; 364 } 365 strncpy(lockpath, dbpath, PATH_MAX - 1); 366 strcat(lockpath, "/"); 367 strcat(lockpath, LOCKFILENAME); 368 369 if ((lockfd = open(lockpath, O_RDWR | O_CREAT, 0644)) < 0) { 370 LOG(log_error, logtype_cnid, "Error opening lockfile: %s", strerror(errno)); 371 return -1; 372 } 373 374 if ((stat(dbpath, &st)) != 0) { 375 LOG(log_error, logtype_cnid, "Error statting lockfile: %s", strerror(errno)); 376 return -1; 377 } 378 379 if ((chown(lockpath, st.st_uid, st.st_gid)) != 0) { 380 LOG(log_error, logtype_cnid, "Error inheriting lockfile permissions: %s", 381 strerror(errno)); 382 return -1; 383 } 384 } 385 386 if (cmd == LOCK_EXCL) 387 ret = write_lock(lockfd, 0, SEEK_SET, 0); 388 else 389 ret = read_lock(lockfd, 0, SEEK_SET, 0); 390 391 if (ret != 0) { 392 if (cmd == LOCK_SHRD) 393 LOG(log_error, logtype_cnid, "Volume CNID db is locked, try again..."); 394 return 0; 395 } 396 397 LOG(log_debug, logtype_cnid, "get_lock: got %s lock", 398 cmd == LOCK_EXCL ? "LOCK_EXCL" : "LOCK_SHRD"); 399 return cmd; 400 401 default: 402 return -1; 403 } /* switch(cmd) */ 404 405 /* deadc0de, never get here */ 406 return -1; 407} 408 409/* --------------- */ 410DBD *dbif_init(const char *envhome, const char *filename) 411{ 412 DBD *dbd; 413 414 if ( NULL == (dbd = calloc(sizeof(DBD), 1)) ) 415 return NULL; 416 417 /* filename == NULL means in memory db */ 418 if (filename) { 419 if (! envhome) 420 return NULL; 421 422 dbd->db_envhome = strdup(envhome); 423 if (NULL == dbd->db_envhome) { 424 free(dbd); 425 return NULL; 426 } 427 428 dbd->db_filename = strdup(filename); 429 if (NULL == dbd->db_filename) { 430 free(dbd->db_envhome); 431 free(dbd); 432 return NULL; 433 } 434 } 435 436 dbd->db_table[DBIF_CNID].name = "cnid2.db"; 437 dbd->db_table[DBIF_IDX_DEVINO].name = "devino.db"; 438 dbd->db_table[DBIF_IDX_DIDNAME].name = "didname.db"; 439 dbd->db_table[DBIF_IDX_NAME].name = "name.db"; 440 441 dbd->db_table[DBIF_CNID].type = DB_BTREE; 442 dbd->db_table[DBIF_IDX_DEVINO].type = DB_BTREE; 443 dbd->db_table[DBIF_IDX_DIDNAME].type = DB_BTREE; 444 dbd->db_table[DBIF_IDX_NAME].type = DB_BTREE; 445 446 dbd->db_table[DBIF_CNID].openflags = DB_CREATE; 447 dbd->db_table[DBIF_IDX_DEVINO].openflags = DB_CREATE; 448 dbd->db_table[DBIF_IDX_DIDNAME].openflags = DB_CREATE; 449 dbd->db_table[DBIF_IDX_NAME].openflags = DB_CREATE; 450 451 dbd->db_table[DBIF_IDX_NAME].flags = DB_DUPSORT; 452 453 return dbd; 454} 455 456/* 457 We must open the db_env with an absolute pathname, as `dbd` keeps chdir'ing, which 458 breaks e.g. bdb logfile-rotation with relative pathnames. 459 But still we use relative paths with DB_ERRLOGFILE 460 in order to avoid creating absolute paths by copying. Both have no problem with 461 a relative path. 462*/ 463int dbif_env_open(DBD *dbd, struct db_param *dbp, uint32_t dbenv_oflags) 464{ 465 int ret; 466 467 if ((ret = db_env_create(&dbd->db_env, 0))) { 468 LOG(log_error, logtype_cnid, "error creating DB environment: %s", 469 db_strerror(ret)); 470 dbd->db_env = NULL; 471 return -1; 472 } 473 474 dbd->db_param = *dbp; 475 476 if ((dbif_openlog(dbd)) != 0) 477 return -1; 478 479 if (dbenv_oflags & DB_RECOVER) { 480 481 LOG(log_debug, logtype_cnid, "Running recovery"); 482 483 dbd->db_env->set_verbose(dbd->db_env, DB_VERB_RECOVERY, 1); 484 /* Open the database for recovery using DB_PRIVATE option which is faster */ 485 if ((ret = dbd->db_env->open(dbd->db_env, dbd->db_envhome, dbenv_oflags | DB_PRIVATE, 0))) { 486 LOG(log_error, logtype_cnid, "error opening DB environment: %s", 487 db_strerror(ret)); 488 dbd->db_env->close(dbd->db_env, 0); 489 dbd->db_env = NULL; 490 return -1; 491 } 492 dbenv_oflags = (dbenv_oflags & ~DB_RECOVER); 493 494 if (dbd->db_errlog != NULL) 495 fflush(dbd->db_errlog); 496 497 if ((ret = dbd->db_env->close(dbd->db_env, 0))) { 498 LOG(log_error, logtype_cnid, "error closing DB environment after recovery: %s", 499 db_strerror(ret)); 500 dbd->db_env = NULL; 501 return -1; 502 } 503 dbd->db_errlog = NULL; 504 505 if ((ret = db_env_create(&dbd->db_env, 0))) { 506 LOG(log_error, logtype_cnid, "error creating DB environment after recovery: %s", 507 db_strerror(ret)); 508 dbd->db_env = NULL; 509 return -1; 510 } 511 512 if ((dbif_openlog(dbd)) != 0) 513 return -1; 514 515 LOG(log_debug, logtype_cnid, "Finished recovery."); 516 } 517 518 if ((ret = dbd->db_env->set_cachesize(dbd->db_env, 0, 1024 * dbp->cachesize, 0))) { 519 LOG(log_error, logtype_cnid, "error setting DB environment cachesize to %i: %s", 520 dbp->cachesize, db_strerror(ret)); 521 dbd->db_env->close(dbd->db_env, 0); 522 dbd->db_env = NULL; 523 return -1; 524 } 525 526 if ((ret = dbd->db_env->set_lk_max_locks(dbd->db_env, dbp->maxlocks))) { 527 LOG(log_error, logtype_cnid, "error setting DB environment maxlocks to %i: %s", 528 10000, db_strerror(ret)); 529 dbd->db_env->close(dbd->db_env, 0); 530 dbd->db_env = NULL; 531 return -1; 532 } 533 534 if ((ret = dbd->db_env->set_lk_max_objects(dbd->db_env, dbp->maxlockobjs))) { 535 LOG(log_error, logtype_cnid, "error setting DB environment max lockobjects to %i: %s", 536 10000, db_strerror(ret)); 537 dbd->db_env->close(dbd->db_env, 0); 538 dbd->db_env = NULL; 539 return -1; 540 } 541 542 if ((ret = dbd->db_env->open(dbd->db_env, dbd->db_envhome, dbenv_oflags, 0))) { 543 LOG(log_error, logtype_cnid, "error opening DB environment after recovery: %s", 544 db_strerror(ret)); 545 dbd->db_env->close(dbd->db_env, 0); 546 dbd->db_env = NULL; 547 return -1; 548 } 549 550 if ((ret = dbd->db_env->set_flags(dbd->db_env, DB_AUTO_COMMIT, 1))) { 551 LOG(log_error, logtype_cnid, "error setting DB_AUTO_COMMIT flag: %s", 552 db_strerror(ret)); 553 dbd->db_env->close(dbd->db_env, 0); 554 dbd->db_env = NULL; 555 return -1; 556 } 557 558 if (dbp->logfile_autoremove) { 559 if ((dbif_logautorem(dbd)) != 0) 560 return -1; 561 562#if DB_VERSION_MAJOR > 4 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 7) 563 if ((ret = dbd->db_env->log_set_config(dbd->db_env, DB_LOG_AUTO_REMOVE, 1))) { 564 LOG(log_error, logtype_cnid, "error setting DB_LOG_AUTO_REMOVE flag: %s", 565 db_strerror(ret)); 566 dbd->db_env->close(dbd->db_env, 0); 567 dbd->db_env = NULL; 568 return -1; 569 } 570#else 571 if ((ret = dbd->db_env->set_flags(dbd->db_env, DB_LOG_AUTOREMOVE, 1))) { 572 LOG(log_error, logtype_cnid, "error setting DB_LOG_AUTOREMOVE flag: %s", 573 db_strerror(ret)); 574 dbd->db_env->close(dbd->db_env, 0); 575 dbd->db_env = NULL; 576 return -1; 577 } 578#endif 579 } 580 581 return 0; 582} 583 584/* --------------- */ 585int dbif_open(DBD *dbd, struct db_param *dbp, int reindex) 586{ 587 int ret, i, cwd; 588 u_int32_t count; 589 struct stat st; 590 DB *upgrade_db; 591 592 /* Try to upgrade if it's a normal on-disk database */ 593 if (dbd->db_envhome) { 594 /* Remember cwd */ 595 if ((cwd = open(".", O_RDONLY)) < 0) { 596 LOG(log_error, logtype_cnid, "error opening cwd: %s", strerror(errno)); 597 return -1; 598 } 599 600 /* chdir to db_envhome. makes it easier checking for old db files and creating db_errlog file */ 601 if ((chdir(dbd->db_envhome)) != 0) { 602 LOG(log_error, logtype_cnid, "error chdiring to db_env '%s': %s", dbd->db_envhome, strerror(errno)); 603 return -1; 604 } 605 606 if ((stat(dbd->db_filename, &st)) == 0) { 607 LOG(log_debug, logtype_cnid, "See if we can upgrade the CNID database..."); 608 if ((ret = db_create(&upgrade_db, dbd->db_env, 0))) { 609 LOG(log_error, logtype_cnid, "error creating handle for database: %s", db_strerror(ret)); 610 return -1; 611 } 612 if ((ret = upgrade_db->upgrade(upgrade_db, dbd->db_filename, 0))) { 613 LOG(log_error, logtype_cnid, "error upgarding database: %s", db_strerror(ret)); 614 return -1; 615 } 616 if ((ret = upgrade_db->close(upgrade_db, 0))) { 617 LOG(log_error, logtype_cnid, "error closing database: %s", db_strerror(ret)); 618 return -1; 619 } 620 if ((ret = dbd->db_env->txn_checkpoint(dbd->db_env, 0, 0, DB_FORCE))) { 621 LOG(log_error, logtype_cnid, "error forcing checkpoint: %s", db_strerror(ret)); 622 return -1; 623 } 624 LOG(log_debug, logtype_cnid, "Finished BerkeleyBD upgrade check"); 625 } 626 627 if ((fchdir(cwd)) != 0) { 628 LOG(log_error, logtype_cnid, "error chdiring back: %s", strerror(errno)); 629 return -1; 630 } 631 } 632 633 /* Now open databases ... */ 634 for (i = 0; i != DBIF_DB_CNT; i++) { 635 if ((ret = db_create(&dbd->db_table[i].db, dbd->db_env, 0))) { 636 LOG(log_error, logtype_cnid, "error creating handle for database %s: %s", 637 dbd->db_table[i].name, db_strerror(ret)); 638 return -1; 639 } 640 641 if (dbd->db_table[i].flags) { 642 if ((ret = dbd->db_table[i].db->set_flags(dbd->db_table[i].db, 643 dbd->db_table[i].flags))) { 644 LOG(log_error, logtype_cnid, "error setting flags for database %s: %s", 645 dbd->db_table[i].name, db_strerror(ret)); 646 return -1; 647 } 648 } 649 650 if ( ! dbd->db_env) { /* In memory db */ 651 if ((ret = dbd->db_table[i].db->set_cachesize(dbd->db_table[i].db, 652 0, 653 dbp->cachesize, 654 4)) /* split in 4 memory chunks */ 655 < 0) { 656 LOG(log_error, logtype_cnid, "error setting cachesize %u KB for database %s: %s", 657 dbp->cachesize / 1024, dbd->db_table[i].name, db_strerror(ret)); 658 return -1; 659 } 660 } 661 662 if (dbd->db_table[i].db->open(dbd->db_table[i].db, 663 dbd->db_txn, 664 dbd->db_filename, 665 dbd->db_table[i].name, 666 dbd->db_table[i].type, 667 dbd->db_table[i].openflags, 668 0664) < 0) { 669 LOG(log_error, logtype_cnid, "Cant open database"); 670 return -1; 671 } 672 673 if (reindex && i > 0) { 674 LOG(log_info, logtype_cnid, "Truncating CNID index."); 675 if ((ret = dbd->db_table[i].db->truncate(dbd->db_table[i].db, NULL, &count, 0))) { 676 LOG(log_error, logtype_cnid, "error truncating database %s: %s", 677 dbd->db_table[i].name, db_strerror(ret)); 678 return -1; 679 } 680 } 681 } 682 683 /* TODO: Implement CNID DB versioning info on new databases. */ 684 685 /* Associate the secondary with the primary. */ 686 if (reindex) 687 LOG(log_info, logtype_cnid, "Reindexing did/name index..."); 688 if ((ret = dbd->db_table[0].db->associate(dbd->db_table[DBIF_CNID].db, 689 dbd->db_txn, 690 dbd->db_table[DBIF_IDX_DIDNAME].db, 691 didname, 692 (reindex) ? DB_CREATE : 0)) 693 != 0) { 694 LOG(log_error, logtype_cnid, "Failed to associate didname database: %s",db_strerror(ret)); 695 return -1; 696 } 697 if (reindex) 698 LOG(log_info, logtype_cnid, "... done."); 699 700 if (reindex) 701 LOG(log_info, logtype_cnid, "Reindexing dev/ino index..."); 702 if ((ret = dbd->db_table[0].db->associate(dbd->db_table[0].db, 703 dbd->db_txn, 704 dbd->db_table[DBIF_IDX_DEVINO].db, 705 devino, 706 (reindex) ? DB_CREATE : 0)) 707 != 0) { 708 LOG(log_error, logtype_cnid, "Failed to associate devino database: %s",db_strerror(ret)); 709 return -1; 710 } 711 if (reindex) 712 LOG(log_info, logtype_cnid, "... done."); 713 714 if (reindex) 715 LOG(log_info, logtype_cnid, "Reindexing name index..."); 716 717 /* 718 * Upgrading from version 0 to 1 requires adding the name index below which 719 * must be done by specifying the DB_CREATE flag 720 */ 721 uint32_t version = CNID_VERSION; 722 if (dbd->db_envhome && !reindex) { 723 if (dbif_getversion(dbd, &version) == -1) 724 return -1; 725 } 726 727 if ((ret = dbd->db_table[0].db->associate(dbd->db_table[0].db, 728 dbd->db_txn, 729 dbd->db_table[DBIF_IDX_NAME].db, 730 idxname, 731 (reindex 732 || 733 ((CNID_VERSION == CNID_VERSION_1) && (version == CNID_VERSION_0))) 734 ? DB_CREATE : 0)) != 0) { 735 LOG(log_error, logtype_cnid, "Failed to associate name index: %s", db_strerror(ret)); 736 return -1; 737 } 738 if (reindex) 739 LOG(log_info, logtype_cnid, "... done."); 740 741 if ((dbd->db_envhome) && ((ret = dbif_upgrade(dbd)) != 0)) { 742 LOG(log_error, logtype_cnid, "Error upgrading CNID database to version %d", CNID_VERSION); 743 return -1; 744 } 745 746 return 0; 747} 748 749/* ------------------------ */ 750static int dbif_closedb(DBD *dbd) 751{ 752 int i; 753 int ret; 754 int err = 0; 755 756 for (i = DBIF_DB_CNT -1; i >= 0; i--) { 757 if (dbd->db_table[i].db != NULL && (ret = dbd->db_table[i].db->close(dbd->db_table[i].db, 0))) { 758 LOG(log_error, logtype_cnid, "error closing database %s: %s", dbd->db_table[i].name, db_strerror(ret)); 759 err++; 760 } 761 } 762 if (err) 763 return -1; 764 return 0; 765} 766 767/* ------------------------ */ 768int dbif_close(DBD *dbd) 769{ 770 int ret; 771 int err = 0; 772 773 if (dbif_closedb(dbd)) 774 err++; 775 776 if (dbd->db_env != NULL && (ret = dbd->db_env->close(dbd->db_env, 0))) { 777 LOG(log_error, logtype_cnid, "error closing DB environment: %s", db_strerror(ret)); 778 err++; 779 } 780 if (dbd->db_errlog != NULL && fclose(dbd->db_errlog) == EOF) { 781 LOG(log_error, logtype_cnid, "error closing DB logfile: %s", strerror(errno)); 782 err++; 783 } 784 785 free(dbd->db_filename); 786 free(dbd); 787 dbd = NULL; 788 789 if (err) 790 return -1; 791 return 0; 792} 793 794/* 795 In order to support silent database upgrades: 796 destroy env at cnid_dbd shutdown. 797 */ 798int dbif_env_remove(const char *path) 799{ 800 int ret; 801 DBD *dbd; 802 803 LOG(log_debug, logtype_cnid, "Reopening BerkeleyDB environment"); 804 805 if (NULL == (dbd = dbif_init(path, "cnid2.db"))) 806 return -1; 807 808 /* Get db_env handle */ 809 if ((ret = db_env_create(&dbd->db_env, 0))) { 810 LOG(log_error, logtype_cnid, "error creating DB environment: %s", db_strerror(ret)); 811 dbd->db_env = NULL; 812 return -1; 813 } 814 815 if ((dbif_openlog(dbd)) != 0) 816 return -1; 817 818 /* Open environment with recovery */ 819 if ((ret = dbd->db_env->open(dbd->db_env, 820 dbd->db_envhome, 821 DB_CREATE | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_LOCK | DB_INIT_TXN | DB_RECOVER | DB_PRIVATE, 822 0))) { 823 LOG(log_error, logtype_cnid, "error opening DB environment: %s", 824 db_strerror(ret)); 825 dbd->db_env->close(dbd->db_env, 0); 826 dbd->db_env = NULL; 827 return -1; 828 } 829 830 if (dbd->db_errlog != NULL) 831 fflush(dbd->db_errlog); 832 833 /* Remove logfiles */ 834 if ((ret = dbd->db_env->log_archive(dbd->db_env, NULL, DB_ARCH_REMOVE))) { 835 LOG(log_error, logtype_cnid, "error removing transaction logfiles: %s", db_strerror(ret)); 836 return -1; 837 } 838 839 if ((ret = dbd->db_env->close(dbd->db_env, 0))) { 840 LOG(log_error, logtype_cnid, "error closing DB environment after recovery: %s", db_strerror(ret)); 841 dbd->db_env = NULL; 842 return -1; 843 } 844 845 LOG(log_debug, logtype_cnid, "BerkeleyDB environment recovery done."); 846 847 /* Get a new db_env handle and then remove environment */ 848 if ((ret = db_env_create(&dbd->db_env, 0))) { 849 LOG(log_error, logtype_cnid, "error acquiring db_end handle: %s", db_strerror(ret)); 850 dbd->db_env = NULL; 851 return -1; 852 } 853 if ((ret = dbd->db_env->remove(dbd->db_env, dbd->db_envhome, 0))) { 854 LOG(log_error, logtype_cnid, "error removing BerkeleyDB environment: %s", db_strerror(ret)); 855 return -1; 856 } 857 858 LOG(log_debug, logtype_cnid, "Removed BerkeleyDB environment."); 859 860 return 0; 861} 862 863/* 864 * The following three functions are wrappers for DB->get(), DB->put() and DB->del(). 865 * All three return -1 on error. dbif_get()/dbif_del return 1 if the key was found and 0 866 * otherwise. dbif_put() returns 0 if key/val was successfully updated and 1 if 867 * the DB_NOOVERWRITE flag was specified and the key already exists. 868 * 869 * All return codes other than DB_NOTFOUND and DB_KEYEXIST from the DB->() 870 * functions are not expected and therefore error conditions. 871 */ 872 873int dbif_get(DBD *dbd, const int dbi, DBT *key, DBT *val, u_int32_t flags) 874{ 875 int ret; 876 877 ret = dbd->db_table[dbi].db->get(dbd->db_table[dbi].db, 878 dbd->db_txn, 879 key, 880 val, 881 flags); 882 883 if (ret == DB_NOTFOUND) 884 return 0; 885 if (ret) { 886 LOG(log_error, logtype_cnid, "error retrieving value from %s: %s", 887 dbd->db_table[dbi].name, db_strerror(ret)); 888 return -1; 889 } else 890 return 1; 891} 892 893/* search by secondary return primary */ 894int dbif_pget(DBD *dbd, const int dbi, DBT *key, DBT *pkey, DBT *val, u_int32_t flags) 895{ 896 int ret; 897 898 ret = dbd->db_table[dbi].db->pget(dbd->db_table[dbi].db, 899 dbd->db_txn, 900 key, 901 pkey, 902 val, 903 flags); 904 905 if (ret == DB_NOTFOUND || ret == DB_SECONDARY_BAD) { 906 return 0; 907 } 908 if (ret) { 909 LOG(log_error, logtype_cnid, "error retrieving value from %s: %s", 910 dbd->db_table[dbi].name, db_strerror(ret)); 911 return -1; 912 } else 913 return 1; 914} 915 916/* -------------------------- */ 917int dbif_put(DBD *dbd, const int dbi, DBT *key, DBT *val, u_int32_t flags) 918{ 919 int ret; 920 921 if (dbif_txn_begin(dbd) < 0) { 922 LOG(log_error, logtype_cnid, "error setting key/value in %s", dbd->db_table[dbi].name); 923 return -1; 924 } 925 926 ret = dbd->db_table[dbi].db->put(dbd->db_table[dbi].db, 927 dbd->db_txn, 928 key, 929 val, 930 flags); 931 932 933 if (ret) { 934 if ((flags & DB_NOOVERWRITE) && ret == DB_KEYEXIST) { 935 return 1; 936 } else { 937 LOG(log_error, logtype_cnid, "error setting key/value in %s: %s", 938 dbd->db_table[dbi].name, db_strerror(ret)); 939 return -1; 940 } 941 } else 942 return 0; 943} 944 945int dbif_del(DBD *dbd, const int dbi, DBT *key, u_int32_t flags) 946{ 947 int ret; 948 949 /* For cooperation with the dbd utility and its usage of a cursor */ 950 if (dbd->db_cur) { 951 dbd->db_cur->close(dbd->db_cur); 952 dbd->db_cur = NULL; 953 } 954 955 if (dbif_txn_begin(dbd) < 0) { 956 LOG(log_error, logtype_cnid, "error deleting key/value from %s", dbd->db_table[dbi].name); 957 return -1; 958 } 959 960 ret = dbd->db_table[dbi].db->del(dbd->db_table[dbi].db, 961 dbd->db_txn, 962 key, 963 flags); 964 965 if (ret == DB_NOTFOUND) { 966 LOG(log_info, logtype_cnid, "key not found"); 967 return 0; 968 } 969 if (ret) { 970 LOG(log_error, logtype_cnid, "error deleting key/value from %s: %s", 971 dbd->db_table[dbi].name, db_strerror(ret)); 972 return -1; 973 } else 974 return 1; 975} 976 977/*! 978 * Search the database by name 979 * 980 * @param resbuf (w) buffer for search results CNIDs, maxsize is assumed to be 981 * DBD_MAX_SRCH_RSLTS * sizefof(cnid_t) 982 * 983 * @returns -1 on error, 0 when nothing found, else the number of matches 984 */ 985int dbif_search(DBD *dbd, DBT *key, char *resbuf) 986{ 987 int ret = 0; 988 int count = 0; 989 DBC *cursorp = NULL; 990 DBT pkey, data; 991 char *cnids = resbuf; 992 cnid_t cnid; 993 char *namebkp = key->data; 994 int namelenbkp = key->size; 995 996 memset(&pkey, 0, sizeof(DBT)); 997 memset(&data, 0, sizeof(DBT)); 998 999 /* Get a cursor */ 1000 ret = dbd->db_table[DBIF_IDX_NAME].db->cursor(dbd->db_table[DBIF_IDX_NAME].db, 1001 NULL, 1002 &cursorp, 1003 0); 1004 if (ret != 0) { 1005 LOG(log_error, logtype_cnid, "Couldn't create cursor: %s", db_strerror(ret)); 1006 ret = -1; 1007 goto exit; 1008 } 1009 1010 ret = cursorp->pget(cursorp, key, &pkey, &data, DB_SET_RANGE); 1011 while (count < DBD_MAX_SRCH_RSLTS && ret != DB_NOTFOUND) { 1012 if (!((namelenbkp <= key->size) && (strncmp(namebkp, key->data, namelenbkp) == 0))) 1013 break; 1014 count++; 1015 memcpy(cnids, pkey.data, sizeof(cnid_t)); 1016 memcpy(&cnid, pkey.data, sizeof(cnid_t)); 1017 cnids += sizeof(cnid_t); 1018 LOG(log_debug, logtype_cnid, "match: CNID %" PRIu32, ntohl(cnid)); 1019 1020 ret = cursorp->pget(cursorp, key, &pkey, &data, DB_NEXT); 1021 } 1022 1023 ret = count; 1024 1025exit: 1026 if (cursorp != NULL) 1027 cursorp->close(cursorp); 1028 return ret; 1029} 1030 1031int dbif_txn_begin(DBD *dbd) 1032{ 1033 int ret; 1034 1035 /* If we already have an active txn, just return */ 1036 if (dbd->db_txn) 1037 return 0; 1038 1039 /* If our DBD has no env, just return (-> in memory db) */ 1040 if (dbd->db_env == NULL) 1041 return 0; 1042 1043 ret = dbd->db_env->txn_begin(dbd->db_env, NULL, &dbd->db_txn, 0); 1044 1045 if (ret) { 1046 LOG(log_error, logtype_cnid, "error starting transaction: %s", db_strerror(ret)); 1047 return -1; 1048 } else 1049 return 0; 1050} 1051 1052int dbif_txn_commit(DBD *dbd) 1053{ 1054 int ret; 1055 1056 if (! dbd->db_txn) 1057 return 0; 1058 1059 /* If our DBD has no env, just return (-> in memory db) */ 1060 if (dbd->db_env == NULL) 1061 return 0; 1062 1063 ret = dbd->db_txn->commit(dbd->db_txn, 0); 1064 dbd->db_txn = NULL; 1065 1066 if (ret) { 1067 LOG(log_error, logtype_cnid, "error committing transaction: %s", db_strerror(ret)); 1068 return -1; 1069 } else 1070 return 1; 1071} 1072 1073int dbif_txn_abort(DBD *dbd) 1074{ 1075 int ret; 1076 1077 if (! dbd->db_txn) 1078 return 0; 1079 1080 /* If our DBD has no env, just return (-> in memory db) */ 1081 if (dbd->db_env == NULL) 1082 return 0; 1083 1084 ret = dbd->db_txn->abort(dbd->db_txn); 1085 dbd->db_txn = NULL; 1086 1087 if (ret) { 1088 LOG(log_error, logtype_cnid, "error aborting transaction: %s", db_strerror(ret)); 1089 return -1; 1090 } else 1091 return 0; 1092} 1093 1094/* 1095 ret = 1 -> commit txn if db_param.txn_frequency 1096 ret = 0 -> abort txn db_param.txn_frequency -> exit! 1097 anything else -> exit! 1098 1099 @returns 0 on success (abort or commit), -1 on error 1100*/ 1101int dbif_txn_close(DBD *dbd, int ret) 1102{ 1103 if (ret == 0) { 1104 if (dbif_txn_abort(dbd) < 0) { 1105 LOG( log_error, logtype_cnid, "Fatal error aborting transaction. Exiting!"); 1106 return -1; 1107 } 1108 } else if (ret == 1) { 1109 ret = dbif_txn_commit(dbd); 1110 if ( ret < 0) { 1111 LOG( log_error, logtype_cnid, "Fatal error committing transaction. Exiting!"); 1112 return -1; 1113 } 1114 } else { 1115 return -1; 1116 } 1117 1118 return 0; 1119} 1120 1121int dbif_txn_checkpoint(DBD *dbd, u_int32_t kbyte, u_int32_t min, u_int32_t flags) 1122{ 1123 int ret; 1124 ret = dbd->db_env->txn_checkpoint(dbd->db_env, kbyte, min, flags); 1125 if (ret) { 1126 LOG(log_error, logtype_cnid, "error checkpointing transaction susystem: %s", db_strerror(ret)); 1127 return -1; 1128 } else 1129 return 0; 1130} 1131 1132int dbif_count(DBD *dbd, const int dbi, u_int32_t *count) 1133{ 1134 int ret; 1135 DB_BTREE_STAT *sp; 1136 DB *db = dbd->db_table[dbi].db; 1137 1138 ret = db->stat(db, NULL, &sp, 0); 1139 1140 if (ret) { 1141 LOG(log_error, logtype_cnid, "error getting stat infotmation on database: %s", db_strerror(ret)); 1142 return -1; 1143 } 1144 1145 *count = sp->bt_ndata; 1146 free(sp); 1147 1148 return 0; 1149} 1150 1151int dbif_copy_rootinfokey(DBD *srcdbd, DBD *destdbd) 1152{ 1153 DBT key, data; 1154 int rc; 1155 1156 memset(&key, 0, sizeof(key)); 1157 memset(&data, 0, sizeof(data)); 1158 1159 key.data = ROOTINFO_KEY; 1160 key.size = ROOTINFO_KEYLEN; 1161 1162 if ((rc = dbif_get(srcdbd, DBIF_CNID, &key, &data, 0)) <= 0) { 1163 LOG(log_error, logtype_cnid, "dbif_copy_rootinfokey: Error getting rootinfo record"); 1164 return -1; 1165 } 1166 1167 memset(&key, 0, sizeof(key)); 1168 key.data = ROOTINFO_KEY; 1169 key.size = ROOTINFO_KEYLEN; 1170 1171 if ((rc = dbif_put(destdbd, DBIF_CNID, &key, &data, 0))) { 1172 LOG(log_error, logtype_cnid, "dbif_copy_rootinfokey: Error writing rootinfo key"); 1173 return -1; 1174 } 1175 1176 return 0; 1177} 1178 1179int dbif_dump(DBD *dbd, int dumpindexes) 1180{ 1181 int rc; 1182 uint32_t max = 0, count = 0, cnid, type, did, lastid, version; 1183 uint64_t dev, ino; 1184 time_t stamp; 1185 DBC *cur; 1186 DBT key = { 0 }, data = { 0 }; 1187 DB *db = dbd->db_table[DBIF_CNID].db; 1188 char *typestring[2] = {"f", "d"}; 1189 char timebuf[64]; 1190 1191 printf("CNID database dump:\n"); 1192 1193 rc = db->cursor(db, NULL, &cur, 0); 1194 if (rc) { 1195 LOG(log_error, logtype_cnid, "Couldn't create cursor: %s", db_strerror(rc)); 1196 return -1; 1197 } 1198 1199 cur->c_get(cur, &key, &data, DB_FIRST); 1200 while (rc == 0) { 1201 /* Parse and print data */ 1202 memcpy(&cnid, key.data, 4); 1203 cnid = ntohl(cnid); 1204 if (cnid > max) 1205 max = cnid; 1206 1207 /* Rootinfo node ? */ 1208 if (cnid == 0) { 1209 memcpy(&stamp, (char *)data.data + CNID_DEV_OFS, sizeof(time_t)); 1210 memcpy(&lastid, (char *)data.data + CNID_TYPE_OFS, CNID_TYPE_LEN); 1211 lastid = ntohl(lastid); 1212 memcpy(&version, (char *)data.data + CNID_DID_OFS, CNID_DID_LEN); 1213 version = ntohl(version); 1214 1215 strftime(timebuf, sizeof(timebuf), "%b %d %Y %H:%M:%S", localtime(&stamp)); 1216 printf("CNID db version: %u, dbd stamp: 0x%08x (%s), next free CNID: %u\n", 1217 version, (unsigned int)stamp, timebuf, lastid + 1); 1218 } else { 1219 /* dev */ 1220 memcpy(&dev, (char *)data.data + CNID_DEV_OFS, 8); 1221 dev = ntoh64(dev); 1222 /* ino */ 1223 memcpy(&ino, (char *)data.data + CNID_INO_OFS, 8); 1224 ino = ntoh64(ino); 1225 /* type */ 1226 memcpy(&type, (char *)data.data + CNID_TYPE_OFS, 4); 1227 type = ntohl(type); 1228 /* did */ 1229 memcpy(&did, (char *)data.data + CNID_DID_OFS, 4); 1230 did = ntohl(did); 1231 1232 count++; 1233 printf("id: %10u, did: %10u, type: %s, dev: 0x%llx, ino: 0x%016llx, name: %s\n", 1234 cnid, did, typestring[type], 1235 (long long unsigned int)dev, (long long unsigned int)ino, 1236 (char *)data.data + CNID_NAME_OFS); 1237 1238 } 1239 1240 rc = cur->c_get(cur, &key, &data, DB_NEXT); 1241 } 1242 1243 if (rc != DB_NOTFOUND) { 1244 LOG(log_error, logtype_cnid, "Error iterating over btree: %s", db_strerror(rc)); 1245 return -1; 1246 } 1247 1248 rc = cur->c_close(cur); 1249 if (rc) { 1250 LOG(log_error, logtype_cnid, "Couldn't close cursor: %s", db_strerror(rc)); 1251 return -1; 1252 } 1253 printf("%u CNIDs in database. Max CNID: %u.\n", count, max); 1254 1255 /* Dump indexes too ? */ 1256 if (dumpindexes) { 1257 /* DBIF_IDX_DEVINO */ 1258 printf("\ndev/inode index:\n"); 1259 count = 0; 1260 db = dbd->db_table[DBIF_IDX_DEVINO].db; 1261 rc = db->cursor(db, NULL, &cur, 0); 1262 if (rc) { 1263 LOG(log_error, logtype_cnid, "Couldn't create cursor: %s", db_strerror(rc)); 1264 return -1; 1265 } 1266 1267 cur->c_get(cur, &key, &data, DB_FIRST); 1268 while (rc == 0) { 1269 /* Parse and print data */ 1270 1271 /* cnid */ 1272 memcpy(&cnid, data.data, CNID_LEN); 1273 cnid = ntohl(cnid); 1274 if (cnid == 0) { 1275 /* Rootinfo node */ 1276 } else { 1277 /* dev */ 1278 memcpy(&dev, key.data, CNID_DEV_LEN); 1279 dev = ntoh64(dev); 1280 /* ino */ 1281 memcpy(&ino, (char *)key.data + CNID_DEV_LEN, CNID_INO_LEN); 1282 ino = ntoh64(ino); 1283 1284 printf("id: %10u <== dev: 0x%llx, ino: 0x%016llx\n", 1285 cnid, (unsigned long long int)dev, (unsigned long long int)ino); 1286 count++; 1287 } 1288 rc = cur->c_get(cur, &key, &data, DB_NEXT); 1289 } 1290 if (rc != DB_NOTFOUND) { 1291 LOG(log_error, logtype_cnid, "Error iterating over btree: %s", db_strerror(rc)); 1292 return -1; 1293 } 1294 1295 rc = cur->c_close(cur); 1296 if (rc) { 1297 LOG(log_error, logtype_cnid, "Couldn't close cursor: %s", db_strerror(rc)); 1298 return -1; 1299 } 1300 printf("%u items\n", count); 1301 1302 /* Now dump DBIF_IDX_DIDNAME */ 1303 printf("\ndid/name index:\n"); 1304 count = 0; 1305 db = dbd->db_table[DBIF_IDX_DIDNAME].db; 1306 rc = db->cursor(db, NULL, &cur, 0); 1307 if (rc) { 1308 LOG(log_error, logtype_cnid, "Couldn't create cursor: %s", db_strerror(rc)); 1309 return -1; 1310 } 1311 1312 cur->c_get(cur, &key, &data, DB_FIRST); 1313 while (rc == 0) { 1314 /* Parse and print data */ 1315 1316 /* cnid */ 1317 memcpy(&cnid, data.data, CNID_LEN); 1318 cnid = ntohl(cnid); 1319 if (cnid == 0) { 1320 /* Rootinfo node */ 1321 } else { 1322 /* did */ 1323 memcpy(&did, key.data, CNID_LEN); 1324 did = ntohl(did); 1325 1326 printf("id: %10u <== did: %10u, name: %s\n", cnid, did, (char *)key.data + CNID_LEN); 1327 count++; 1328 } 1329 rc = cur->c_get(cur, &key, &data, DB_NEXT); 1330 } 1331 if (rc != DB_NOTFOUND) { 1332 LOG(log_error, logtype_cnid, "Error iterating over btree: %s", db_strerror(rc)); 1333 return -1; 1334 } 1335 1336 rc = cur->c_close(cur); 1337 if (rc) { 1338 LOG(log_error, logtype_cnid, "Couldn't close cursor: %s", db_strerror(rc)); 1339 return -1; 1340 } 1341 printf("%u items\n", count); 1342 } 1343 1344 return 0; 1345} 1346 1347/* 1348 Iterates over dbd, returning cnids. 1349 Uses in-value of cnid to seek to that cnid, then gets next and return that in cnid. 1350 If close=1, close cursor. 1351 Return -1 on error, 0 on EOD (end-of-database), 1 if returning cnid. 1352*/ 1353int dbif_idwalk(DBD *dbd, cnid_t *cnid, int close) 1354{ 1355 int rc; 1356 int flag; 1357 cnid_t id; 1358 1359 static DBT key = { 0 }, data = { 0 }; 1360 DB *db = dbd->db_table[DBIF_CNID].db; 1361 1362 if (close) { 1363 if (dbd->db_cur) { 1364 dbd->db_cur->close(dbd->db_cur); 1365 dbd->db_cur = NULL; 1366 } 1367 return 0; 1368 } 1369 1370 /* An dbif_del will have closed our cursor too */ 1371 if ( ! dbd->db_cur ) { 1372 if ((rc = db->cursor(db, NULL, &dbd->db_cur, 0)) != 0) { 1373 LOG(log_error, logtype_cnid, "Couldn't create cursor: %s", db_strerror(rc)); 1374 return -1; 1375 } 1376 flag = DB_SET_RANGE; /* This will seek to next cnid after the one just deleted */ 1377 id = htonl(*cnid); 1378 key.data = &id; 1379 key.size = sizeof(cnid_t); 1380 } else 1381 flag = DB_NEXT; 1382 1383 if ((rc = dbd->db_cur->get(dbd->db_cur, &key, &data, flag)) == 0) { 1384 memcpy(cnid, key.data, sizeof(cnid_t)); 1385 *cnid = ntohl(*cnid); 1386 return 1; 1387 } 1388 1389 if (rc != DB_NOTFOUND) { 1390 LOG(log_error, logtype_cnid, "Error iterating over btree: %s", db_strerror(rc)); 1391 dbd->db_cur->close(dbd->db_cur); 1392 dbd->db_cur = NULL; 1393 return -1; 1394 } 1395 1396 if (dbd->db_cur) { 1397 dbd->db_cur->close(dbd->db_cur); 1398 dbd->db_cur = NULL; 1399 } 1400 1401 return 0; 1402} 1403