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