1/* 2 Copyright (c) 2004 Didier Gautheron 3 Copyright (c) 2009 Frank Lahm 4 5 This program is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by 7 the Free Software Foundation; either version 2 of the License, or 8 (at your option) any later version. 9 10 This program is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program; if not, write to the Free Software 17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 18 19*/ 20#ifdef HAVE_CONFIG_H 21#include "config.h" 22#endif /* HAVE_CONFIG_H */ 23 24#include <sys/types.h> 25#include <sys/stat.h> 26#include <unistd.h> 27#include <string.h> 28#include <stdio.h> 29#include <stdlib.h> 30#include <libgen.h> 31 32#include <atalk/afp.h> 33#include <atalk/adouble.h> 34#include <atalk/ea.h> 35#include <atalk/acl.h> 36#include <atalk/logger.h> 37#include <atalk/util.h> 38#include <atalk/volume.h> 39#include <atalk/vfs.h> 40#include <atalk/directory.h> 41#include <atalk/unix.h> 42#include <atalk/errchk.h> 43#include <atalk/bstrlib.h> 44#include <atalk/bstradd.h> 45 46struct perm { 47 uid_t uid; 48 gid_t gid; 49}; 50 51typedef int (*rf_loop)(struct dirent *, char *, void *, int , mode_t ); 52 53/* ----------------------------- */ 54static int 55for_each_adouble(const char *from, const char *name, rf_loop fn, void *data, int flag, mode_t v_umask) 56{ 57 char buf[ MAXPATHLEN + 1]; 58 char *m; 59 DIR *dp; 60 struct dirent *de; 61 int ret; 62 63 64 if (NULL == ( dp = opendir( name)) ) { 65 if (!flag) { 66 LOG(log_error, logtype_afpd, "%s: opendir %s: %s", from, fullpathname(name),strerror(errno) ); 67 return -1; 68 } 69 return 0; 70 } 71 strlcpy( buf, name, sizeof(buf)); 72 strlcat( buf, "/", sizeof(buf) ); 73 m = strchr( buf, '\0' ); 74 ret = 0; 75 while ((de = readdir(dp))) { 76 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) { 77 continue; 78 } 79 80 strlcat(buf, de->d_name, sizeof(buf)); 81 if (fn && (ret = fn(de, buf, data, flag, v_umask))) { 82 closedir(dp); 83 return ret; 84 } 85 *m = 0; 86 } 87 closedir(dp); 88 return ret; 89} 90 91/******************************************************************************* 92 * classic adouble format 93 *******************************************************************************/ 94 95static int netatalk_name(const char *name) 96{ 97 return strcasecmp(name,".AppleDouble") && 98 strcasecmp(name,".AppleDB") && 99 strcasecmp(name,".AppleDesktop"); 100} 101 102static int validupath_adouble(VFS_FUNC_ARGS_VALIDUPATH) 103{ 104 if (name[0] != '.') 105 return 1; 106 107 if (!(vol->v_flags & AFPVOL_USEDOTS)) 108 return 0; 109 110 return netatalk_name(name) && strcasecmp(name,".Parent"); 111} 112 113/* ----------------- */ 114static int RF_chown_adouble(VFS_FUNC_ARGS_CHOWN) 115{ 116 struct stat st; 117 char *ad_p; 118 119 ad_p = vol->ad_path(path, ADFLAGS_HF ); 120 121 if ( stat( ad_p, &st ) < 0 ) 122 return 0; /* ignore */ 123 124 return chown( ad_p, uid, gid ); 125} 126 127/* ----------------- */ 128static int RF_renamedir_adouble(VFS_FUNC_ARGS_RENAMEDIR) 129{ 130 return 0; 131} 132 133/* ----------------- */ 134static int deletecurdir_adouble_loop(struct dirent *de, char *name, void *data _U_, int flag _U_, mode_t v_umask) 135{ 136 struct stat st; 137 int err; 138 139 /* bail if the file exists in the current directory. 140 * note: this will not fail with dangling symlinks */ 141 142 if (stat(de->d_name, &st) == 0) 143 return AFPERR_DIRNEMPT; 144 145 if ((err = netatalk_unlink(name))) 146 return err; 147 148 return 0; 149} 150 151static int RF_deletecurdir_adouble(VFS_FUNC_ARGS_DELETECURDIR) 152{ 153 int err; 154 155 /* delete stray .AppleDouble files. this happens to get .Parent files 156 as well. */ 157 if ((err = for_each_adouble("deletecurdir", ".AppleDouble", deletecurdir_adouble_loop, NULL, 1, vol->v_umask))) 158 return err; 159 return netatalk_rmdir(-1, ".AppleDouble" ); 160} 161 162/* ----------------- */ 163static int adouble_setfilmode(const char * name, mode_t mode, struct stat *st, mode_t v_umask) 164{ 165 return setfilmode(name, ad_hf_mode(mode), st, v_umask); 166} 167 168static int RF_setfilmode_adouble(VFS_FUNC_ARGS_SETFILEMODE) 169{ 170 return adouble_setfilmode(vol->ad_path(name, ADFLAGS_HF ), mode, st, vol->v_umask); 171} 172 173/* ----------------- */ 174static int RF_setdirunixmode_adouble(VFS_FUNC_ARGS_SETDIRUNIXMODE) 175{ 176 char *adouble = vol->ad_path(name, ADFLAGS_DIR ); 177 int dropbox = vol->v_flags; 178 179 if (dir_rx_set(mode)) { 180 if (stickydirmode(ad_dir(adouble), DIRBITS | mode, dropbox, vol->v_umask) < 0 ) 181 return -1; 182 } 183 184 if (adouble_setfilmode(vol->ad_path(name, ADFLAGS_DIR ), mode, st, vol->v_umask) < 0) 185 return -1; 186 187 if (!dir_rx_set(mode)) { 188 if (stickydirmode(ad_dir(adouble), DIRBITS | mode, dropbox, vol->v_umask) < 0 ) 189 return -1 ; 190 } 191 return 0; 192} 193 194/* ----------------- */ 195static int setdirmode_adouble_loop(struct dirent *de _U_, char *name, void *data, int flag, mode_t v_umask) 196{ 197 mode_t hf_mode = *(mode_t *)data; 198 struct stat st; 199 200 if ( stat( name, &st ) < 0 ) { 201 if (flag) 202 return 0; 203 LOG(log_error, logtype_afpd, "setdirmode: stat %s: %s", name, strerror(errno) ); 204 } 205 else if (!S_ISDIR(st.st_mode)) { 206 if (setfilmode(name, hf_mode , &st, v_umask) < 0) { 207 /* FIXME what do we do then? */ 208 } 209 } 210 return 0; 211} 212 213static int RF_setdirmode_adouble(VFS_FUNC_ARGS_SETDIRMODE) 214{ 215 int dropbox = vol->v_flags; 216 mode_t hf_mode = ad_hf_mode(mode); 217 char *adouble = vol->ad_path(name, ADFLAGS_DIR ); 218 char *adouble_p = ad_dir(adouble); 219 220 if (dir_rx_set(mode)) { 221 if (stickydirmode(ad_dir(adouble), DIRBITS | mode, dropbox, vol->v_umask) < 0) 222 return -1; 223 } 224 225 if (for_each_adouble("setdirmode", adouble_p, setdirmode_adouble_loop, &hf_mode, vol_noadouble(vol), vol->v_umask)) 226 return -1; 227 228 if (!dir_rx_set(mode)) { 229 if (stickydirmode(ad_dir(adouble), DIRBITS | mode, dropbox, vol->v_umask) < 0) 230 return -1 ; 231 } 232 return 0; 233} 234 235/* ----------------- */ 236static int setdirowner_adouble_loop(struct dirent *de _U_, char *name, void *data, int flag _U_, mode_t v_umask _U_) 237{ 238 struct perm *owner = data; 239 240 if ( chown( name, owner->uid, owner->gid ) < 0 && errno != EPERM ) { 241 LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s", 242 owner->uid, owner->gid, fullpathname(name), strerror(errno) ); 243 /* return ( -1 ); Sometimes this is okay */ 244 } 245 return 0; 246} 247 248static int RF_setdirowner_adouble(VFS_FUNC_ARGS_SETDIROWNER) 249{ 250 int noadouble = vol_noadouble(vol); 251 char *adouble_p; 252 struct stat st; 253 struct perm owner; 254 255 owner.uid = uid; 256 owner.gid = gid; 257 258 adouble_p = ad_dir(vol->ad_path(name, ADFLAGS_DIR )); 259 260 if (for_each_adouble("setdirowner", adouble_p, setdirowner_adouble_loop, &owner, noadouble, vol->v_umask)) 261 return -1; 262 263 /* 264 * We cheat: we know that chown doesn't do anything. 265 */ 266 if ( stat( ".AppleDouble", &st ) < 0) { 267 if (errno == ENOENT && noadouble) 268 return 0; 269 LOG(log_error, logtype_afpd, "setdirowner: stat %s: %s", fullpathname(".AppleDouble"), strerror(errno) ); 270 return -1; 271 } 272 if ( gid && gid != st.st_gid && chown( ".AppleDouble", uid, gid ) < 0 && errno != EPERM ) { 273 LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s", 274 uid, gid,fullpathname(".AppleDouble"), strerror(errno) ); 275 /* return ( -1 ); Sometimes this is okay */ 276 } 277 return 0; 278} 279 280/* ----------------- */ 281static int RF_deletefile_adouble(VFS_FUNC_ARGS_DELETEFILE) 282{ 283 return netatalk_unlinkat(dirfd, vol->ad_path(file, ADFLAGS_HF)); 284} 285 286/* ----------------- */ 287static int RF_renamefile_adouble(VFS_FUNC_ARGS_RENAMEFILE) 288{ 289 char adsrc[ MAXPATHLEN + 1]; 290 int err = 0; 291 292 strcpy( adsrc, vol->ad_path(src, 0 )); 293 if (unix_rename(dirfd, adsrc, -1, vol->ad_path(dst, 0 )) < 0) { 294 struct stat st; 295 296 err = errno; 297 if (errno == ENOENT) { 298 struct adouble ad; 299 300 if (lstatat(dirfd, adsrc, &st)) /* source has no ressource fork, */ 301 return 0; 302 303 /* We are here because : 304 * -there's no dest folder. 305 * -there's no .AppleDouble in the dest folder. 306 * if we use the struct adouble passed in parameter it will not 307 * create .AppleDouble if the file is already opened, so we 308 * use a diff one, it's not a pb,ie it's not the same file, yet. 309 */ 310 ad_init(&ad, vol->v_adouble, vol->v_ad_options); 311 if (!ad_open(dst, ADFLAGS_HF, O_RDWR | O_CREAT, 0666, &ad)) { 312 ad_close(&ad, ADFLAGS_HF); 313 if (!unix_rename(dirfd, adsrc, -1, vol->ad_path(dst, 0 )) ) 314 err = 0; 315 else 316 err = errno; 317 } 318 else { /* it's something else, bail out */ 319 err = errno; 320 } 321 } 322 } 323 if (err) { 324 errno = err; 325 return -1; 326 } 327 return 0; 328} 329 330static int RF_copyfile_adouble(VFS_FUNC_ARGS_COPYFILE) 331/* const struct vol *vol, int sfd, const char *src, const char *dst */ 332{ 333 EC_INIT; 334 bstring s = NULL, d = NULL; 335 char *dup1 = NULL; 336 char *dup2 = NULL; 337 char *dup3 = NULL; 338 char *dup4 = NULL; 339 const char *name = NULL; 340 const char *dir = NULL; 341 342 struct stat st; 343 EC_ZERO(stat(dst, &st)); 344 345 if (S_ISDIR(st.st_mode)) { 346 /* build src path to AppleDouble file*/ 347 EC_NULL(s = bfromcstr(src)); 348 EC_ZERO(bcatcstr(s, "/.AppleDouble/.Parent")); 349 350 /* build dst path to AppleDouble file*/ 351 EC_NULL(d = bfromcstr(dst)); 352 EC_ZERO(bcatcstr(d, "/.AppleDouble/.Parent")); 353 } else { 354 /* get basename */ 355 356 /* build src path to AppleDouble file*/ 357 EC_NULL(dup1 = strdup(src)); 358 EC_NULL(name = basename(strdup(dup1))); 359 360 EC_NULL(dup2 = strdup(src)); 361 EC_NULL(dir = dirname(dup2)); 362 EC_NULL(s = bfromcstr(dir)); 363 EC_ZERO(bcatcstr(s, "/.AppleDouble/")); 364 EC_ZERO(bcatcstr(s, name)); 365 366 /* build dst path to AppleDouble file*/ 367 EC_NULL(dup4 = strdup(dst)); 368 EC_NULL(name = basename(strdup(dup4))); 369 370 EC_NULL(dup3 = strdup(dst)); 371 EC_NULL(dir = dirname(dup3)); 372 EC_NULL(d = bfromcstr(dir)); 373 EC_ZERO(bcatcstr(d, "/.AppleDouble/")); 374 EC_ZERO(bcatcstr(d, name)); 375 } 376 377 EC_ZERO(copy_file(sfd, cfrombstr(s), cfrombstr(d), 0666)); 378 379EC_CLEANUP: 380 bdestroy(s); 381 bdestroy(d); 382 if (dup1) free(dup1); 383 if (dup2) free(dup2); 384 if (dup3) free(dup3); 385 if (dup4) free(dup4); 386 387 EC_EXIT; 388} 389 390#ifdef HAVE_SOLARIS_ACLS 391static int RF_solaris_acl(VFS_FUNC_ARGS_ACL) 392{ 393 static char buf[ MAXPATHLEN + 1]; 394 struct stat st; 395 int len; 396 397 if ((stat(path, &st)) != 0) 398 return -1; 399 if (S_ISDIR(st.st_mode)) { 400 len = snprintf(buf, MAXPATHLEN, "%s/.AppleDouble",path); 401 if (len < 0 || len >= MAXPATHLEN) 402 return -1; 403 /* set acl on .AppleDouble dir first */ 404 if ((acl(buf, cmd, count, aces)) != 0) 405 return -1; 406 /* now set ACL on ressource fork */ 407 if ((acl(vol->ad_path(path, ADFLAGS_DIR), cmd, count, aces)) != 0) 408 return -1; 409 } else 410 /* set ACL on ressource fork */ 411 if ((acl(vol->ad_path(path, ADFLAGS_HF), cmd, count, aces)) != 0) 412 return -1; 413 414 return 0; 415} 416 417static int RF_solaris_remove_acl(VFS_FUNC_ARGS_REMOVE_ACL) 418{ 419 int ret; 420 static char buf[ MAXPATHLEN + 1]; 421 int len; 422 423 if (dir) { 424 len = snprintf(buf, MAXPATHLEN, "%s/.AppleDouble",path); 425 if (len < 0 || len >= MAXPATHLEN) 426 return AFPERR_MISC; 427 /* remove ACL from .AppleDouble/.Parent first */ 428 if ((ret = remove_acl_vfs(vol->ad_path(path, ADFLAGS_DIR))) != AFP_OK) 429 return ret; 430 /* now remove from .AppleDouble dir */ 431 if ((ret = remove_acl_vfs(buf)) != AFP_OK) 432 return ret; 433 } else 434 /* remove ACL from ressource fork */ 435 if ((ret = remove_acl_vfs(vol->ad_path(path, ADFLAGS_HF))) != AFP_OK) 436 return ret; 437 438 return AFP_OK; 439} 440#endif 441 442#ifdef HAVE_POSIX_ACLS 443static int RF_posix_acl(VFS_FUNC_ARGS_ACL) 444{ 445 EC_INIT; 446 static char buf[ MAXPATHLEN + 1]; 447 struct stat st; 448 int len; 449 450 if (S_ISDIR(st.st_mode)) { 451 len = snprintf(buf, MAXPATHLEN, "%s/.AppleDouble",path); 452 if (len < 0 || len >= MAXPATHLEN) 453 EC_FAIL; 454 /* set acl on .AppleDouble dir first */ 455 EC_ZERO_LOG(acl_set_file(buf, type, acl)); 456 457 if (type == ACL_TYPE_ACCESS) 458 /* set ACL on ressource fork (".Parent") too */ 459 EC_ZERO_LOG(acl_set_file(vol->ad_path(path, ADFLAGS_DIR), type, acl)); 460 } else { 461 /* set ACL on ressource fork */ 462 EC_ZERO_LOG(acl_set_file(vol->ad_path(path, ADFLAGS_HF), type, acl)); 463 } 464 465EC_CLEANUP: 466 if (ret != 0) 467 return AFPERR_MISC; 468 return AFP_OK; 469} 470 471static int RF_posix_remove_acl(VFS_FUNC_ARGS_REMOVE_ACL) 472{ 473 EC_INIT; 474 static char buf[ MAXPATHLEN + 1]; 475 int len; 476 477 if (dir) { 478 len = snprintf(buf, MAXPATHLEN, "%s/.AppleDouble",path); 479 if (len < 0 || len >= MAXPATHLEN) 480 return AFPERR_MISC; 481 /* remove ACL from .AppleDouble/.Parent first */ 482 EC_ZERO_LOG_ERR(remove_acl_vfs(vol->ad_path(path, ADFLAGS_DIR)), AFPERR_MISC); 483 484 /* now remove from .AppleDouble dir */ 485 EC_ZERO_LOG_ERR(remove_acl_vfs(buf), AFPERR_MISC); 486 } else { 487 /* remove ACL from ressource fork */ 488 EC_ZERO_LOG_ERR(remove_acl_vfs(vol->ad_path(path, ADFLAGS_HF)), AFPERR_MISC); 489 } 490 491EC_CLEANUP: 492 EC_EXIT; 493} 494#endif 495 496/********************************************************************************* 497 * sfm adouble format 498 *********************************************************************************/ 499static int ads_chown_loop(struct dirent *de _U_, char *name, void *data, int flag _U_, mode_t v_umask _U_) 500{ 501 struct perm *owner = data; 502 503 if (chown( name , owner->uid, owner->gid ) < 0) { 504 return -1; 505 } 506 return 0; 507} 508 509static int RF_chown_ads(VFS_FUNC_ARGS_CHOWN) 510{ 511 struct stat st; 512 char *ad_p; 513 struct perm owner; 514 515 owner.uid = uid; 516 owner.gid = gid; 517 518 519 ad_p = ad_dir(vol->ad_path(path, ADFLAGS_HF )); 520 521 if ( stat( ad_p, &st ) < 0 ) { 522 /* ignore */ 523 return 0; 524 } 525 526 if (chown( ad_p, uid, gid ) < 0) { 527 return -1; 528 } 529 return for_each_adouble("chown_ads", ad_p, ads_chown_loop, &owner, 1, vol->v_umask); 530} 531 532/* --------------------------------- */ 533static int deletecurdir_ads1_loop(struct dirent *de _U_, char *name, void *data _U_, int flag _U_, mode_t v_umask _U_) 534{ 535 return netatalk_unlink(name); 536} 537 538static int ads_delete_rf(char *name) 539{ 540 int err; 541 542 if ((err = for_each_adouble("deletecurdir", name, deletecurdir_ads1_loop, NULL, 1, 0))) 543 return err; 544 /* FIXME 545 * it's a problem for a nfs mounted folder, there's .nfsxxx around 546 * for linux the following line solve it. 547 * but it could fail if rm .nfsxxx create a new .nfsyyy :( 548 */ 549 if ((err = for_each_adouble("deletecurdir", name, deletecurdir_ads1_loop, NULL, 1, 0))) 550 return err; 551 return netatalk_rmdir(-1, name); 552} 553 554static int deletecurdir_ads_loop(struct dirent *de, char *name, void *data _U_, int flag _U_, mode_t v_umask _U_) 555{ 556 struct stat st; 557 558 /* bail if the file exists in the current directory. 559 * note: this will not fail with dangling symlinks */ 560 561 if (stat(de->d_name, &st) == 0) { 562 return AFPERR_DIRNEMPT; 563 } 564 return ads_delete_rf(name); 565} 566 567static int RF_deletecurdir_ads(VFS_FUNC_ARGS_DELETECURDIR) 568{ 569 int err; 570 571 /* delete stray .AppleDouble files. this happens to get .Parent files as well. */ 572 if ((err = for_each_adouble("deletecurdir", ".AppleDouble", deletecurdir_ads_loop, NULL, 1, 0))) 573 return err; 574 575 return netatalk_rmdir(-1, ".AppleDouble" ); 576} 577 578/* ------------------- */ 579struct set_mode { 580 mode_t mode; 581 struct stat *st; 582}; 583 584static int ads_setfilmode_loop(struct dirent *de _U_, char *name, void *data, int flag _U_, mode_t v_umask) 585{ 586 struct set_mode *param = data; 587 588 return setfilmode(name, param->mode, param->st, v_umask); 589} 590 591static int ads_setfilmode(const char * name, mode_t mode, struct stat *st, mode_t v_umask) 592{ 593 mode_t file_mode = ad_hf_mode(mode); 594 mode_t dir_mode = file_mode; 595 struct set_mode param; 596 597 if ((dir_mode & (S_IRUSR | S_IWUSR ))) 598 dir_mode |= S_IXUSR; 599 if ((dir_mode & (S_IRGRP | S_IWGRP ))) 600 dir_mode |= S_IXGRP; 601 if ((dir_mode & (S_IROTH | S_IWOTH ))) 602 dir_mode |= S_IXOTH; 603 604 /* change folder */ 605 dir_mode |= DIRBITS; 606 if (dir_rx_set(dir_mode)) { 607 if (chmod( name, dir_mode ) < 0) 608 return -1; 609 } 610 param.st = st; 611 param.mode = file_mode; 612 if (for_each_adouble("setfilmode_ads", name, ads_setfilmode_loop, ¶m, 0, v_umask) < 0) 613 return -1; 614 615 if (!dir_rx_set(dir_mode)) { 616 if (chmod( name, dir_mode ) < 0) 617 return -1; 618 } 619 620 return 0; 621} 622 623static int RF_setfilmode_ads(VFS_FUNC_ARGS_SETFILEMODE) 624{ 625 return ads_setfilmode(ad_dir(vol->ad_path(name, ADFLAGS_HF )), mode, st, vol->v_umask); 626} 627 628/* ------------------- */ 629static int RF_setdirunixmode_ads(VFS_FUNC_ARGS_SETDIRUNIXMODE) 630{ 631 char *adouble = vol->ad_path(name, ADFLAGS_DIR ); 632 char ad_p[ MAXPATHLEN + 1]; 633 int dropbox = vol->v_flags; 634 635 strlcpy(ad_p,ad_dir(adouble), MAXPATHLEN + 1); 636 637 if (dir_rx_set(mode)) { 638 639 /* .AppleDouble */ 640 if (stickydirmode(ad_dir(ad_p), DIRBITS | mode, dropbox, vol->v_umask) < 0) 641 return -1; 642 643 /* .AppleDouble/.Parent */ 644 if (stickydirmode(ad_p, DIRBITS | mode, dropbox, vol->v_umask) < 0) 645 return -1; 646 } 647 648 if (ads_setfilmode(ad_dir(vol->ad_path(name, ADFLAGS_DIR)), mode, st, vol->v_umask) < 0) 649 return -1; 650 651 if (!dir_rx_set(mode)) { 652 if (stickydirmode(ad_p, DIRBITS | mode, dropbox, vol->v_umask) < 0) 653 return -1 ; 654 if (stickydirmode(ad_dir(ad_p), DIRBITS | mode, dropbox, vol->v_umask) < 0) 655 return -1; 656 } 657 return 0; 658} 659 660/* ------------------- */ 661struct dir_mode { 662 mode_t mode; 663 int dropbox; 664}; 665 666static int setdirmode_ads_loop(struct dirent *de _U_, char *name, void *data, int flag, mode_t v_umask) 667{ 668 669 struct dir_mode *param = data; 670 int ret = 0; /* 0 ignore error, -1 */ 671 672 if (dir_rx_set(param->mode)) { 673 if (stickydirmode(name, DIRBITS | param->mode, param->dropbox, v_umask) < 0) { 674 if (flag) { 675 return 0; 676 } 677 return ret; 678 } 679 } 680 if (ads_setfilmode(name, param->mode, NULL, v_umask) < 0) 681 return ret; 682 683 if (!dir_rx_set(param->mode)) { 684 if (stickydirmode(name, DIRBITS | param->mode, param->dropbox, v_umask) < 0) { 685 if (flag) { 686 return 0; 687 } 688 return ret; 689 } 690 } 691 return 0; 692} 693 694static int RF_setdirmode_ads(VFS_FUNC_ARGS_SETDIRMODE) 695{ 696 char *adouble = vol->ad_path(name, ADFLAGS_DIR ); 697 char ad_p[ MAXPATHLEN + 1]; 698 struct dir_mode param; 699 700 param.mode = mode; 701 param.dropbox = vol->v_flags; 702 703 strlcpy(ad_p,ad_dir(adouble), sizeof(ad_p)); 704 705 if (dir_rx_set(mode)) { 706 /* .AppleDouble */ 707 if (stickydirmode(ad_dir(ad_p), DIRBITS | mode, param.dropbox, vol->v_umask) < 0) 708 return -1; 709 } 710 711 if (for_each_adouble("setdirmode_ads", ad_dir(ad_p), setdirmode_ads_loop, ¶m, vol_noadouble(vol), vol->v_umask)) 712 return -1; 713 714 if (!dir_rx_set(mode)) { 715 if (stickydirmode(ad_dir(ad_p), DIRBITS | mode, param.dropbox, vol->v_umask) < 0 ) 716 return -1; 717 } 718 return 0; 719} 720 721/* ------------------- */ 722static int setdirowner_ads1_loop(struct dirent *de _U_, char *name, void *data, int flag _U_, mode_t v_umask _U_) 723{ 724 struct perm *owner = data; 725 726 if ( chown( name, owner->uid, owner->gid ) < 0 && errno != EPERM ) { 727 LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s", 728 owner->uid, owner->gid, fullpathname(name), strerror(errno) ); 729 /* return ( -1 ); Sometimes this is okay */ 730 } 731 return 0; 732} 733 734static int setdirowner_ads_loop(struct dirent *de _U_, char *name, void *data, int flag, mode_t v_umask _U_) 735{ 736 struct perm *owner = data; 737 738 if (for_each_adouble("setdirowner", name, setdirowner_ads1_loop, data, flag, 0) < 0) 739 return -1; 740 741 if ( chown( name, owner->uid, owner->gid ) < 0 && errno != EPERM ) { 742 LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s", 743 owner->uid, owner->gid, fullpathname(name), strerror(errno) ); 744 /* return ( -1 ); Sometimes this is okay */ 745 } 746 return 0; 747} 748 749static int RF_setdirowner_ads(VFS_FUNC_ARGS_SETDIROWNER) 750{ 751 int noadouble = vol_noadouble(vol); 752 char adouble_p[ MAXPATHLEN + 1]; 753 struct stat st; 754 struct perm owner; 755 756 owner.uid = uid; 757 owner.gid = gid; 758 759 strlcpy(adouble_p, ad_dir(vol->ad_path(name, ADFLAGS_DIR )), sizeof(adouble_p)); 760 761 if (for_each_adouble("setdirowner", ad_dir(adouble_p), setdirowner_ads_loop, &owner, noadouble, 0)) 762 return -1; 763 764 /* 765 * We cheat: we know that chown doesn't do anything. 766 */ 767 if ( stat( ".AppleDouble", &st ) < 0) { 768 if (errno == ENOENT && noadouble) 769 return 0; 770 LOG(log_error, logtype_afpd, "setdirowner: stat %s: %s", fullpathname(".AppleDouble"), strerror(errno) ); 771 return -1; 772 } 773 if ( gid && gid != st.st_gid && chown( ".AppleDouble", uid, gid ) < 0 && errno != EPERM ) { 774 LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s", 775 uid, gid,fullpathname(".AppleDouble"), strerror(errno) ); 776 /* return ( -1 ); Sometimes this is okay */ 777 } 778 return 0; 779} 780 781/* ------------------- */ 782static int RF_deletefile_ads(VFS_FUNC_ARGS_DELETEFILE) 783{ 784 int ret = 0; 785 int cwd = -1; 786 char *ad_p; 787 788 ad_p = ad_dir(vol->ad_path(file, ADFLAGS_HF )); 789 790 if (dirfd != -1) { 791 if (((cwd = open(".", O_RDONLY)) == -1) || (fchdir(dirfd) != 0)) { 792 ret = AFPERR_MISC; 793 goto exit; 794 } 795 } 796 797 ret = ads_delete_rf(ad_p); 798 799 if (dirfd != -1 && fchdir(cwd) != 0) { 800 LOG(log_error, logtype_afpd, "RF_deletefile_ads: cant chdir back. exit!"); 801 exit(EXITERR_SYS); 802 } 803 804exit: 805 if (cwd != -1) 806 close(cwd); 807 808 return ret; 809} 810 811/* --------------------------- */ 812static int RF_renamefile_ads(VFS_FUNC_ARGS_RENAMEFILE) 813{ 814 char adsrc[ MAXPATHLEN + 1]; 815 int err = 0; 816 817 strcpy( adsrc, ad_dir(vol->ad_path(src, 0 ))); 818 if (unix_rename(dirfd, adsrc, -1, ad_dir(vol->ad_path(dst, 0 ))) < 0) { 819 struct stat st; 820 821 err = errno; 822 if (errno == ENOENT) { 823 struct adouble ad; 824 825 if (lstatat(dirfd, adsrc, &st)) /* source has no ressource fork, */ 826 return 0; 827 828 /* We are here because : 829 * -there's no dest folder. 830 * -there's no .AppleDouble in the dest folder. 831 * if we use the struct adouble passed in parameter it will not 832 * create .AppleDouble if the file is already opened, so we 833 * use a diff one, it's not a pb,ie it's not the same file, yet. 834 */ 835 ad_init(&ad, vol->v_adouble, vol->v_ad_options); 836 if (!ad_open(dst, ADFLAGS_HF, O_RDWR | O_CREAT, 0666, &ad)) { 837 ad_close(&ad, ADFLAGS_HF); 838 839 /* We must delete it */ 840 RF_deletefile_ads(vol, -1, dst ); 841 if (!unix_rename(dirfd, adsrc, -1, ad_dir(vol->ad_path(dst, 0 ))) ) 842 err = 0; 843 else 844 err = errno; 845 } 846 else { /* it's something else, bail out */ 847 err = errno; 848 } 849 } 850 } 851 if (err) { 852 errno = err; 853 return -1; 854 } 855 return 0; 856} 857 858/************************************************************************* 859 * osx adouble format 860 ************************************************************************/ 861static int validupath_osx(VFS_FUNC_ARGS_VALIDUPATH) 862{ 863 return strncmp(name,"._", 2) && ( 864 (vol->v_flags & AFPVOL_USEDOTS) ? netatalk_name(name): name[0] != '.'); 865} 866 867/* ---------------- */ 868static int RF_renamedir_osx(VFS_FUNC_ARGS_RENAMEDIR) 869{ 870 /* We simply move the corresponding ad file as well */ 871 char tempbuf[258]="._"; 872 return unix_rename(dirfd, vol->ad_path(oldpath,0), -1, strcat(tempbuf,newpath)); 873} 874 875/* ---------------- */ 876static int RF_deletecurdir_osx(VFS_FUNC_ARGS_DELETECURDIR) 877{ 878 return netatalk_unlink( vol->ad_path(".",0) ); 879} 880 881/* ---------------- */ 882static int RF_setdirunixmode_osx(VFS_FUNC_ARGS_SETDIRUNIXMODE) 883{ 884 return adouble_setfilmode(vol->ad_path(name, ADFLAGS_DIR ), mode, st, vol->v_umask); 885} 886 887/* ---------------- */ 888static int RF_setdirmode_osx(VFS_FUNC_ARGS_SETDIRMODE) 889{ 890 return 0; 891} 892 893/* ---------------- */ 894static int RF_setdirowner_osx(VFS_FUNC_ARGS_SETDIROWNER) 895{ 896 return 0; 897} 898 899/* ---------------- */ 900static int RF_renamefile_osx(VFS_FUNC_ARGS_RENAMEFILE) 901{ 902 char adsrc[ MAXPATHLEN + 1]; 903 int err = 0; 904 905 strcpy( adsrc, vol->ad_path(src, 0 )); 906 907 if (unix_rename(dirfd, adsrc, -1, vol->ad_path(dst, 0 )) < 0) { 908 struct stat st; 909 910 err = errno; 911 if (errno == ENOENT && lstatat(dirfd, adsrc, &st)) /* source has no ressource fork, */ 912 return 0; 913 errno = err; 914 return -1; 915 } 916 return 0; 917} 918 919/******************************************************************************************** 920 * VFS chaining 921 ********************************************************************************************/ 922 923/* 924 * Up until we really start stacking many VFS modules on top of one another or use 925 * dynamic module loading like we do for UAMs, up until then we just stack VFS modules 926 * via an fixed size array. 927 * All VFS funcs must return AFP_ERR codes. When a func in the chain returns an error 928 * this error code will be returned to the caller, BUT the chain in followed and all 929 * following funcs are called in order to give them a chance. 930 */ 931 932/* 933 * Define most VFS funcs with macros as they all do the same. 934 * Only "ad_path" and "validupath" will NOT do stacking and only 935 * call the func from the first module. 936 */ 937 938#define VFS_MFUNC(name, args, vars) \ 939 static int vfs_ ## name(args) \ 940 { \ 941 int i = 0, ret = AFP_OK, err; \ 942 while (vol->vfs_modules[i]) { \ 943 if (vol->vfs_modules[i]->vfs_ ## name) { \ 944 err = vol->vfs_modules[i]->vfs_ ## name (vars); \ 945 if ((ret == AFP_OK) && (err != AFP_OK)) \ 946 ret = err; \ 947 } \ 948 i ++; \ 949 } \ 950 return ret; \ 951 } 952 953VFS_MFUNC(chown, VFS_FUNC_ARGS_CHOWN, VFS_FUNC_VARS_CHOWN) 954VFS_MFUNC(renamedir, VFS_FUNC_ARGS_RENAMEDIR, VFS_FUNC_VARS_RENAMEDIR) 955VFS_MFUNC(deletecurdir, VFS_FUNC_ARGS_DELETECURDIR, VFS_FUNC_VARS_DELETECURDIR) 956VFS_MFUNC(setfilmode, VFS_FUNC_ARGS_SETFILEMODE, VFS_FUNC_VARS_SETFILEMODE) 957VFS_MFUNC(setdirmode, VFS_FUNC_ARGS_SETDIRMODE, VFS_FUNC_VARS_SETDIRMODE) 958VFS_MFUNC(setdirunixmode, VFS_FUNC_ARGS_SETDIRUNIXMODE, VFS_FUNC_VARS_SETDIRUNIXMODE) 959VFS_MFUNC(setdirowner, VFS_FUNC_ARGS_SETDIROWNER, VFS_FUNC_VARS_SETDIROWNER) 960VFS_MFUNC(deletefile, VFS_FUNC_ARGS_DELETEFILE, VFS_FUNC_VARS_DELETEFILE) 961VFS_MFUNC(renamefile, VFS_FUNC_ARGS_RENAMEFILE, VFS_FUNC_VARS_RENAMEFILE) 962VFS_MFUNC(copyfile, VFS_FUNC_ARGS_COPYFILE, VFS_FUNC_VARS_COPYFILE) 963#ifdef HAVE_ACLS 964VFS_MFUNC(acl, VFS_FUNC_ARGS_ACL, VFS_FUNC_VARS_ACL) 965VFS_MFUNC(remove_acl, VFS_FUNC_ARGS_REMOVE_ACL, VFS_FUNC_VARS_REMOVE_ACL) 966#endif 967VFS_MFUNC(ea_getsize, VFS_FUNC_ARGS_EA_GETSIZE, VFS_FUNC_VARS_EA_GETSIZE) 968VFS_MFUNC(ea_getcontent, VFS_FUNC_ARGS_EA_GETCONTENT, VFS_FUNC_VARS_EA_GETCONTENT) 969VFS_MFUNC(ea_list, VFS_FUNC_ARGS_EA_LIST, VFS_FUNC_VARS_EA_LIST) 970VFS_MFUNC(ea_set, VFS_FUNC_ARGS_EA_SET, VFS_FUNC_VARS_EA_SET) 971VFS_MFUNC(ea_remove, VFS_FUNC_ARGS_EA_REMOVE, VFS_FUNC_VARS_EA_REMOVE) 972 973static int vfs_validupath(VFS_FUNC_ARGS_VALIDUPATH) 974{ 975 return vol->vfs_modules[0]->vfs_validupath(VFS_FUNC_VARS_VALIDUPATH); 976} 977 978/* 979 * These function pointers get called from the lib users via vol->vfs->func. 980 * These funcs are defined via the macros above. 981 */ 982static struct vfs_ops vfs_master_funcs = { 983 vfs_validupath, 984 vfs_chown, 985 vfs_renamedir, 986 vfs_deletecurdir, 987 vfs_setfilmode, 988 vfs_setdirmode, 989 vfs_setdirunixmode, 990 vfs_setdirowner, 991 vfs_deletefile, 992 vfs_renamefile, 993 vfs_copyfile, 994#ifdef HAVE_ACLS 995 vfs_acl, 996 vfs_remove_acl, 997#endif 998 vfs_ea_getsize, 999 vfs_ea_getcontent, 1000 vfs_ea_list, 1001 vfs_ea_set, 1002 vfs_ea_remove 1003}; 1004 1005/* 1006 * Primary adouble modules: default, osx, sfm 1007 */ 1008 1009static struct vfs_ops netatalk_adouble = { 1010 /* vfs_validupath: */ validupath_adouble, 1011 /* vfs_chown: */ RF_chown_adouble, 1012 /* vfs_renamedir: */ RF_renamedir_adouble, 1013 /* vfs_deletecurdir: */ RF_deletecurdir_adouble, 1014 /* vfs_setfilmode: */ RF_setfilmode_adouble, 1015 /* vfs_setdirmode: */ RF_setdirmode_adouble, 1016 /* vfs_setdirunixmode:*/ RF_setdirunixmode_adouble, 1017 /* vfs_setdirowner: */ RF_setdirowner_adouble, 1018 /* vfs_deletefile: */ RF_deletefile_adouble, 1019 /* vfs_renamefile: */ RF_renamefile_adouble, 1020 /* vfs_copyfile: */ RF_copyfile_adouble, 1021 NULL 1022}; 1023 1024static struct vfs_ops netatalk_adouble_osx = { 1025 /* vfs_validupath: */ validupath_osx, 1026 /* vfs_chown: */ RF_chown_adouble, 1027 /* vfs_renamedir: */ RF_renamedir_osx, 1028 /* vfs_deletecurdir: */ RF_deletecurdir_osx, 1029 /* vfs_setfilmode: */ RF_setfilmode_adouble, 1030 /* vfs_setdirmode: */ RF_setdirmode_osx, 1031 /* vfs_setdirunixmode:*/ RF_setdirunixmode_osx, 1032 /* vfs_setdirowner: */ RF_setdirowner_osx, 1033 /* vfs_deletefile: */ RF_deletefile_adouble, 1034 /* vfs_renamefile: */ RF_renamefile_osx, 1035 /* vfs_copyfile: */ NULL, 1036 NULL 1037}; 1038 1039/* samba sfm format. ad_path shouldn't be set her */ 1040static struct vfs_ops netatalk_adouble_sfm = { 1041 /* vfs_validupath: */ validupath_adouble, 1042 /* vfs_chown: */ RF_chown_ads, 1043 /* vfs_renamedir: */ RF_renamedir_adouble, 1044 /* vfs_deletecurdir: */ RF_deletecurdir_ads, 1045 /* vfs_setfilmode: */ RF_setfilmode_ads, 1046 /* vfs_setdirmode: */ RF_setdirmode_ads, 1047 /* vfs_setdirunixmode:*/ RF_setdirunixmode_ads, 1048 /* vfs_setdirowner: */ RF_setdirowner_ads, 1049 /* vfs_deletefile: */ RF_deletefile_ads, 1050 /* vfs_renamefile: */ RF_renamefile_ads, 1051 /* vfs_copyfile: */ NULL, 1052 NULL 1053}; 1054 1055/* 1056 * Secondary vfs modules for Extended Attributes 1057 */ 1058 1059static struct vfs_ops netatalk_ea_adouble = { 1060 /* vfs_validupath: */ NULL, 1061 /* vfs_chown: */ ea_chown, 1062 /* vfs_renamedir: */ NULL, /* ok */ 1063 /* vfs_deletecurdir: */ NULL, /* ok */ 1064 /* vfs_setfilmode: */ ea_chmod_file, 1065 /* vfs_setdirmode: */ NULL, /* ok */ 1066 /* vfs_setdirunixmode:*/ ea_chmod_dir, 1067 /* vfs_setdirowner: */ NULL, /* ok */ 1068 /* vfs_deletefile: */ ea_deletefile, 1069 /* vfs_renamefile: */ ea_renamefile, 1070 /* vfs_copyfile */ ea_copyfile, 1071#ifdef HAVE_ACLS 1072 /* vfs_acl: */ NULL, 1073 /* vfs_remove_acl */ NULL, 1074#endif 1075 /* vfs_getsize */ get_easize, 1076 /* vfs_getcontent */ get_eacontent, 1077 /* vfs_list */ list_eas, 1078 /* vfs_set */ set_ea, 1079 /* vfs_remove */ remove_ea 1080}; 1081 1082static struct vfs_ops netatalk_ea_sys = { 1083 /* validupath: */ NULL, 1084 /* rf_chown: */ NULL, 1085 /* rf_renamedir: */ NULL, 1086 /* rf_deletecurdir: */ NULL, 1087 /* rf_setfilmode: */ NULL, 1088 /* rf_setdirmode: */ NULL, 1089 /* rf_setdirunixmode: */ NULL, 1090 /* rf_setdirowner: */ NULL, 1091 /* rf_deletefile: */ NULL, 1092 /* rf_renamefile: */ NULL, 1093 /* vfs_copyfile: */ sys_ea_copyfile, 1094#ifdef HAVE_ACLS 1095 /* rf_acl: */ NULL, 1096 /* rf_remove_acl */ NULL, 1097#endif 1098 /* ea_getsize */ sys_get_easize, 1099 /* ea_getcontent */ sys_get_eacontent, 1100 /* ea_list */ sys_list_eas, 1101 /* ea_set */ sys_set_ea, 1102 /* ea_remove */ sys_remove_ea 1103}; 1104 1105/* 1106 * Tertiary VFS modules for ACLs 1107 */ 1108 1109#ifdef HAVE_SOLARIS_ACLS 1110static struct vfs_ops netatalk_solaris_acl_adouble = { 1111 /* validupath: */ NULL, 1112 /* rf_chown: */ NULL, 1113 /* rf_renamedir: */ NULL, 1114 /* rf_deletecurdir: */ NULL, 1115 /* rf_setfilmode: */ NULL, 1116 /* rf_setdirmode: */ NULL, 1117 /* rf_setdirunixmode: */ NULL, 1118 /* rf_setdirowner: */ NULL, 1119 /* rf_deletefile: */ NULL, 1120 /* rf_renamefile: */ NULL, 1121 /* vfs_copyfile */ NULL, 1122 /* rf_acl: */ RF_solaris_acl, 1123 /* rf_remove_acl */ RF_solaris_remove_acl, 1124 NULL 1125}; 1126#endif 1127 1128#ifdef HAVE_POSIX_ACLS 1129static struct vfs_ops netatalk_posix_acl_adouble = { 1130 /* validupath: */ NULL, 1131 /* rf_chown: */ NULL, 1132 /* rf_renamedir: */ NULL, 1133 /* rf_deletecurdir: */ NULL, 1134 /* rf_setfilmode: */ NULL, 1135 /* rf_setdirmode: */ NULL, 1136 /* rf_setdirunixmode: */ NULL, 1137 /* rf_setdirowner: */ NULL, 1138 /* rf_deletefile: */ NULL, 1139 /* rf_renamefile: */ NULL, 1140 /* vfs_copyfile */ NULL, 1141 /* rf_acl: */ RF_posix_acl, 1142 /* rf_remove_acl */ RF_posix_remove_acl, 1143 NULL 1144}; 1145#endif 1146 1147/* ---------------- */ 1148void initvol_vfs(struct vol *vol) 1149{ 1150 vol->vfs = &vfs_master_funcs; 1151 1152 /* Default adouble stuff */ 1153 if (vol->v_adouble == AD_VERSION2_OSX) { 1154 vol->vfs_modules[0] = &netatalk_adouble_osx; 1155 vol->ad_path = ad_path_osx; 1156 } 1157 else if (vol->v_adouble == AD_VERSION1_SFM) { 1158 vol->vfs_modules[0] = &netatalk_adouble_sfm; 1159 vol->ad_path = ad_path_sfm; 1160 } 1161 else { 1162 vol->vfs_modules[0] = &netatalk_adouble; 1163 vol->ad_path = ad_path; 1164 } 1165 1166 /* Extended Attributes */ 1167 if (vol->v_vfs_ea == AFPVOL_EA_SYS) { 1168 LOG(log_debug, logtype_afpd, "initvol_vfs: enabling EA support with native EAs"); 1169 vol->vfs_modules[1] = &netatalk_ea_sys; 1170 } else if (vol->v_vfs_ea == AFPVOL_EA_AD) { 1171 LOG(log_debug, logtype_afpd, "initvol_vfs: enabling EA support with adouble files"); 1172 vol->vfs_modules[1] = &netatalk_ea_adouble; 1173 } else { 1174 LOG(log_debug, logtype_afpd, "initvol_vfs: volume without EA support"); 1175 } 1176 1177 /* ACLs */ 1178#ifdef HAVE_SOLARIS_ACLS 1179 vol->vfs_modules[2] = &netatalk_solaris_acl_adouble; 1180#endif 1181#ifdef HAVE_POSIX_ACLS 1182 vol->vfs_modules[2] = &netatalk_posix_acl_adouble; 1183#endif 1184 1185} 1186