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/netatalk_conf.h> 32#include <atalk/volume.h> 33#include <atalk/ea.h> 34#include <atalk/util.h> 35#include <atalk/acl.h> 36#include <atalk/compat.h> 37#include <atalk/cnid.h> 38#include <atalk/errchk.h> 39 40#include "cmd_dbd.h" 41#include "dbif.h" 42#include "db_param.h" 43#include "dbd.h" 44 45/* Some defines to ease code parsing */ 46#define ADDIR_OK (addir_ok == 0) 47#define ADFILE_OK (adfile_ok == 0) 48 49 50static char cwdbuf[MAXPATHLEN+1]; 51static struct vol *vol; 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 char pname[MAXPATHLEN] = "../"; 67 68/* 69 Taken form afpd/desktop.c 70*/ 71static char *utompath(char *upath) 72{ 73 static char mpath[ MAXPATHLEN + 2]; /* for convert_charset dest_len parameter +2 */ 74 char *m, *u; 75 uint16_t flags = CONV_IGNORE | CONV_UNESCAPEHEX; 76 size_t outlen; 77 78 if (!upath) 79 return NULL; 80 81 m = mpath; 82 u = upath; 83 outlen = strlen(upath); 84 85 if ((vol->v_casefold & AFPVOL_UTOMUPPER)) 86 flags |= CONV_TOUPPER; 87 else if ((vol->v_casefold & AFPVOL_UTOMLOWER)) 88 flags |= CONV_TOLOWER; 89 90 if ((vol->v_flags & AFPVOL_EILSEQ)) { 91 flags |= CONV__EILSEQ; 92 } 93 94 /* convert charsets */ 95 if ((size_t)-1 == ( outlen = convert_charset(vol->v_volcharset, 96 CH_UTF8_MAC, 97 vol->v_maccharset, 98 u, outlen, mpath, MAXPATHLEN, &flags)) ) { 99 dbd_log( LOGSTD, "Conversion from %s to %s for %s failed.", 100 vol->v_volcodepage, vol->v_maccodepage, u); 101 return NULL; 102 } 103 104 return(m); 105} 106 107/* 108 Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop" 109 Returns pointer to name or NULL. 110*/ 111static const char *check_netatalk_dirs(const char *name) 112{ 113 int c; 114 115 for (c=0; netatalk_dirs[c]; c++) { 116 if ((strcmp(name, netatalk_dirs[c])) == 0) 117 return netatalk_dirs[c]; 118 } 119 return NULL; 120} 121 122/* 123 Check for special names 124 Returns pointer to name or NULL. 125*/ 126static const char *check_special_dirs(const char *name) 127{ 128 int c; 129 130 for (c=0; special_dirs[c]; c++) { 131 if ((strcmp(name, special_dirs[c])) == 0) 132 return special_dirs[c]; 133 } 134 return NULL; 135} 136 137/* 138 * We unCAPed a name, update CNID db 139 */ 140static int update_cnid(cnid_t did, const struct stat *sp, const char *oldname, const char *newname) 141{ 142 cnid_t id; 143 144 /* Query the database */ 145 if ((id = cnid_lookup(vol->v_cdb, sp, did, (char *)oldname, strlen(oldname))) == CNID_INVALID) 146 /* not in db, no need to update */ 147 return 0; 148 149 /* Update the database */ 150 if (cnid_update(vol->v_cdb, id, sp, did, (char *)newname, strlen(newname)) < 0) 151 return -1; 152 153 return 0; 154} 155 156/* 157 Check for .AppleDouble file, create if missing 158*/ 159static int check_adfile(const char *fname, const struct stat *st, const char **newname) 160{ 161 int ret; 162 int adflags = ADFLAGS_HF; 163 struct adouble ad; 164 const char *adname; 165 166 *newname = NULL; 167 168 if (vol->v_adouble == AD_VERSION_EA) { 169 if (!(dbd_flags & DBD_FLAGS_V2TOEA)) 170 return 0; 171 if (ad_convert(fname, st, vol, newname) != 0) { 172 switch (errno) { 173 case ENOENT: 174 break; 175 default: 176 dbd_log(LOGSTD, "Conversion error for \"%s/%s\": %s", cwdbuf, fname, strerror(errno)); 177 break; 178 } 179 } 180 return 0; 181 } 182 183 if (S_ISDIR(st->st_mode)) 184 adflags |= ADFLAGS_DIR; 185 186 adname = vol->ad_path(fname, adflags); 187 188 if ((ret = access( adname, F_OK)) != 0) { 189 if (errno != ENOENT) { 190 dbd_log(LOGSTD, "Access error for ad-file '%s/%s': %s", 191 cwdbuf, adname, strerror(errno)); 192 return -1; 193 } 194 /* Missing. Log and create it */ 195 dbd_log(LOGSTD, "Missing AppleDouble file '%s/%s'", cwdbuf, adname); 196 197 if (dbd_flags & DBD_FLAGS_SCAN) 198 /* Scan only requested, dont change anything */ 199 return -1; 200 201 /* Create ad file */ 202 ad_init(&ad, vol); 203 204 if ((ret = ad_open(&ad, fname, adflags | ADFLAGS_CREATE | ADFLAGS_RDWR, 0666)) != 0) { 205 dbd_log( LOGSTD, "Error creating AppleDouble file '%s/%s': %s", 206 cwdbuf, adname, strerror(errno)); 207 208 return -1; 209 } 210 211 /* Set name in ad-file */ 212 ad_setname(&ad, utompath((char *)fname)); 213 ad_flush(&ad); 214 ad_close(&ad, ADFLAGS_HF); 215 216 chown(adname, st->st_uid, st->st_gid); 217 /* FIXME: should we inherit mode too here ? */ 218#if 0 219 chmod(adname, st->st_mode); 220#endif 221 } else { 222 ad_init(&ad, vol); 223 if (ad_open(&ad, fname, adflags | ADFLAGS_RDONLY) != 0) { 224 dbd_log( LOGSTD, "Error opening AppleDouble file for '%s/%s'", cwdbuf, fname); 225 return -1; 226 } 227 ad_close(&ad, ADFLAGS_HF); 228 } 229 return 0; 230} 231 232/* 233 Remove all files with file::EA* from adouble dir 234*/ 235static void remove_eafiles(const char *name, struct ea *ea) 236{ 237 DIR *dp = NULL; 238 struct dirent *ep; 239 char eaname[MAXPATHLEN]; 240 241 strlcpy(eaname, name, sizeof(eaname)); 242 if (strlcat(eaname, "::EA", sizeof(eaname)) >= sizeof(eaname)) { 243 dbd_log(LOGSTD, "name too long: '%s/%s/%s'", cwdbuf, ADv2_DIRNAME, name); 244 return; 245 } 246 247 if ((chdir(ADv2_DIRNAME)) != 0) { 248 dbd_log(LOGSTD, "Couldn't chdir to '%s/%s': %s", 249 cwdbuf, ADv2_DIRNAME, strerror(errno)); 250 return; 251 } 252 253 if ((dp = opendir(".")) == NULL) { 254 dbd_log(LOGSTD, "Couldn't open the directory '%s/%s': %s", 255 cwdbuf, ADv2_DIRNAME, strerror(errno)); 256 goto exit; 257 } 258 259 while ((ep = readdir(dp))) { 260 if (strstr(ep->d_name, eaname) != NULL) { 261 dbd_log(LOGSTD, "Removing EA file: '%s/%s/%s'", 262 cwdbuf, ADv2_DIRNAME, ep->d_name); 263 if ((unlink(ep->d_name)) != 0) { 264 dbd_log(LOGSTD, "Error unlinking EA file '%s/%s/%s': %s", 265 cwdbuf, ADv2_DIRNAME, ep->d_name, strerror(errno)); 266 } 267 } /* if */ 268 } /* while */ 269 270exit: 271 if (dp) 272 closedir(dp); 273 if ((chdir("..")) != 0) { 274 dbd_log(LOGSTD, "Couldn't chdir to '%s': %s", cwdbuf, strerror(errno)); 275 /* we can't proceed */ 276 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */ 277 } 278} 279 280/* 281 Check Extended Attributes files 282*/ 283static int check_eafiles(const char *fname) 284{ 285 unsigned int count = 0; 286 int ret = 0, remove; 287 struct ea ea; 288 struct stat st; 289 char *eaname; 290 291 if ((ret = ea_open(vol, fname, EA_RDWR, &ea)) != 0) { 292 if (errno == ENOENT) 293 return 0; 294 dbd_log(LOGSTD, "Error calling ea_open for file: %s/%s, removing EA files", cwdbuf, fname); 295 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) 296 remove_eafiles(fname, &ea); 297 return -1; 298 } 299 300 /* Check all EAs */ 301 while (count < ea.ea_count) { 302 dbd_log(LOGDEBUG, "EA: %s", (*ea.ea_entries)[count].ea_name); 303 remove = 0; 304 305 eaname = ea_path(&ea, (*ea.ea_entries)[count].ea_name, 0); 306 307 if (lstat(eaname, &st) != 0) { 308 if (errno == ENOENT) 309 dbd_log(LOGSTD, "Missing EA: %s/%s", cwdbuf, eaname); 310 else 311 dbd_log(LOGSTD, "Bogus EA: %s/%s", cwdbuf, eaname); 312 remove = 1; 313 } else if (st.st_size != (*ea.ea_entries)[count].ea_size) { 314 dbd_log(LOGSTD, "Bogus EA: %s/%s, removing it...", cwdbuf, eaname); 315 remove = 1; 316 if ((unlink(eaname)) != 0) 317 dbd_log(LOGSTD, "Error removing EA file '%s/%s': %s", 318 cwdbuf, eaname, strerror(errno)); 319 } 320 321 if (remove) { 322 /* Be CAREFUL here! This should do what ea_delentry does. ea_close relies on it !*/ 323 free((*ea.ea_entries)[count].ea_name); 324 (*ea.ea_entries)[count].ea_name = NULL; 325 } 326 327 count++; 328 } /* while */ 329 330 ea_close(&ea); 331 return ret; 332} 333 334/* 335 Check for .AppleDouble folder and .Parent, create if missing 336*/ 337static int check_addir(int volroot) 338{ 339 int addir_ok, adpar_ok; 340 struct stat st; 341 struct adouble ad; 342 char *mname = NULL; 343 344 if (vol->v_adouble == AD_VERSION_EA) 345 return 0; 346 347 /* Check for ad-dir */ 348 if ( (addir_ok = access(ADv2_DIRNAME, F_OK)) != 0) { 349 if (errno != ENOENT) { 350 dbd_log(LOGSTD, "Access error in directory %s: %s", cwdbuf, strerror(errno)); 351 return -1; 352 } 353 dbd_log(LOGSTD, "Missing %s for '%s'", ADv2_DIRNAME, cwdbuf); 354 } 355 356 /* Check for ".Parent" */ 357 if ( (adpar_ok = access(vol->ad_path(".", ADFLAGS_DIR), F_OK)) != 0) { 358 if (errno != ENOENT) { 359 dbd_log(LOGSTD, "Access error on '%s/%s': %s", 360 cwdbuf, vol->ad_path(".", ADFLAGS_DIR), strerror(errno)); 361 return -1; 362 } 363 dbd_log(LOGSTD, "Missing .AppleDouble/.Parent for '%s'", cwdbuf); 364 } 365 366 /* Is one missing ? */ 367 if ((addir_ok != 0) || (adpar_ok != 0)) { 368 /* Yes, but are we only scanning ? */ 369 if (dbd_flags & DBD_FLAGS_SCAN) { 370 /* Yes: missing .Parent is not a problem, but missing ad-dir 371 causes later checking of ad-files to fail. So we have to return appropiately */ 372 if (addir_ok != 0) 373 return -1; 374 else /* (adpar_ok != 0) */ 375 return 0; 376 } 377 378 /* Create ad dir and set name */ 379 ad_init(&ad, vol); 380 381 if (ad_open(&ad, ".", ADFLAGS_HF | ADFLAGS_DIR | ADFLAGS_CREATE | ADFLAGS_RDWR, 0777) != 0) { 382 dbd_log( LOGSTD, "Error creating AppleDouble dir in %s: %s", cwdbuf, strerror(errno)); 383 return -1; 384 } 385 386 /* Get basename of cwd from cwdbuf */ 387 mname = utompath(strrchr(cwdbuf, '/') + 1); 388 389 /* Update name in ad file */ 390 ad_setname(&ad, mname); 391 ad_flush(&ad); 392 ad_close(&ad, ADFLAGS_HF); 393 394 /* Inherit owner/group from "." to ".AppleDouble" and ".Parent" */ 395 if ((lstat(".", &st)) != 0) { 396 dbd_log( LOGSTD, "Couldnt stat %s: %s", cwdbuf, strerror(errno)); 397 return -1; 398 } 399 chown(ADv2_DIRNAME, st.st_uid, st.st_gid); 400 chown(vol->ad_path(".", ADFLAGS_DIR), st.st_uid, st.st_gid); 401 } 402 403 return 0; 404} 405 406/* 407 Check if file cotains "::EA" and if it does check if its correspondig data fork exists. 408 Returns: 409 0 = name is not an EA file 410 1 = name is an EA file and no problem was found 411 -1 = name is an EA file and data fork is gone 412 */ 413static int check_eafile_in_adouble(const char *name) 414{ 415 int ret = 0; 416 char *namep, *namedup = NULL; 417 418 /* Check if this is an AFPVOL_EA_AD vol */ 419 if (vol->v_vfs_ea == AFPVOL_EA_AD) { 420 /* Does the filename contain "::EA" ? */ 421 namedup = strdup(name); 422 if ((namep = strstr(namedup, "::EA")) == NULL) { 423 ret = 0; 424 goto ea_check_done; 425 } else { 426 /* File contains "::EA" so it's an EA file. Check for data file */ 427 428 /* Get string before "::EA" from EA filename */ 429 namep[0] = 0; 430 strlcpy(pname + 3, namedup, sizeof(pname)); /* Prepends "../" */ 431 432 if ((access( pname, F_OK)) == 0) { 433 ret = 1; 434 goto ea_check_done; 435 } else { 436 ret = -1; 437 if (errno != ENOENT) { 438 dbd_log(LOGSTD, "Access error for file '%s/%s': %s", 439 cwdbuf, name, strerror(errno)); 440 goto ea_check_done; 441 } 442 443 /* Orphaned EA file*/ 444 dbd_log(LOGSTD, "Orphaned Extended Attribute file '%s/%s/%s'", 445 cwdbuf, ADv2_DIRNAME, name); 446 447 if (dbd_flags & DBD_FLAGS_SCAN) 448 /* Scan only requested, dont change anything */ 449 goto ea_check_done; 450 451 if ((unlink(name)) != 0) { 452 dbd_log(LOGSTD, "Error unlinking orphaned Extended Attribute file '%s/%s/%s'", 453 cwdbuf, ADv2_DIRNAME, name); 454 } 455 } /* if (access) */ 456 } /* if strstr */ 457 } /* if AFPVOL_EA_AD */ 458 459ea_check_done: 460 if (namedup) 461 free(namedup); 462 463 return ret; 464} 465 466/* 467 Check files and dirs inside .AppleDouble folder: 468 - remove orphaned files 469 - bail on dirs 470*/ 471static int read_addir(void) 472{ 473 DIR *dp; 474 struct dirent *ep; 475 struct stat st; 476 477 if ((chdir(ADv2_DIRNAME)) != 0) { 478 if (vol->v_adouble == AD_VERSION_EA) { 479 return 0; 480 } 481 dbd_log(LOGSTD, "Couldn't chdir to '%s/%s': %s", 482 cwdbuf, ADv2_DIRNAME, strerror(errno)); 483 return -1; 484 } 485 486 if ((dp = opendir(".")) == NULL) { 487 dbd_log(LOGSTD, "Couldn't open the directory '%s/%s': %s", 488 cwdbuf, ADv2_DIRNAME, strerror(errno)); 489 return -1; 490 } 491 492 while ((ep = readdir(dp))) { 493 /* Check if its "." or ".." */ 494 if (DIR_DOT_OR_DOTDOT(ep->d_name)) 495 continue; 496 497 /* Skip ".Parent" */ 498 if (STRCMP(ep->d_name, ==, ".Parent")) 499 continue; 500 501 if ((lstat(ep->d_name, &st)) < 0) { 502 dbd_log( LOGSTD, "Lost file or dir while enumeratin dir '%s/%s/%s', probably removed: %s", 503 cwdbuf, ADv2_DIRNAME, ep->d_name, strerror(errno)); 504 continue; 505 } 506 507 /* Check for dirs */ 508 if (S_ISDIR(st.st_mode)) { 509 dbd_log( LOGSTD, "Unexpected directory '%s' in AppleDouble dir '%s/%s'", 510 ep->d_name, cwdbuf, ADv2_DIRNAME); 511 continue; 512 } 513 514 /* Check if for orphaned and corrupt Extended Attributes file */ 515 if (check_eafile_in_adouble(ep->d_name) != 0) 516 continue; 517 518 /* Check for data file */ 519 strcpy(pname + 3, ep->d_name); 520 if ((access( pname, F_OK)) != 0) { 521 if (errno != ENOENT) { 522 dbd_log(LOGSTD, "Access error for file '%s/%s': %s", 523 cwdbuf, pname, strerror(errno)); 524 continue; 525 } 526 /* Orphaned ad-file*/ 527 dbd_log(LOGSTD, "Orphaned AppleDoube file '%s/%s/%s'", 528 cwdbuf, ADv2_DIRNAME, ep->d_name); 529 530 if (dbd_flags & DBD_FLAGS_SCAN) 531 /* Scan only requested, dont change anything */ 532 continue;; 533 534 if ((unlink(ep->d_name)) != 0) { 535 dbd_log(LOGSTD, "Error unlinking orphaned AppleDoube file '%s/%s/%s'", 536 cwdbuf, ADv2_DIRNAME, ep->d_name); 537 538 } 539 } 540 } 541 542 if ((chdir("..")) != 0) { 543 dbd_log(LOGSTD, "Couldn't chdir back to '%s' from AppleDouble dir: %s", 544 cwdbuf, strerror(errno)); 545 /* This really is EOT! */ 546 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */ 547 } 548 549 closedir(dp); 550 551 return 0; 552} 553 554/* 555 Check CNID for a file/dir, both from db and from ad-file. 556 For detailed specs see intro. 557 558 @return Correct CNID of object or CNID_INVALID (ie 0) on error 559*/ 560static cnid_t check_cnid(const char *name, cnid_t did, struct stat *st, int adfile_ok) 561{ 562 int adflags = ADFLAGS_HF; 563 cnid_t db_cnid, ad_cnid; 564 struct adouble ad; 565 566 adflags = ADFLAGS_HF | (S_ISDIR(st->st_mode) ? ADFLAGS_DIR : 0); 567 568 /* Get CNID from ad-file */ 569 ad_cnid = CNID_INVALID; 570 if (ADFILE_OK) { 571 ad_init(&ad, vol); 572 if (ad_open(&ad, name, adflags | ADFLAGS_RDWR) != 0) { 573 574 if (vol->v_adouble != AD_VERSION_EA) { 575 dbd_log( LOGSTD, "Error opening AppleDouble file for '%s/%s': %s", cwdbuf, name, strerror(errno)); 576 return CNID_INVALID; 577 } 578 dbd_log( LOGDEBUG, "File without meta EA: \"%s/%s\"", cwdbuf, name); 579 adfile_ok = 1; 580 } else { 581 ad_cnid = ad_getid(&ad, st->st_dev, st->st_ino, 0, stamp); 582 if (ad_cnid == CNID_INVALID) 583 dbd_log( LOGSTD, "Bad CNID in adouble file of '%s/%s'", cwdbuf, name); 584 else 585 dbd_log( LOGDEBUG, "CNID from .AppleDouble file for '%s/%s': %u", cwdbuf, name, ntohl(ad_cnid)); 586 ad_close(&ad, ADFLAGS_HF); 587 } 588 } 589 590 /* Get CNID from database */ 591 if ((db_cnid = cnid_add(vol->v_cdb, st, did, (char *)name, strlen(name), ad_cnid)) == CNID_INVALID) 592 return CNID_INVALID; 593 594 /* Compare CNID from db and adouble file */ 595 if (ad_cnid != db_cnid && adfile_ok == 0) { 596 /* Mismatch, overwrite ad file with value from db */ 597 dbd_log(LOGSTD, "CNID mismatch for '%s/%s', CNID db: %u, ad-file: %u", 598 cwdbuf, name, ntohl(db_cnid), ntohl(ad_cnid)); 599 ad_init(&ad, vol); 600 if (ad_open(&ad, name, adflags | ADFLAGS_HF | ADFLAGS_RDWR) != 0) { 601 dbd_log(LOGSTD, "Error opening AppleDouble file for '%s/%s': %s", 602 cwdbuf, name, strerror(errno)); 603 return CNID_INVALID; 604 } 605 ad_setid( &ad, st->st_dev, st->st_ino, db_cnid, did, stamp); 606 ad_flush(&ad); 607 ad_close(&ad, ADFLAGS_HF); 608 } 609 610 return db_cnid; 611} 612 613/* 614 This is called recursively for all dirs. 615 volroot=1 means we're in the volume root dir, 0 means we aren't. 616 We use this when checking for netatalk private folders like .AppleDB. 617 did is our parents CNID. 618*/ 619static int dbd_readdir(int volroot, cnid_t did) 620{ 621 int cwd, ret = 0, adfile_ok, addir_ok; 622 cnid_t cnid = 0; 623 const char *name; 624 DIR *dp; 625 struct dirent *ep; 626 static struct stat st; /* Save some stack space */ 627 628 /* Check again for .AppleDouble folder, check_adfile also checks/creates it */ 629 if ((addir_ok = check_addir(volroot)) != 0) 630 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) 631 /* Fatal on rebuild run, continue if only scanning ! */ 632 return -1; 633 634 /* Check AppleDouble files in AppleDouble folder, but only if it exists or could be created */ 635 if (ADDIR_OK) 636 if ((read_addir()) != 0) 637 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) 638 /* Fatal on rebuild run, continue if only scanning ! */ 639 return -1; 640 641 if ((dp = opendir (".")) == NULL) { 642 dbd_log(LOGSTD, "Couldn't open the directory: %s",strerror(errno)); 643 return -1; 644 } 645 646 while ((ep = readdir (dp))) { 647 /* Check if we got a termination signal */ 648 if (alarmed) 649 longjmp(jmp, 1); /* this jumps back to cmd_dbd_scanvol() */ 650 651 /* Check if its "." or ".." */ 652 if (DIR_DOT_OR_DOTDOT(ep->d_name)) 653 continue; 654 655 /* Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop" */ 656 if ((name = check_netatalk_dirs(ep->d_name)) != NULL) { 657 if (! volroot) 658 dbd_log(LOGSTD, "Nested %s in %s", name, cwdbuf); 659 continue; 660 } 661 662 /* Check for special folders in volume root e.g. ".zfs" */ 663 if (volroot) { 664 if ((name = check_special_dirs(ep->d_name)) != NULL) { 665 dbd_log(LOGSTD, "Ignoring special dir \"%s\"", name); 666 continue; 667 } 668 } 669 670 /* Skip .AppleDouble dir in this loop */ 671 if (STRCMP(ep->d_name, == , ADv2_DIRNAME)) 672 continue; 673 674 if (!vol->vfs->vfs_validupath(vol, ep->d_name)) { 675 dbd_log(LOGDEBUG, "Ignoring \"%s\"", ep->d_name); 676 continue; 677 } 678 679 if ((ret = lstat(ep->d_name, &st)) < 0) { 680 dbd_log( LOGSTD, "Lost file while reading dir '%s/%s', probably removed: %s", 681 cwdbuf, ep->d_name, strerror(errno)); 682 continue; 683 } 684 685 switch (st.st_mode & S_IFMT) { 686 case S_IFREG: 687 case S_IFDIR: 688 case S_IFLNK: 689 break; 690 default: 691 dbd_log(LOGSTD, "Bad filetype: %s/%s", cwdbuf, ep->d_name); 692 if ( ! (dbd_flags & DBD_FLAGS_SCAN)) { 693 if ((unlink(ep->d_name)) != 0) { 694 dbd_log(LOGSTD, "Error removing: %s/%s: %s", cwdbuf, ep->d_name, strerror(errno)); 695 } 696 } 697 continue; 698 } 699 700 /************************************************************************** 701 Statistics 702 **************************************************************************/ 703 static unsigned long long statcount = 0; 704 static time_t t = 0; 705 706 if (t == 0) 707 t = time(NULL); 708 709 statcount++; 710 if ((statcount % 10000) == 0) { 711 if (dbd_flags & DBD_FLAGS_STATS) 712 dbd_log(LOGSTD, "Scanned: %10llu, time: %10llu s", 713 statcount, (unsigned long long)(time(NULL) - t)); 714 } 715 716 /************************************************************************** 717 Tests 718 **************************************************************************/ 719 720 /* Check for appledouble file, create if missing, but only if we have addir */ 721 const char *name = NULL; 722 adfile_ok = -1; 723 if (ADDIR_OK) 724 adfile_ok = check_adfile(ep->d_name, &st, &name); 725 726 if (!S_ISLNK(st.st_mode)) { 727 if (name == NULL) { 728 name = ep->d_name; 729 } else { 730 update_cnid(did, &st, ep->d_name, name); 731 } 732 733 /* Check CNIDs */ 734 cnid = check_cnid(name, did, &st, adfile_ok); 735 736 /* Check EA files */ 737 if (vol->v_vfs_ea == AFPVOL_EA_AD) 738 check_eafiles(name); 739 } 740 741 /************************************************************************** 742 Recursion 743 **************************************************************************/ 744 if (S_ISDIR(st.st_mode) && cnid) { /* If we have no cnid for it we cant enter recursion */ 745 strcat(cwdbuf, "/"); 746 strcat(cwdbuf, name); 747 dbd_log( LOGDEBUG, "Entering directory: %s", cwdbuf); 748 if (-1 == (cwd = open(".", O_RDONLY))) { 749 dbd_log( LOGSTD, "Cant open directory '%s': %s", cwdbuf, strerror(errno)); 750 continue; 751 } 752 if (0 != chdir(name)) { 753 dbd_log( LOGSTD, "Cant chdir to directory '%s': %s", cwdbuf, strerror(errno)); 754 close(cwd); 755 continue; 756 } 757 758 ret = dbd_readdir(0, cnid); 759 760 fchdir(cwd); 761 close(cwd); 762 *(strrchr(cwdbuf, '/')) = 0; 763 if (ret < 0) 764 return -1; 765 } 766 } 767 768 /* 769 Use results of previous checks 770 */ 771 if ((vol->v_adouble == AD_VERSION_EA) && (dbd_flags & DBD_FLAGS_V2TOEA)) { 772 if (rmdir(ADv2_DIRNAME) != 0) { 773 switch (errno) { 774 case ENOENT: 775 break; 776 default: 777 dbd_log(LOGSTD, "Error removing adouble dir \"%s/%s\": %s", cwdbuf, ADv2_DIRNAME, strerror(errno)); 778 break; 779 } 780 } 781 } 782 closedir(dp); 783 return ret; 784} 785 786/* 787 Main func called from cmd_dbd.c 788*/ 789int cmd_dbd_scanvol(struct vol *vol_in, dbd_flags_t flags) 790{ 791 EC_INIT; 792 struct stat st; 793 794 /* Run with umask 0 */ 795 umask(0); 796 797 /* Make vol accessible for all funcs */ 798 vol = vol_in; 799 dbd_flags = flags; 800 801 /* We only support unicode volumes ! */ 802 if (vol->v_volcharset != CH_UTF8) { 803 dbd_log(LOGSTD, "Not a Unicode volume: %s, %u != %u", vol->v_volcodepage, vol->v_volcharset, CH_UTF8); 804 EC_FAIL; 805 } 806 807 /* 808 * Get CNID database stamp, cnid_getstamp() passes the buffer, 809 * then cnid_resolve() actually gets the value from the db 810 */ 811 cnid_getstamp(vol->v_cdb, stamp, sizeof(stamp)); 812 813 if (setjmp(jmp) != 0) { 814 EC_EXIT_STATUS(0); /* Got signal, jump from dbd_readdir */ 815 } 816 817 strcpy(cwdbuf, vol->v_path); 818 chdir(vol->v_path); 819 820 if ((vol->v_adouble == AD_VERSION_EA) && (dbd_flags & DBD_FLAGS_V2TOEA)) { 821 if (lstat(".", &st) != 0) 822 EC_FAIL; 823 if (ad_convert(".", &st, vol, NULL) != 0) { 824 switch (errno) { 825 case ENOENT: 826 break; 827 default: 828 dbd_log(LOGSTD, "Conversion error for \"%s\": %s", vol->v_path, strerror(errno)); 829 break; 830 } 831 } 832 } 833 834 /* Start recursion */ 835 EC_NEG1( dbd_readdir(1, htonl(2)) ); /* 2 = volumeroot CNID */ 836 837EC_CLEANUP: 838 EC_EXIT; 839} 840