1/* 2 Copyright (c) 2009 Frank Lahm <franklahm@gmail.com> 3 4 This program is free software; you can redistribute it and/or modify 5 it under the terms of the GNU General Public License as published by 6 the Free Software Foundation; either version 2 of the License, or 7 (at your option) any later version. 8 9 This program is distributed in the hope that it will be useful, 10 but WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 GNU General Public License for more details. 13*/ 14 15#ifdef HAVE_CONFIG_H 16#include "config.h" 17#endif /* HAVE_CONFIG_H */ 18 19#include <unistd.h> 20#include <stdlib.h> 21#include <sys/types.h> 22#include <sys/stat.h> 23#include <dirent.h> 24#include <fcntl.h> 25#include <string.h> 26#include <errno.h> 27#include <setjmp.h> 28 29#include <atalk/adouble.h> 30#include <atalk/unicode.h> 31#include <atalk/volinfo.h> 32#include <atalk/cnid_dbd_private.h> 33#include <atalk/volume.h> 34#include <atalk/ea.h> 35#include <atalk/util.h> 36#include <atalk/acl.h> 37 38#include "cmd_dbd.h" 39#include "dbif.h" 40#include "db_param.h" 41#include "dbd.h" 42 43/* Some defines to ease code parsing */ 44#define ADDIR_OK (addir_ok == 0) 45#define ADFILE_OK (adfile_ok == 0) 46 47 48static struct volinfo *myvolinfo; 49static char cwdbuf[MAXPATHLEN+1]; 50static DBD *dbd; 51static DBD *dbd_rebuild; 52static dbd_flags_t dbd_flags; 53static char stamp[CNID_DEV_LEN]; 54static char *netatalk_dirs[] = { 55 ".AppleDB", 56 ".AppleDesktop", 57 NULL 58}; 59static char *special_dirs[] = { 60 ".zfs", 61 NULL 62}; 63static struct cnid_dbd_rqst rqst; 64static struct cnid_dbd_rply rply; 65static jmp_buf jmp; 66static struct vol volume; /* fake it for ea_open */ 67static char pname[MAXPATHLEN] = "../"; 68 69/* 70 Taken form afpd/desktop.c 71*/ 72static char *utompath(char *upath) 73{ 74 static char mpath[ MAXPATHLEN + 2]; /* for convert_charset dest_len parameter +2 */ 75 char *m, *u; 76 uint16_t flags = CONV_IGNORE | CONV_UNESCAPEHEX; 77 size_t outlen; 78 79 if (!upath) 80 return NULL; 81 82 m = mpath; 83 u = upath; 84 outlen = strlen(upath); 85 86 if ((myvolinfo->v_casefold & AFPVOL_UTOMUPPER)) 87 flags |= CONV_TOUPPER; 88 else if ((myvolinfo->v_casefold & AFPVOL_UTOMLOWER)) 89 flags |= CONV_TOLOWER; 90 91 if ((myvolinfo->v_flags & AFPVOL_EILSEQ)) { 92 flags |= CONV__EILSEQ; 93 } 94 95 /* convert charsets */ 96 if ((size_t)-1 == ( outlen = convert_charset(myvolinfo->v_volcharset, 97 CH_UTF8_MAC, 98 myvolinfo->v_maccharset, 99 u, outlen, mpath, MAXPATHLEN, &flags)) ) { 100 dbd_log( LOGSTD, "Conversion from %s to %s for %s failed.", 101 myvolinfo->v_volcodepage, myvolinfo->v_maccodepage, u); 102 return NULL; 103 } 104 105 return(m); 106} 107 108/* 109 Taken form afpd/desktop.c 110*/ 111static char *mtoupath(char *mpath) 112{ 113 static char upath[ MAXPATHLEN + 2]; /* for convert_charset dest_len parameter +2 */ 114 char *m, *u; 115 size_t inplen; 116 size_t outlen; 117 u_int16_t flags = 0; 118 119 if (!mpath) 120 return NULL; 121 122 if ( *mpath == '\0' ) { 123 return( "." ); 124 } 125 126 /* set conversion flags */ 127 if (!(myvolinfo->v_flags & AFPVOL_NOHEX)) 128 flags |= CONV_ESCAPEHEX; 129 if (!(myvolinfo->v_flags & AFPVOL_USEDOTS)) 130 flags |= CONV_ESCAPEDOTS; 131 132 if ((myvolinfo->v_casefold & AFPVOL_MTOUUPPER)) 133 flags |= CONV_TOUPPER; 134 else if ((myvolinfo->v_casefold & AFPVOL_MTOULOWER)) 135 flags |= CONV_TOLOWER; 136 137 if ((myvolinfo->v_flags & AFPVOL_EILSEQ)) { 138 flags |= CONV__EILSEQ; 139 } 140 141 m = mpath; 142 u = upath; 143 144 inplen = strlen(m); 145 outlen = MAXPATHLEN; 146 147 if ((size_t)-1 == (outlen = convert_charset(CH_UTF8_MAC, 148 myvolinfo->v_volcharset, 149 myvolinfo->v_maccharset, 150 m, inplen, u, outlen, &flags)) ) { 151 dbd_log( LOGSTD, "conversion from UTF8-MAC to %s for %s failed.", 152 myvolinfo->v_volcodepage, mpath); 153 return NULL; 154 } 155 156 return( upath ); 157} 158 159#if 0 160/* 161 Check if "name" is pointing to 162 a) an object inside the current volume (return 0) 163 b) an object outside the current volume (return 1) 164 Then stats the pointed to object and if it is a dir ors ADFLAGS_DIR to *adflags 165 Return -1 on any serious error. 166 */ 167static int check_symlink(const char *name, int *adflags) 168{ 169 int cwd; 170 ssize_t len; 171 char pathbuf[MAXPATHLEN + 1]; 172 char *sep; 173 struct stat st; 174 175 if ((len = readlink(name, pathbuf, MAXPATHLEN)) == -1) { 176 dbd_log(LOGSTD, "Error reading link info for '%s/%s': %s", 177 cwdbuf, name, strerror(errno)); 178 return -1; 179 } 180 pathbuf[len] = 0; 181 182 if ((stat(pathbuf, &st)) != 0) { 183 dbd_log(LOGSTD, "stat error '%s': %s", pathbuf, strerror(errno)); 184 } 185 186 /* Remember cwd */ 187 if ((cwd = open(".", O_RDONLY)) < 0) { 188 dbd_log(LOGSTD, "error opening cwd '%s': %s", cwdbuf, strerror(errno)); 189 return -1; 190 } 191 192 if (S_ISDIR(st.st_mode)) { 193 *adflags |= ADFLAGS_DIR; 194 } else { /* file */ 195 /* get basename from path */ 196 if ((sep = strrchr(pathbuf, '/')) == NULL) 197 /* just a file at the same level */ 198 return 0; 199 sep = 0; /* pathbuf now contains the directory*/ 200 } 201 202 /* fchdir() to pathbuf so we can easily get its path with getcwd() */ 203 if ((chdir(pathbuf)) != 0) { 204 dbd_log(LOGSTD, "Cant chdir to '%s': %s", pathbuf, strerror(errno)); 205 return -1; 206 } 207 208 if ((getcwd(pathbuf, MAXPATHLEN)) == NULL) { 209 dbd_log(LOGSTD, "Cant get symlink'ed dir '%s/%s': %s", cwdbuf, pathbuf, strerror(errno)); 210 if ((fchdir(cwd)) != 0) 211 /* We're foobared */ 212 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */ 213 return -1; 214 } 215 216 if ((fchdir(cwd)) != 0) 217 /* We're foobared */ 218 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */ 219 220 /* 221 We now have the symlink target dir as absoulte path in pathbuf 222 and can compare it with the currents volume path 223 */ 224 int i = 0; 225 while (myvolinfo->v_path[i]) { 226 if ((pathbuf[i] == 0) || (myvolinfo->v_path[i] != pathbuf[i])) { 227 dbd_log( LOGDEBUG, "extra-share symlink '%s/%s', following", cwdbuf, name); 228 return 1; 229 } 230 i++; 231 } 232 233 dbd_log( LOGDEBUG, "intra-share symlink '%s/%s', not following", cwdbuf, name); 234 return 0; 235} 236#endif 237 238/* 239 Check for wrong encoding e.g. "." at the beginning is not CAP encoded (:2e) although volume is default !AFPVOL_USEDOTS. 240 We do it by roundtripiping from volcharset to UTF8-MAC and back and then compare the result. 241*/ 242static int check_name_encoding(char *uname) 243{ 244 char *roundtripped; 245 246 roundtripped = mtoupath(utompath(uname)); 247 if (!roundtripped) { 248 dbd_log( LOGSTD, "Error checking encoding for '%s/%s'", cwdbuf, uname); 249 return -1; 250 } 251 252 if ( STRCMP(uname, !=, roundtripped)) { 253 dbd_log( LOGSTD, "Bad encoding for '%s/%s'", cwdbuf, uname); 254 return -1; 255 } 256 257 return 0; 258} 259 260/* 261 Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop" 262 Returns pointer to name or NULL. 263*/ 264static const char *check_netatalk_dirs(const char *name) 265{ 266 int c; 267 268 for (c=0; netatalk_dirs[c]; c++) { 269 if ((strcmp(name, netatalk_dirs[c])) == 0) 270 return netatalk_dirs[c]; 271 } 272 return NULL; 273} 274 275/* 276 Check for special names 277 Returns pointer to name or NULL. 278*/ 279static const char *check_special_dirs(const char *name) 280{ 281 int c; 282 283 for (c=0; special_dirs[c]; c++) { 284 if ((strcmp(name, special_dirs[c])) == 0) 285 return special_dirs[c]; 286 } 287 return NULL; 288} 289 290/* 291 Check for .AppleDouble file, create if missing 292*/ 293static int check_adfile(const char *fname, const struct stat *st) 294{ 295 int ret, adflags; 296 struct adouble ad; 297 char *adname; 298 299 if (dbd_flags & DBD_FLAGS_CLEANUP) 300 return 0; 301 302 if (S_ISREG(st->st_mode)) 303 adflags = 0; 304 else 305 adflags = ADFLAGS_DIR; 306 307 adname = myvolinfo->ad_path(fname, adflags); 308 309 if ((ret = access( adname, F_OK)) != 0) { 310 if (errno != ENOENT) { 311 dbd_log(LOGSTD, "Access error for ad-file '%s/%s': %s", 312 cwdbuf, adname, strerror(errno)); 313 return -1; 314 } 315 /* Missing. Log and create it */ 316 dbd_log(LOGSTD, "Missing AppleDouble file '%s/%s'", cwdbuf, adname); 317 318 if (dbd_flags & DBD_FLAGS_SCAN) 319 /* Scan only requested, dont change anything */ 320 return -1; 321 322 /* Create ad file */ 323 ad_init(&ad, myvolinfo->v_adouble, myvolinfo->v_ad_options); 324 325 if ((ret = ad_open_metadata( fname, adflags, O_CREAT, &ad)) != 0) { 326 dbd_log( LOGSTD, "Error creating AppleDouble file '%s/%s': %s", 327 cwdbuf, adname, strerror(errno)); 328 329 return -1; 330 } 331 332 /* Set name in ad-file */ 333 ad_setname(&ad, utompath((char *)fname)); 334 ad_flush(&ad); 335 ad_close_metadata(&ad); 336 337 chown(adname, st->st_uid, st->st_gid); 338 /* FIXME: should we inherit mode too here ? */ 339#if 0 340 chmod(adname, st->st_mode); 341#endif 342 } else { 343 ad_init(&ad, myvolinfo->v_adouble, myvolinfo->v_ad_options); 344 if (ad_open_metadata( fname, adflags, O_RDONLY, &ad) != 0) { 345 dbd_log( LOGSTD, "Error opening AppleDouble file for '%s/%s'", cwdbuf, fname); 346 return -1; 347 } 348 ad_close_metadata(&ad); 349 } 350 return 0; 351} 352 353/* 354 Remove all files with file::EA* from adouble dir 355*/ 356static void remove_eafiles(const char *name, struct ea *ea) 357{ 358 DIR *dp = NULL; 359 struct dirent *ep; 360 char eaname[MAXPATHLEN]; 361 362 strlcpy(eaname, name, sizeof(eaname)); 363 if (strlcat(eaname, "::EA", sizeof(eaname)) >= sizeof(eaname)) { 364 dbd_log(LOGSTD, "name too long: '%s/%s/%s'", cwdbuf, ADv2_DIRNAME, name); 365 return; 366 } 367 368 if ((chdir(ADv2_DIRNAME)) != 0) { 369 dbd_log(LOGSTD, "Couldn't chdir to '%s/%s': %s", 370 cwdbuf, ADv2_DIRNAME, strerror(errno)); 371 return; 372 } 373 374 if ((dp = opendir(".")) == NULL) { 375 dbd_log(LOGSTD, "Couldn't open the directory '%s/%s': %s", 376 cwdbuf, ADv2_DIRNAME, strerror(errno)); 377 return; 378 } 379 380 while ((ep = readdir(dp))) { 381 if (strstr(ep->d_name, eaname) != NULL) { 382 dbd_log(LOGSTD, "Removing EA file: '%s/%s/%s'", 383 cwdbuf, ADv2_DIRNAME, ep->d_name); 384 if ((unlink(ep->d_name)) != 0) { 385 dbd_log(LOGSTD, "Error unlinking EA file '%s/%s/%s': %s", 386 cwdbuf, ADv2_DIRNAME, ep->d_name, strerror(errno)); 387 } 388 } /* if */ 389 } /* while */ 390 391 if (dp) 392 closedir(dp); 393 394} 395 396/* 397 Check Extended Attributes files 398*/ 399static int check_eafiles(const char *fname) 400{ 401 unsigned int count = 0; 402 int ret = 0, remove; 403 struct ea ea; 404 struct stat st; 405 char *eaname; 406 407 if ((ret = ea_open(&volume, fname, EA_RDWR, &ea)) != 0) { 408 if (errno == ENOENT) 409 return 0; 410 dbd_log(LOGSTD, "Error calling ea_open for file: %s/%s, removing EA files", cwdbuf, fname); 411 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) 412 remove_eafiles(fname, &ea); 413 return -1; 414 } 415 416 /* Check all EAs */ 417 while (count < ea.ea_count) { 418 dbd_log(LOGDEBUG, "EA: %s", (*ea.ea_entries)[count].ea_name); 419 remove = 0; 420 421 eaname = ea_path(&ea, (*ea.ea_entries)[count].ea_name, 0); 422 423 if (lstat(eaname, &st) != 0) { 424 if (errno == ENOENT) 425 dbd_log(LOGSTD, "Missing EA: %s/%s", cwdbuf, eaname); 426 else 427 dbd_log(LOGSTD, "Bogus EA: %s/%s", cwdbuf, eaname); 428 remove = 1; 429 } else if (st.st_size != (*ea.ea_entries)[count].ea_size) { 430 dbd_log(LOGSTD, "Bogus EA: %s/%s, removing it...", cwdbuf, eaname); 431 remove = 1; 432 if ((unlink(eaname)) != 0) 433 dbd_log(LOGSTD, "Error removing EA file '%s/%s': %s", 434 cwdbuf, eaname, strerror(errno)); 435 } 436 437 if (remove) { 438 /* Be CAREFUL here! This should do what ea_delentry does. ea_close relies on it !*/ 439 free((*ea.ea_entries)[count].ea_name); 440 (*ea.ea_entries)[count].ea_name = NULL; 441 } 442 443 count++; 444 } /* while */ 445 446 ea_close(&ea); 447 return ret; 448} 449 450/* 451 Check for .AppleDouble folder and .Parent, create if missing 452*/ 453static int check_addir(int volroot) 454{ 455 int addir_ok, adpar_ok; 456 struct stat st; 457 struct adouble ad; 458 char *mname = NULL; 459 460 if (dbd_flags & DBD_FLAGS_CLEANUP) 461 return 0; 462 463 /* Check for ad-dir */ 464 if ( (addir_ok = access(ADv2_DIRNAME, F_OK)) != 0) { 465 if (errno != ENOENT) { 466 dbd_log(LOGSTD, "Access error in directory %s: %s", cwdbuf, strerror(errno)); 467 return -1; 468 } 469 dbd_log(LOGSTD, "Missing %s for '%s'", ADv2_DIRNAME, cwdbuf); 470 } 471 472 /* Check for ".Parent" */ 473 if ( (adpar_ok = access(myvolinfo->ad_path(".", ADFLAGS_DIR), F_OK)) != 0) { 474 if (errno != ENOENT) { 475 dbd_log(LOGSTD, "Access error on '%s/%s': %s", 476 cwdbuf, myvolinfo->ad_path(".", ADFLAGS_DIR), strerror(errno)); 477 return -1; 478 } 479 dbd_log(LOGSTD, "Missing .AppleDouble/.Parent for '%s'", cwdbuf); 480 } 481 482 /* Is one missing ? */ 483 if ((addir_ok != 0) || (adpar_ok != 0)) { 484 /* Yes, but are we only scanning ? */ 485 if (dbd_flags & DBD_FLAGS_SCAN) { 486 /* Yes: missing .Parent is not a problem, but missing ad-dir 487 causes later checking of ad-files to fail. So we have to return appropiately */ 488 if (addir_ok != 0) 489 return -1; 490 else /* (adpar_ok != 0) */ 491 return 0; 492 } 493 494 /* Create ad dir and set name */ 495 ad_init(&ad, myvolinfo->v_adouble, myvolinfo->v_ad_options); 496 497 if (ad_open_metadata( ".", ADFLAGS_DIR, O_CREAT, &ad) != 0) { 498 dbd_log( LOGSTD, "Error creating AppleDouble dir in %s: %s", cwdbuf, strerror(errno)); 499 return -1; 500 } 501 502 /* Get basename of cwd from cwdbuf */ 503 mname = utompath(strrchr(cwdbuf, '/') + 1); 504 505 /* Update name in ad file */ 506 ad_setname(&ad, mname); 507 ad_flush(&ad); 508 ad_close_metadata(&ad); 509 510 /* Inherit owner/group from "." to ".AppleDouble" and ".Parent" */ 511 if ((lstat(".", &st)) != 0) { 512 dbd_log( LOGSTD, "Couldnt stat %s: %s", cwdbuf, strerror(errno)); 513 return -1; 514 } 515 chown(ADv2_DIRNAME, st.st_uid, st.st_gid); 516 chown(myvolinfo->ad_path(".", ADFLAGS_DIR), st.st_uid, st.st_gid); 517 } 518 519 return 0; 520} 521 522/* 523 Check if file cotains "::EA" and if it does check if its correspondig data fork exists. 524 Returns: 525 0 = name is not an EA file 526 1 = name is an EA file and no problem was found 527 -1 = name is an EA file and data fork is gone 528 */ 529static int check_eafile_in_adouble(const char *name) 530{ 531 int ret = 0; 532 char *namep, *namedup = NULL; 533 534 /* Check if this is an AFPVOL_EA_AD vol */ 535 if (myvolinfo->v_vfs_ea == AFPVOL_EA_AD) { 536 /* Does the filename contain "::EA" ? */ 537 namedup = strdup(name); 538 if ((namep = strstr(namedup, "::EA")) == NULL) { 539 ret = 0; 540 goto ea_check_done; 541 } else { 542 /* File contains "::EA" so it's an EA file. Check for data file */ 543 544 /* Get string before "::EA" from EA filename */ 545 namep[0] = 0; 546 strlcpy(pname + 3, namedup, sizeof(pname)); /* Prepends "../" */ 547 548 if ((access( pname, F_OK)) == 0) { 549 ret = 1; 550 goto ea_check_done; 551 } else { 552 ret = -1; 553 if (errno != ENOENT) { 554 dbd_log(LOGSTD, "Access error for file '%s/%s': %s", 555 cwdbuf, name, strerror(errno)); 556 goto ea_check_done; 557 } 558 559 /* Orphaned EA file*/ 560 dbd_log(LOGSTD, "Orphaned Extended Attribute file '%s/%s/%s'", 561 cwdbuf, ADv2_DIRNAME, name); 562 563 if (dbd_flags & DBD_FLAGS_SCAN) 564 /* Scan only requested, dont change anything */ 565 goto ea_check_done; 566 567 if ((unlink(name)) != 0) { 568 dbd_log(LOGSTD, "Error unlinking orphaned Extended Attribute file '%s/%s/%s'", 569 cwdbuf, ADv2_DIRNAME, name); 570 } 571 } /* if (access) */ 572 } /* if strstr */ 573 } /* if AFPVOL_EA_AD */ 574 575ea_check_done: 576 if (namedup) 577 free(namedup); 578 579 return ret; 580} 581 582/* 583 Check files and dirs inside .AppleDouble folder: 584 - remove orphaned files 585 - bail on dirs 586*/ 587static int read_addir(void) 588{ 589 DIR *dp; 590 struct dirent *ep; 591 struct stat st; 592 593 if ((chdir(ADv2_DIRNAME)) != 0) { 594 dbd_log(LOGSTD, "Couldn't chdir to '%s/%s': %s", 595 cwdbuf, ADv2_DIRNAME, strerror(errno)); 596 return -1; 597 } 598 599 if ((dp = opendir(".")) == NULL) { 600 dbd_log(LOGSTD, "Couldn't open the directory '%s/%s': %s", 601 cwdbuf, ADv2_DIRNAME, strerror(errno)); 602 return -1; 603 } 604 605 while ((ep = readdir(dp))) { 606 /* Check if its "." or ".." */ 607 if (DIR_DOT_OR_DOTDOT(ep->d_name)) 608 continue; 609 /* Skip ".Parent" */ 610 if (STRCMP(ep->d_name, ==, ".Parent")) 611 continue; 612 613 if ((lstat(ep->d_name, &st)) < 0) { 614 dbd_log( LOGSTD, "Lost file or dir while enumeratin dir '%s/%s/%s', probably removed: %s", 615 cwdbuf, ADv2_DIRNAME, ep->d_name, strerror(errno)); 616 continue; 617 } 618 619 /* Check for dirs */ 620 if (S_ISDIR(st.st_mode)) { 621 dbd_log( LOGSTD, "Unexpected directory '%s' in AppleDouble dir '%s/%s'", 622 ep->d_name, cwdbuf, ADv2_DIRNAME); 623 continue; 624 } 625 626 /* Check if for orphaned and corrupt Extended Attributes file */ 627 if (check_eafile_in_adouble(ep->d_name) != 0) 628 continue; 629 630 /* Check for data file */ 631 strcpy(pname + 3, ep->d_name); 632 if ((access( pname, F_OK)) != 0) { 633 if (errno != ENOENT) { 634 dbd_log(LOGSTD, "Access error for file '%s/%s': %s", 635 cwdbuf, pname, strerror(errno)); 636 continue; 637 } 638 /* Orphaned ad-file*/ 639 dbd_log(LOGSTD, "Orphaned AppleDoube file '%s/%s/%s'", 640 cwdbuf, ADv2_DIRNAME, ep->d_name); 641 642 if (dbd_flags & DBD_FLAGS_SCAN) 643 /* Scan only requested, dont change anything */ 644 continue;; 645 646 if ((unlink(ep->d_name)) != 0) { 647 dbd_log(LOGSTD, "Error unlinking orphaned AppleDoube file '%s/%s/%s'", 648 cwdbuf, ADv2_DIRNAME, ep->d_name); 649 650 } 651 } 652 } 653 654 if ((chdir("..")) != 0) { 655 dbd_log(LOGSTD, "Couldn't chdir back to '%s' from AppleDouble dir: %s", 656 cwdbuf, strerror(errno)); 657 /* This really is EOT! */ 658 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */ 659 } 660 661 closedir(dp); 662 663 return 0; 664} 665 666/* 667 Check CNID for a file/dir, both from db and from ad-file. 668 For detailed specs see intro. 669 670 @return Correct CNID of object or CNID_INVALID (ie 0) on error 671*/ 672static cnid_t check_cnid(const char *name, cnid_t did, struct stat *st, int adfile_ok, int adflags) 673{ 674 int ret; 675 cnid_t db_cnid, ad_cnid; 676 struct adouble ad; 677 678 /* Force checkout every X items */ 679 static int cnidcount = 0; 680 cnidcount++; 681 if (cnidcount > 10000) { 682 cnidcount = 0; 683 if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0) { 684 dbd_log(LOGSTD, "Error checkpointing!"); 685 return CNID_INVALID; 686 } 687 } 688 689 /* Get CNID from ad-file if volume is using AFPVOL_CACHE */ 690 ad_cnid = 0; 691 if ( (myvolinfo->v_flags & AFPVOL_CACHE) && ADFILE_OK) { 692 ad_init(&ad, myvolinfo->v_adouble, myvolinfo->v_ad_options); 693 if (ad_open_metadata( name, adflags, O_RDWR, &ad) != 0) { 694 695 if (dbd_flags & DBD_FLAGS_CLEANUP) 696 return CNID_INVALID; 697 698 dbd_log( LOGSTD, "Error opening AppleDouble file for '%s/%s': %s", cwdbuf, name, strerror(errno)); 699 return CNID_INVALID; 700 } 701 702 if (dbd_flags & DBD_FLAGS_FORCE) { 703 ad_cnid = ad_forcegetid(&ad); 704 /* This ensures the changed stamp is written */ 705 ad_setid( &ad, st->st_dev, st->st_ino, ad_cnid, did, stamp); 706 ad_flush(&ad); 707 } 708 else 709 ad_cnid = ad_getid(&ad, st->st_dev, st->st_ino, did, stamp); 710 711 if (ad_cnid == 0) 712 dbd_log( LOGSTD, "Bad CNID in adouble file of '%s/%s'", cwdbuf, name); 713 else 714 dbd_log( LOGDEBUG, "CNID from .AppleDouble file for '%s/%s': %u", cwdbuf, name, ntohl(ad_cnid)); 715 716 ad_close_metadata(&ad); 717 } 718 719 /* Get CNID from database */ 720 721 /* Prepare request data */ 722 memset(&rqst, 0, sizeof(struct cnid_dbd_rqst)); 723 memset(&rply, 0, sizeof(struct cnid_dbd_rply)); 724 rqst.did = did; 725 rqst.cnid = ad_cnid; 726 if ( ! (myvolinfo->v_flags & AFPVOL_NODEV)) 727 rqst.dev = st->st_dev; 728 rqst.ino = st->st_ino; 729 rqst.type = S_ISDIR(st->st_mode)?1:0; 730 rqst.name = (char *)name; 731 rqst.namelen = strlen(name); 732 733 /* Query the database */ 734 ret = dbd_lookup(dbd, &rqst, &rply, (dbd_flags & DBD_FLAGS_SCAN) ? 1 : 0); 735 if (dbif_txn_close(dbd, ret) != 0) 736 return CNID_INVALID; 737 if (rply.result == CNID_DBD_RES_OK) { 738 db_cnid = rply.cnid; 739 } else if (rply.result == CNID_DBD_RES_NOTFOUND) { 740 if ( ! (dbd_flags & DBD_FLAGS_FORCE)) 741 dbd_log( LOGSTD, "No CNID for '%s/%s' in database", cwdbuf, name); 742 db_cnid = 0; 743 } else { 744 dbd_log( LOGSTD, "Fatal error resolving '%s/%s'", cwdbuf, name); 745 db_cnid = 0; 746 } 747 748 /* Compare results from both CNID searches */ 749 if (ad_cnid && db_cnid && (ad_cnid == db_cnid)) { 750 /* Everything is fine */ 751 return db_cnid; 752 } else if (ad_cnid && db_cnid && (ad_cnid != db_cnid)) { 753 /* Mismatch ? Delete both from db and re-add data from file */ 754 dbd_log( LOGSTD, "CNID mismatch for '%s/%s', db: %u, ad-file: %u", cwdbuf, name, ntohl(db_cnid), ntohl(ad_cnid)); 755 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) { 756 rqst.cnid = db_cnid; 757 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID); 758 if (dbif_txn_close(dbd, ret) != 0) 759 return CNID_INVALID; 760 761 rqst.cnid = ad_cnid; 762 ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID); 763 if (dbif_txn_close(dbd, ret) != 0) 764 return CNID_INVALID; 765 766 ret = dbd_rebuild_add(dbd, &rqst, &rply); 767 if (dbif_txn_close(dbd, ret) != 0) 768 return CNID_INVALID; 769 } 770 return ad_cnid; 771 } else if (ad_cnid && (db_cnid == 0)) { 772 /* in ad-file but not in db */ 773 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) { 774 /* Ensure the cnid from the ad-file is not already occupied by another file */ 775 dbd_log(LOGDEBUG, "Checking whether CNID %u from ad-file is occupied", 776 ntohl(ad_cnid)); 777 778 rqst.cnid = ad_cnid; 779 ret = dbd_resolve(dbd, &rqst, &rply); 780 if (ret == CNID_DBD_RES_OK) { 781 /* Occupied! Choose another, update ad-file */ 782 ret = dbd_add(dbd, &rqst, &rply, 1); 783 if (dbif_txn_close(dbd, ret) != 0) 784 return CNID_INVALID; 785 db_cnid = rply.cnid; 786 dbd_log(LOGSTD, "New CNID for '%s/%s': %u", cwdbuf, name, ntohl(db_cnid)); 787 788 if ((myvolinfo->v_flags & AFPVOL_CACHE) 789 && ADFILE_OK 790 && ( ! (dbd_flags & DBD_FLAGS_SCAN))) { 791 dbd_log(LOGSTD, "Writing CNID data for '%s/%s' to AppleDouble file", 792 cwdbuf, name, ntohl(db_cnid)); 793 ad_init(&ad, myvolinfo->v_adouble, myvolinfo->v_ad_options); 794 if (ad_open_metadata( name, adflags, O_RDWR, &ad) != 0) { 795 dbd_log(LOGSTD, "Error opening AppleDouble file for '%s/%s': %s", 796 cwdbuf, name, strerror(errno)); 797 return CNID_INVALID; 798 } 799 ad_setid( &ad, st->st_dev, st->st_ino, db_cnid, did, stamp); 800 ad_flush(&ad); 801 ad_close_metadata(&ad); 802 } 803 return db_cnid; 804 } 805 806 dbd_log(LOGDEBUG, "CNID rebuild add '%s/%s' with CNID from ad-file %u", 807 cwdbuf, name, ntohl(ad_cnid)); 808 rqst.cnid = ad_cnid; 809 ret = dbd_rebuild_add(dbd, &rqst, &rply); 810 if (dbif_txn_close(dbd, ret) != 0) 811 return CNID_INVALID; 812 } 813 return ad_cnid; 814 } else if ((db_cnid == 0) && (ad_cnid == 0)) { 815 /* No CNID at all, we clearly have to allocate a fresh one... */ 816 /* Note: the next test will use this new CNID too! */ 817 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) { 818 /* add to db */ 819 ret = dbd_add(dbd, &rqst, &rply, 1); 820 if (dbif_txn_close(dbd, ret) != 0) 821 return CNID_INVALID; 822 db_cnid = rply.cnid; 823 dbd_log(LOGSTD, "New CNID for '%s/%s': %u", cwdbuf, name, ntohl(db_cnid)); 824 } 825 } 826 827 if ((ad_cnid == 0) && db_cnid) { 828 /* in db but zeroID in ad-file, write it to ad-file if AFPVOL_CACHE */ 829 if ((myvolinfo->v_flags & AFPVOL_CACHE) && ADFILE_OK) { 830 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) { 831 dbd_log(LOGSTD, "Writing CNID data for '%s/%s' to AppleDouble file", 832 cwdbuf, name, ntohl(db_cnid)); 833 ad_init(&ad, myvolinfo->v_adouble, myvolinfo->v_ad_options); 834 if (ad_open_metadata( name, adflags, O_RDWR, &ad) != 0) { 835 dbd_log(LOGSTD, "Error opening AppleDouble file for '%s/%s': %s", 836 cwdbuf, name, strerror(errno)); 837 return CNID_INVALID; 838 } 839 ad_setid( &ad, st->st_dev, st->st_ino, db_cnid, did, stamp); 840 ad_flush(&ad); 841 ad_close_metadata(&ad); 842 } 843 } 844 return db_cnid; 845 } 846 847 return CNID_INVALID; 848} 849 850/* 851 This is called recursively for all dirs. 852 volroot=1 means we're in the volume root dir, 0 means we aren't. 853 We use this when checking for netatalk private folders like .AppleDB. 854 did is our parents CNID. 855*/ 856static int dbd_readdir(int volroot, cnid_t did) 857{ 858 int cwd, ret = 0, adflags, adfile_ok, addir_ok, encoding_ok; 859 cnid_t cnid = 0; 860 const char *name; 861 DIR *dp; 862 struct dirent *ep; 863 static struct stat st; /* Save some stack space */ 864 865 /* Check again for .AppleDouble folder, check_adfile also checks/creates it */ 866 if ((addir_ok = check_addir(volroot)) != 0) 867 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) 868 /* Fatal on rebuild run, continue if only scanning ! */ 869 return -1; 870 871 /* Check AppleDouble files in AppleDouble folder, but only if it exists or could be created */ 872 if (ADDIR_OK) 873 if ((read_addir()) != 0) 874 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) 875 /* Fatal on rebuild run, continue if only scanning ! */ 876 return -1; 877 878 if ((dp = opendir (".")) == NULL) { 879 dbd_log(LOGSTD, "Couldn't open the directory: %s",strerror(errno)); 880 return -1; 881 } 882 883 while ((ep = readdir (dp))) { 884 /* Check if we got a termination signal */ 885 if (alarmed) 886 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */ 887 888 /* Check if its "." or ".." */ 889 if (DIR_DOT_OR_DOTDOT(ep->d_name)) 890 continue; 891 892 /* Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop" */ 893 if ((name = check_netatalk_dirs(ep->d_name)) != NULL) { 894 if (! volroot) 895 dbd_log(LOGSTD, "Nested %s in %s", name, cwdbuf); 896 continue; 897 } 898 899 /* Check for special folders in volume root e.g. ".zfs" */ 900 if (volroot) { 901 if ((name = check_special_dirs(ep->d_name)) != NULL) { 902 dbd_log(LOGSTD, "Ignoring special dir \"%s\"", name); 903 continue; 904 } 905 } 906 907 /* Skip .AppleDouble dir in this loop */ 908 if (STRCMP(ep->d_name, == , ADv2_DIRNAME)) 909 continue; 910 911 if ((ret = lstat(ep->d_name, &st)) < 0) { 912 dbd_log( LOGSTD, "Lost file while reading dir '%s/%s', probably removed: %s", 913 cwdbuf, ep->d_name, strerror(errno)); 914 continue; 915 } 916 917 switch (st.st_mode & S_IFMT) { 918 case S_IFREG: 919 adflags = 0; 920 break; 921 case S_IFDIR: 922 adflags = ADFLAGS_DIR; 923 break; 924 case S_IFLNK: 925 dbd_log(LOGDEBUG, "Ignoring symlink %s/%s", cwdbuf, ep->d_name); 926#if 0 927 ret = check_symlink(ep->d_name, &adflags); 928 if (ret == 1) 929 break; 930 if (ret == -1) 931 dbd_log(LOGSTD, "Error checking symlink %s/%s", cwdbuf, ep->d_name); 932#endif 933 continue; 934 default: 935 dbd_log(LOGSTD, "Bad filetype: %s/%s", cwdbuf, ep->d_name); 936 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) { 937 if ((unlink(ep->d_name)) != 0) { 938 dbd_log(LOGSTD, "Error removing: %s/%s: %s", cwdbuf, ep->d_name, strerror(errno)); 939 } 940 } 941 continue; 942 } 943 944 /************************************************************************** 945 Statistics 946 **************************************************************************/ 947 static unsigned long long statcount = 0; 948 static time_t t = 0; 949 950 if (t == 0) 951 t = time(NULL); 952 953 statcount++; 954 if ((statcount % 10000) == 0) { 955 if (dbd_flags & DBD_FLAGS_STATS) 956 dbd_log(LOGSTD, "Scanned: %10llu, time: %10llu s", 957 statcount, (unsigned long long)(time(NULL) - t)); 958 } 959 960 /************************************************************************** 961 Tests 962 **************************************************************************/ 963 964 /* Check encoding */ 965 if ( -1 == (encoding_ok = check_name_encoding(ep->d_name)) ) { 966 /* If its a file: skipp all other tests now ! */ 967 /* For dirs we could try to get a CNID for it and recurse, but currently I prefer not to */ 968 continue; 969 } 970 971 /* Check for appledouble file, create if missing, but only if we have addir */ 972 adfile_ok = -1; 973 if (ADDIR_OK) 974 adfile_ok = check_adfile(ep->d_name, &st); 975 976 if ( ! nocniddb) { 977 /* Check CNIDs */ 978 cnid = check_cnid(ep->d_name, did, &st, adfile_ok, adflags); 979 980 /* Now add this object to our rebuild dbd */ 981 if (cnid && dbd_rebuild) { 982 static uint count = 0; 983 rqst.cnid = rply.cnid; 984 ret = dbd_rebuild_add(dbd_rebuild, &rqst, &rply); 985 if (dbif_txn_close(dbd_rebuild, ret) != 0) 986 return -1; 987 if (rply.result != CNID_DBD_RES_OK) { 988 dbd_log( LOGSTD, "Fatal error adding CNID: %u for '%s/%s' to in-memory rebuild-db", 989 cnid, cwdbuf, ep->d_name); 990 return -1; 991 } 992 count++; 993 if (count == 10000) { 994 if (dbif_txn_checkpoint(dbd_rebuild, 0, 0, 0) < 0) { 995 dbd_log(LOGSTD, "Error checkpointing!"); 996 return -1; 997 } 998 count = 0; 999 } 1000 } 1001 } 1002 1003 /* Check EA files */ 1004 if (myvolinfo->v_vfs_ea == AFPVOL_EA_AD) 1005 check_eafiles(ep->d_name); 1006 1007 /************************************************************************** 1008 Recursion 1009 **************************************************************************/ 1010 if (S_ISDIR(st.st_mode) && (cnid || nocniddb)) { /* If we have no cnid for it we cant recur */ 1011 strcat(cwdbuf, "/"); 1012 strcat(cwdbuf, ep->d_name); 1013 dbd_log( LOGDEBUG, "Entering directory: %s", cwdbuf); 1014 if (-1 == (cwd = open(".", O_RDONLY))) { 1015 dbd_log( LOGSTD, "Cant open directory '%s': %s", cwdbuf, strerror(errno)); 1016 continue; 1017 } 1018 if (0 != chdir(ep->d_name)) { 1019 dbd_log( LOGSTD, "Cant chdir to directory '%s': %s", cwdbuf, strerror(errno)); 1020 close(cwd); 1021 continue; 1022 } 1023 1024 ret = dbd_readdir(0, cnid); 1025 1026 fchdir(cwd); 1027 close(cwd); 1028 *(strrchr(cwdbuf, '/')) = 0; 1029 if (ret < 0) 1030 return -1; 1031 } 1032 } 1033 1034 /* 1035 Use results of previous checks 1036 */ 1037 1038 closedir(dp); 1039 return ret; 1040} 1041 1042static int scanvol(struct volinfo *vi, dbd_flags_t flags) 1043{ 1044 /* Dont scanvol on no-appledouble vols */ 1045 if (vi->v_flags & AFPVOL_NOADOUBLE) { 1046 dbd_log( LOGSTD, "Volume without AppleDouble support: skipping volume scanning."); 1047 return 0; 1048 } 1049 1050 /* Make this stuff accessible from all funcs easily */ 1051 myvolinfo = vi; 1052 dbd_flags = flags; 1053 1054 /* Init a fake struct vol with just enough so we can call ea_open and friends */ 1055 volume.v_adouble = AD_VERSION2; 1056 volume.v_vfs_ea = myvolinfo->v_vfs_ea; 1057 initvol_vfs(&volume); 1058 1059 /* Run with umask 0 */ 1060 umask(0); 1061 1062 /* Remove trailing slash from volume, chdir to vol */ 1063 if (myvolinfo->v_path[strlen(myvolinfo->v_path) - 1] == '/') 1064 myvolinfo->v_path[strlen(myvolinfo->v_path) - 1] = 0; 1065 strcpy(cwdbuf, myvolinfo->v_path); 1066 chdir(myvolinfo->v_path); 1067 1068 /* Start recursion */ 1069 if (dbd_readdir(1, htonl(2)) < 0) /* 2 = volumeroot CNID */ 1070 return -1; 1071 1072 return 0; 1073} 1074 1075/* 1076 Remove all CNIDs from dbd that are not in dbd_rebuild 1077*/ 1078static void delete_orphaned_cnids(DBD *dbd, DBD *dbd_rebuild, dbd_flags_t flags) 1079{ 1080 int ret = 0, deleted = 0; 1081 cnid_t dbd_cnid = 0, rebuild_cnid = 0; 1082 struct cnid_dbd_rqst rqst; 1083 struct cnid_dbd_rply rply; 1084 1085 /* jump over rootinfo key */ 1086 if ( dbif_idwalk(dbd, &dbd_cnid, 0) != 1) 1087 return; 1088 if ( dbif_idwalk(dbd_rebuild, &rebuild_cnid, 0) != 1) 1089 return; 1090 1091 /* Get first id from dbd_rebuild */ 1092 if ((dbif_idwalk(dbd_rebuild, &rebuild_cnid, 0)) == -1) 1093 return; 1094 1095 /* Start main loop through dbd: get CNID from dbd */ 1096 while ((dbif_idwalk(dbd, &dbd_cnid, 0)) == 1) { 1097 /* Check if we got a termination signal */ 1098 if (alarmed) 1099 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */ 1100 1101 if (deleted > 1000) { 1102 deleted = 0; 1103 if (dbif_txn_checkpoint(dbd, 0, 0, 0) < 0) { 1104 dbd_log(LOGSTD, "Error checkpointing!"); 1105 goto cleanup; 1106 } 1107 } 1108 1109 /* This should be the normal case: CNID is in both dbs */ 1110 if (dbd_cnid == rebuild_cnid) { 1111 /* Get next CNID from rebuild db */ 1112 if ((ret = dbif_idwalk(dbd_rebuild, &rebuild_cnid, 0)) == -1) { 1113 /* Some error */ 1114 goto cleanup; 1115 } else if (ret == 0) { 1116 /* end of rebuild_cnid, delete all remaining CNIDs from dbd */ 1117 while ((dbif_idwalk(dbd, &dbd_cnid, 0)) == 1) { 1118 dbd_log(LOGSTD, "Orphaned CNID in database: %u", dbd_cnid); 1119 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) { 1120 rqst.cnid = htonl(dbd_cnid); 1121 if ((ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID)) == -1) { 1122 dbd_log(LOGSTD, "Error deleting CNID %u", dbd_cnid); 1123 (void)dbif_txn_abort(dbd); 1124 goto cleanup; 1125 } 1126 1127 if (dbif_txn_close(dbd, ret) != 0) 1128 return; 1129 deleted++; 1130 } 1131 /* Check if we got a termination signal */ 1132 if (alarmed) 1133 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */ 1134 } 1135 return; 1136 } else 1137 /* Normal case (ret=1): continue while loop */ 1138 continue; 1139 } 1140 1141 if (dbd_cnid < rebuild_cnid) { 1142 /* CNID is orphaned -> delete */ 1143 dbd_log(LOGSTD, "One orphaned CNID in database: %u.", dbd_cnid); 1144 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) { 1145 rqst.cnid = htonl(dbd_cnid); 1146 if ((ret = dbd_delete(dbd, &rqst, &rply, DBIF_CNID)) == -1) { 1147 dbd_log(LOGSTD, "Error deleting CNID %u", dbd_cnid); 1148 (void)dbif_txn_abort(dbd); 1149 goto cleanup; 1150 } 1151 if (dbif_txn_close(dbd, ret) != 0) 1152 return; 1153 deleted++; 1154 } 1155 continue; 1156 } 1157 1158 if (dbd_cnid > rebuild_cnid) { 1159 dbif_idwalk(dbd, NULL, 1); /* Close cursor */ 1160 dbif_idwalk(dbd_rebuild, NULL, 1); /* Close cursor */ 1161 (void)dbif_txn_close(dbd, 2); 1162 (void)dbif_txn_close(dbd_rebuild, 2); 1163 dbd_log(LOGSTD, "Ghost CNID: %u. This is fatal! Dumping rebuild db:\n", rebuild_cnid); 1164 dbif_dump(dbd_rebuild, 0); 1165 dbd_log(LOGSTD, "Send this dump and a `dbd -d ...` dump to the Netatalk Dev team!"); 1166 goto cleanup; 1167 } 1168 } /* while ((dbif_idwalk(dbd, &dbd_cnid, 0)) == 1) */ 1169 1170cleanup: 1171 dbif_idwalk(dbd, NULL, 1); /* Close cursor */ 1172 dbif_idwalk(dbd_rebuild, NULL, 1); /* Close cursor */ 1173 return; 1174} 1175 1176static const char *get_tmpdb_path(void) 1177{ 1178 pid_t pid = getpid(); 1179 static char path[MAXPATHLEN]; 1180 snprintf(path, MAXPATHLEN, "/tmp/tmpdb-dbd.%u", pid); 1181 if (mkdir(path, 0755) != 0) 1182 return NULL; 1183 return path; 1184} 1185 1186/* 1187 Main func called from cmd_dbd.c 1188*/ 1189int cmd_dbd_scanvol(DBD *dbd_ref, struct volinfo *vi, dbd_flags_t flags) 1190{ 1191 int ret = 0; 1192 struct db_param db_param = { 0 }; 1193 const char *tmpdb_path = NULL; 1194 1195 /* Set cachesize for in-memory rebuild db */ 1196 db_param.cachesize = 64 * 1024; /* 64 MB */ 1197 db_param.maxlocks = DEFAULT_MAXLOCKS; 1198 db_param.maxlockobjs = DEFAULT_MAXLOCKOBJS; 1199 db_param.logfile_autoremove = 1; 1200 1201 /* Make it accessible for all funcs */ 1202 dbd = dbd_ref; 1203 1204 /* We only support unicode volumes ! */ 1205 if ( vi->v_volcharset != CH_UTF8) { 1206 dbd_log( LOGSTD, "Not a Unicode volume: %s, %u != %u", vi->v_volcodepage, vi->v_volcharset, CH_UTF8); 1207 return -1; 1208 } 1209 1210 /* Get volume stamp */ 1211 dbd_getstamp(dbd, &rqst, &rply); 1212 if (rply.result != CNID_DBD_RES_OK) { 1213 ret = -1; 1214 goto exit; 1215 } 1216 memcpy(stamp, rply.name, CNID_DEV_LEN); 1217 1218 /* temporary rebuild db, used with -re rebuild to delete unused CNIDs, not used with -f */ 1219 if (! nocniddb && (flags & DBD_FLAGS_EXCL) && !(flags & DBD_FLAGS_FORCE)) { 1220 tmpdb_path = get_tmpdb_path(); 1221 if (NULL == (dbd_rebuild = dbif_init(tmpdb_path, "cnid2.db"))) { 1222 ret = -1; 1223 goto exit; 1224 } 1225 1226 if (dbif_env_open(dbd_rebuild, 1227 &db_param, 1228 DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN) < 0) { 1229 dbd_log(LOGSTD, "error opening tmp database!"); 1230 goto exit; 1231 } 1232 1233 if (0 != (dbif_open(dbd_rebuild, NULL, 0))) { 1234 ret = -1; 1235 goto exit; 1236 } 1237 1238 if (0 != (dbif_copy_rootinfokey(dbd, dbd_rebuild))) { 1239 ret = -1; 1240 goto exit; 1241 } 1242 } 1243 1244 if (setjmp(jmp) != 0) { 1245 ret = 0; /* Got signal, jump from dbd_readdir */ 1246 goto exit; 1247 } 1248 1249 /* scanvol */ 1250 if ( (scanvol(vi, flags)) != 0) { 1251 ret = -1; 1252 goto exit; 1253 } 1254 1255exit: 1256 if (! nocniddb) { 1257 if (dbif_txn_close(dbd, ret == 0 ? 1 : 0) != 0) 1258 ret = -1; 1259 if (dbd_rebuild) 1260 if (dbif_txn_close(dbd_rebuild, ret == 0 ? 1 : 0) != 0) 1261 ret = -1; 1262 if ((ret == 0) && dbd_rebuild && (flags & DBD_FLAGS_EXCL) && !(flags & DBD_FLAGS_FORCE)) 1263 /* We can only do this in exclusive mode, otherwise we might delete CNIDs added from 1264 other clients in between our pass 1 and 2 */ 1265 delete_orphaned_cnids(dbd, dbd_rebuild, flags); 1266 } 1267 1268 if (dbd_rebuild) { 1269 dbd_log(LOGDEBUG, "Closing tmp db"); 1270 dbif_close(dbd_rebuild); 1271 1272 if (tmpdb_path) { 1273 char cmd[8 + MAXPATHLEN]; 1274 snprintf(cmd, 8 + MAXPATHLEN, "rm -f %s/*", tmpdb_path); 1275 dbd_log( LOGDEBUG, "Removing temp database '%s'", tmpdb_path); 1276 system(cmd); 1277 snprintf(cmd, 8 + MAXPATHLEN, "rmdir %s", tmpdb_path); 1278 system(cmd); 1279 } 1280 } 1281 return ret; 1282} 1283