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