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)(const struct vol *, struct dirent *, char *, void *, int); 52 53/* ----------------------------- */ 54static int 55for_each_adouble(const char *from, const char *name, rf_loop fn, const struct vol *vol, void *data, int flag) 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(vol, de, buf, data, flag))) { 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(const struct vol *vol, struct dirent *de, char *name, void *data _U_, int flag _U_) 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, vol, NULL, 1))) 158 return err; 159 return netatalk_rmdir(-1, ".AppleDouble" ); 160} 161 162/* ----------------- */ 163static int adouble_setfilmode(const struct vol *vol, const char *name, mode_t mode, struct stat *st) 164{ 165 return setfilmode(vol, name, ad_hf_mode(mode), st); 166} 167 168static int RF_setfilmode_adouble(VFS_FUNC_ARGS_SETFILEMODE) 169{ 170 return adouble_setfilmode(vol, vol->ad_path(name, ADFLAGS_HF ), mode, st); 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, vol->ad_path(name, ADFLAGS_DIR ), mode, st) < 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(const struct vol *vol, struct dirent *de _U_, char *name, void *data, int flag) 196{ 197 mode_t hf_mode = *(mode_t *)data; 198 struct stat st; 199 200 if (ostat(name, &st, vol_syml_opt(vol)) < 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(vol, name, hf_mode, &st) < 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, vol, &hf_mode, vol_noadouble(vol))) 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(const struct vol *vol, struct dirent *de _U_, char *name, void *data, int flag _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, vol, &owner, noadouble)) 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 (ostatat(dirfd, adsrc, &st, vol_syml_opt(vol))) /* 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(const struct vol *vol, struct dirent *de _U_, char *name, void *data, int flag _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, vol, &owner, 1); 530} 531 532/* --------------------------------- */ 533static int deletecurdir_ads1_loop(const struct vol *vol _U_, struct dirent *de _U_, char *name, void *data _U_, int flag _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, NULL, 1))) 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, NULL, 1))) 550 return err; 551 return netatalk_rmdir(-1, name); 552} 553 554static int deletecurdir_ads_loop(const struct vol *vol, struct dirent *de, char *name, void *data _U_, int flag _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, vol, NULL, 1))) 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(const struct vol *vol, struct dirent *de _U_, char *name, void *data, int flag _U_) 585{ 586 struct set_mode *param = data; 587 588 return setfilmode(vol, name, param->mode, NULL); 589} 590 591static int ads_setfilmode(const struct vol *vol, const char * name, mode_t mode, struct stat *st) 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#ifdef HAVE_ACLS 608 if (ochmod(name, dir_mode, st, vol_syml_opt(vol) | O_NETATALK_ACL) < 0) 609 return -1; 610#else 611 if (ochmod(name, dir_mode, st, vol_syml_opt(vol)) < 0) 612 return -1; 613#endif 614 } 615 param.st = st; 616 param.mode = file_mode; 617 if (for_each_adouble("setfilmode_ads", name, ads_setfilmode_loop, vol, ¶m, 0) < 0) 618 return -1; 619 620 if (!dir_rx_set(dir_mode)) { 621#ifdef HAVE_ACLS 622 if (ochmod(name, dir_mode, st, vol_syml_opt(vol) | O_NETATALK_ACL) < 0) 623 return -1; 624#else 625 if (ochmod(name, dir_mode, st, vol_syml_opt(vol)) < 0) 626 return -1; 627#endif 628 } 629 630 return 0; 631} 632 633static int RF_setfilmode_ads(VFS_FUNC_ARGS_SETFILEMODE) 634{ 635 return ads_setfilmode(vol, ad_dir(vol->ad_path(name, ADFLAGS_HF )), mode, st); 636} 637 638/* ------------------- */ 639static int RF_setdirunixmode_ads(VFS_FUNC_ARGS_SETDIRUNIXMODE) 640{ 641 char *adouble = vol->ad_path(name, ADFLAGS_DIR ); 642 char ad_p[ MAXPATHLEN + 1]; 643 int dropbox = vol->v_flags; 644 645 strlcpy(ad_p,ad_dir(adouble), MAXPATHLEN + 1); 646 647 if (dir_rx_set(mode)) { 648 649 /* .AppleDouble */ 650 if (stickydirmode(ad_dir(ad_p), DIRBITS | mode, dropbox, vol->v_umask) < 0) 651 return -1; 652 653 /* .AppleDouble/.Parent */ 654 if (stickydirmode(ad_p, DIRBITS | mode, dropbox, vol->v_umask) < 0) 655 return -1; 656 } 657 658 if (ads_setfilmode(vol, ad_dir(vol->ad_path(name, ADFLAGS_DIR)), mode, st) < 0) 659 return -1; 660 661 if (!dir_rx_set(mode)) { 662 if (stickydirmode(ad_p, DIRBITS | mode, dropbox, vol->v_umask) < 0) 663 return -1 ; 664 if (stickydirmode(ad_dir(ad_p), DIRBITS | mode, dropbox, vol->v_umask) < 0) 665 return -1; 666 } 667 return 0; 668} 669 670/* ------------------- */ 671struct dir_mode { 672 mode_t mode; 673 int dropbox; 674}; 675 676static int setdirmode_ads_loop(const struct vol *vol, struct dirent *de _U_, char *name, void *data, int flag) 677{ 678 679 struct dir_mode *param = data; 680 int ret = 0; /* 0 ignore error, -1 */ 681 682 if (dir_rx_set(param->mode)) { 683 if (stickydirmode(name, DIRBITS | param->mode, param->dropbox, vol->v_umask) < 0) { 684 if (flag) { 685 return 0; 686 } 687 return ret; 688 } 689 } 690 if (ads_setfilmode(vol, name, param->mode, NULL) < 0) 691 return ret; 692 693 if (!dir_rx_set(param->mode)) { 694 if (stickydirmode(name, DIRBITS | param->mode, param->dropbox, vol->v_umask) < 0) { 695 if (flag) { 696 return 0; 697 } 698 return ret; 699 } 700 } 701 return 0; 702} 703 704static int RF_setdirmode_ads(VFS_FUNC_ARGS_SETDIRMODE) 705{ 706 char *adouble = vol->ad_path(name, ADFLAGS_DIR ); 707 char ad_p[ MAXPATHLEN + 1]; 708 struct dir_mode param; 709 710 param.mode = mode; 711 param.dropbox = vol->v_flags; 712 713 strlcpy(ad_p,ad_dir(adouble), sizeof(ad_p)); 714 715 if (dir_rx_set(mode)) { 716 /* .AppleDouble */ 717 if (stickydirmode(ad_dir(ad_p), DIRBITS | mode, param.dropbox, vol->v_umask) < 0) 718 return -1; 719 } 720 721 if (for_each_adouble("setdirmode_ads", ad_dir(ad_p), setdirmode_ads_loop, vol, ¶m, vol_noadouble(vol))) 722 return -1; 723 724 if (!dir_rx_set(mode)) { 725 if (stickydirmode(ad_dir(ad_p), DIRBITS | mode, param.dropbox, vol->v_umask) < 0 ) 726 return -1; 727 } 728 return 0; 729} 730 731/* ------------------- */ 732static int setdirowner_ads1_loop(const struct vol *vol _U_, struct dirent *de _U_, char *name, void *data, int flag _U_) 733{ 734 struct perm *owner = data; 735 736 if ( chown( name, owner->uid, owner->gid ) < 0 && errno != EPERM ) { 737 LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s", 738 owner->uid, owner->gid, fullpathname(name), strerror(errno) ); 739 /* return ( -1 ); Sometimes this is okay */ 740 } 741 return 0; 742} 743 744static int setdirowner_ads_loop(const struct vol *vol, struct dirent *de _U_, char *name, void *data, int flag) 745{ 746 struct perm *owner = data; 747 748 if (for_each_adouble("setdirowner", name, setdirowner_ads1_loop, vol, data, flag) < 0) 749 return -1; 750 751 if ( chown( name, owner->uid, owner->gid ) < 0 && errno != EPERM ) { 752 LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s", 753 owner->uid, owner->gid, fullpathname(name), strerror(errno) ); 754 /* return ( -1 ); Sometimes this is okay */ 755 } 756 return 0; 757} 758 759static int RF_setdirowner_ads(VFS_FUNC_ARGS_SETDIROWNER) 760{ 761 int noadouble = vol_noadouble(vol); 762 char adouble_p[ MAXPATHLEN + 1]; 763 struct stat st; 764 struct perm owner; 765 766 owner.uid = uid; 767 owner.gid = gid; 768 769 strlcpy(adouble_p, ad_dir(vol->ad_path(name, ADFLAGS_DIR )), sizeof(adouble_p)); 770 771 if (for_each_adouble("setdirowner", ad_dir(adouble_p), setdirowner_ads_loop, vol, &owner, noadouble)) 772 return -1; 773 774 /* 775 * We cheat: we know that chown doesn't do anything. 776 */ 777 if ( stat( ".AppleDouble", &st ) < 0) { 778 if (errno == ENOENT && noadouble) 779 return 0; 780 LOG(log_error, logtype_afpd, "setdirowner: stat %s: %s", fullpathname(".AppleDouble"), strerror(errno) ); 781 return -1; 782 } 783 if ( gid && gid != st.st_gid && chown( ".AppleDouble", uid, gid ) < 0 && errno != EPERM ) { 784 LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s", 785 uid, gid,fullpathname(".AppleDouble"), strerror(errno) ); 786 /* return ( -1 ); Sometimes this is okay */ 787 } 788 return 0; 789} 790 791/* ------------------- */ 792static int RF_deletefile_ads(VFS_FUNC_ARGS_DELETEFILE) 793{ 794 int ret = 0; 795 int cwd = -1; 796 char *ad_p; 797 798 ad_p = ad_dir(vol->ad_path(file, ADFLAGS_HF )); 799 800 if (dirfd != -1) { 801 if (((cwd = open(".", O_RDONLY)) == -1) || (fchdir(dirfd) != 0)) { 802 ret = AFPERR_MISC; 803 goto exit; 804 } 805 } 806 807 ret = ads_delete_rf(ad_p); 808 809 if (dirfd != -1 && fchdir(cwd) != 0) { 810 LOG(log_error, logtype_afpd, "RF_deletefile_ads: cant chdir back. exit!"); 811 exit(EXITERR_SYS); 812 } 813 814exit: 815 if (cwd != -1) 816 close(cwd); 817 818 return ret; 819} 820 821/* --------------------------- */ 822static int RF_renamefile_ads(VFS_FUNC_ARGS_RENAMEFILE) 823{ 824 char adsrc[ MAXPATHLEN + 1]; 825 int err = 0; 826 827 strcpy( adsrc, ad_dir(vol->ad_path(src, 0 ))); 828 if (unix_rename(dirfd, adsrc, -1, ad_dir(vol->ad_path(dst, 0 ))) < 0) { 829 struct stat st; 830 831 err = errno; 832 if (errno == ENOENT) { 833 struct adouble ad; 834 835 if (ostatat(dirfd, adsrc, &st, vol_syml_opt(vol))) /* source has no ressource fork, */ 836 return 0; 837 838 /* We are here because : 839 * -there's no dest folder. 840 * -there's no .AppleDouble in the dest folder. 841 * if we use the struct adouble passed in parameter it will not 842 * create .AppleDouble if the file is already opened, so we 843 * use a diff one, it's not a pb,ie it's not the same file, yet. 844 */ 845 ad_init(&ad, vol->v_adouble, vol->v_ad_options); 846 if (!ad_open(dst, ADFLAGS_HF, O_RDWR | O_CREAT, 0666, &ad)) { 847 ad_close(&ad, ADFLAGS_HF); 848 849 /* We must delete it */ 850 RF_deletefile_ads(vol, -1, dst ); 851 if (!unix_rename(dirfd, adsrc, -1, ad_dir(vol->ad_path(dst, 0 ))) ) 852 err = 0; 853 else 854 err = errno; 855 } 856 else { /* it's something else, bail out */ 857 err = errno; 858 } 859 } 860 } 861 if (err) { 862 errno = err; 863 return -1; 864 } 865 return 0; 866} 867 868/************************************************************************* 869 * osx adouble format 870 ************************************************************************/ 871static int validupath_osx(VFS_FUNC_ARGS_VALIDUPATH) 872{ 873 return strncmp(name,"._", 2) && ( 874 (vol->v_flags & AFPVOL_USEDOTS) ? netatalk_name(name): name[0] != '.'); 875} 876 877/* ---------------- */ 878static int RF_renamedir_osx(VFS_FUNC_ARGS_RENAMEDIR) 879{ 880 /* We simply move the corresponding ad file as well */ 881 char tempbuf[258]="._"; 882 return unix_rename(dirfd, vol->ad_path(oldpath,0), -1, strcat(tempbuf,newpath)); 883} 884 885/* ---------------- */ 886static int RF_deletecurdir_osx(VFS_FUNC_ARGS_DELETECURDIR) 887{ 888 return netatalk_unlink( vol->ad_path(".",0) ); 889} 890 891/* ---------------- */ 892static int RF_setdirunixmode_osx(VFS_FUNC_ARGS_SETDIRUNIXMODE) 893{ 894 return adouble_setfilmode(vol, vol->ad_path(name, ADFLAGS_DIR), mode, st); 895} 896 897/* ---------------- */ 898static int RF_setdirmode_osx(VFS_FUNC_ARGS_SETDIRMODE) 899{ 900 return 0; 901} 902 903/* ---------------- */ 904static int RF_setdirowner_osx(VFS_FUNC_ARGS_SETDIROWNER) 905{ 906 return 0; 907} 908 909/* ---------------- */ 910static int RF_renamefile_osx(VFS_FUNC_ARGS_RENAMEFILE) 911{ 912 char adsrc[ MAXPATHLEN + 1]; 913 int err = 0; 914 915 strcpy( adsrc, vol->ad_path(src, 0 )); 916 917 if (unix_rename(dirfd, adsrc, -1, vol->ad_path(dst, 0 )) < 0) { 918 struct stat st; 919 920 err = errno; 921 if (errno == ENOENT && ostatat(dirfd, adsrc, &st, vol_syml_opt(vol))) /* source has no ressource fork, */ 922 return 0; 923 errno = err; 924 return -1; 925 } 926 return 0; 927} 928 929/******************************************************************************************** 930 * VFS chaining 931 ********************************************************************************************/ 932 933/* 934 * Up until we really start stacking many VFS modules on top of one another or use 935 * dynamic module loading like we do for UAMs, up until then we just stack VFS modules 936 * via an fixed size array. 937 * All VFS funcs must return AFP_ERR codes. When a func in the chain returns an error 938 * this error code will be returned to the caller, BUT the chain in followed and all 939 * following funcs are called in order to give them a chance. 940 */ 941 942/* 943 * Define most VFS funcs with macros as they all do the same. 944 * Only "ad_path" and "validupath" will NOT do stacking and only 945 * call the func from the first module. 946 */ 947 948#define VFS_MFUNC(name, args, vars) \ 949 static int vfs_ ## name(args) \ 950 { \ 951 int i = 0, ret = AFP_OK, err; \ 952 while (vol->vfs_modules[i]) { \ 953 if (vol->vfs_modules[i]->vfs_ ## name) { \ 954 err = vol->vfs_modules[i]->vfs_ ## name (vars); \ 955 if ((ret == AFP_OK) && (err != AFP_OK)) \ 956 ret = err; \ 957 } \ 958 i ++; \ 959 } \ 960 return ret; \ 961 } 962 963VFS_MFUNC(chown, VFS_FUNC_ARGS_CHOWN, VFS_FUNC_VARS_CHOWN) 964VFS_MFUNC(renamedir, VFS_FUNC_ARGS_RENAMEDIR, VFS_FUNC_VARS_RENAMEDIR) 965VFS_MFUNC(deletecurdir, VFS_FUNC_ARGS_DELETECURDIR, VFS_FUNC_VARS_DELETECURDIR) 966VFS_MFUNC(setfilmode, VFS_FUNC_ARGS_SETFILEMODE, VFS_FUNC_VARS_SETFILEMODE) 967VFS_MFUNC(setdirmode, VFS_FUNC_ARGS_SETDIRMODE, VFS_FUNC_VARS_SETDIRMODE) 968VFS_MFUNC(setdirunixmode, VFS_FUNC_ARGS_SETDIRUNIXMODE, VFS_FUNC_VARS_SETDIRUNIXMODE) 969VFS_MFUNC(setdirowner, VFS_FUNC_ARGS_SETDIROWNER, VFS_FUNC_VARS_SETDIROWNER) 970VFS_MFUNC(deletefile, VFS_FUNC_ARGS_DELETEFILE, VFS_FUNC_VARS_DELETEFILE) 971VFS_MFUNC(renamefile, VFS_FUNC_ARGS_RENAMEFILE, VFS_FUNC_VARS_RENAMEFILE) 972VFS_MFUNC(copyfile, VFS_FUNC_ARGS_COPYFILE, VFS_FUNC_VARS_COPYFILE) 973#ifdef HAVE_ACLS 974VFS_MFUNC(acl, VFS_FUNC_ARGS_ACL, VFS_FUNC_VARS_ACL) 975VFS_MFUNC(remove_acl, VFS_FUNC_ARGS_REMOVE_ACL, VFS_FUNC_VARS_REMOVE_ACL) 976#endif 977VFS_MFUNC(ea_getsize, VFS_FUNC_ARGS_EA_GETSIZE, VFS_FUNC_VARS_EA_GETSIZE) 978VFS_MFUNC(ea_getcontent, VFS_FUNC_ARGS_EA_GETCONTENT, VFS_FUNC_VARS_EA_GETCONTENT) 979VFS_MFUNC(ea_list, VFS_FUNC_ARGS_EA_LIST, VFS_FUNC_VARS_EA_LIST) 980VFS_MFUNC(ea_set, VFS_FUNC_ARGS_EA_SET, VFS_FUNC_VARS_EA_SET) 981VFS_MFUNC(ea_remove, VFS_FUNC_ARGS_EA_REMOVE, VFS_FUNC_VARS_EA_REMOVE) 982 983static int vfs_validupath(VFS_FUNC_ARGS_VALIDUPATH) 984{ 985 return vol->vfs_modules[0]->vfs_validupath(VFS_FUNC_VARS_VALIDUPATH); 986} 987 988/* 989 * These function pointers get called from the lib users via vol->vfs->func. 990 * These funcs are defined via the macros above. 991 */ 992static struct vfs_ops vfs_master_funcs = { 993 vfs_validupath, 994 vfs_chown, 995 vfs_renamedir, 996 vfs_deletecurdir, 997 vfs_setfilmode, 998 vfs_setdirmode, 999 vfs_setdirunixmode, 1000 vfs_setdirowner, 1001 vfs_deletefile, 1002 vfs_renamefile, 1003 vfs_copyfile, 1004#ifdef HAVE_ACLS 1005 vfs_acl, 1006 vfs_remove_acl, 1007#endif 1008 vfs_ea_getsize, 1009 vfs_ea_getcontent, 1010 vfs_ea_list, 1011 vfs_ea_set, 1012 vfs_ea_remove 1013}; 1014 1015/* 1016 * Primary adouble modules: default, osx, sfm 1017 */ 1018 1019static struct vfs_ops netatalk_adouble = { 1020 /* vfs_validupath: */ validupath_adouble, 1021 /* vfs_chown: */ RF_chown_adouble, 1022 /* vfs_renamedir: */ RF_renamedir_adouble, 1023 /* vfs_deletecurdir: */ RF_deletecurdir_adouble, 1024 /* vfs_setfilmode: */ RF_setfilmode_adouble, 1025 /* vfs_setdirmode: */ RF_setdirmode_adouble, 1026 /* vfs_setdirunixmode:*/ RF_setdirunixmode_adouble, 1027 /* vfs_setdirowner: */ RF_setdirowner_adouble, 1028 /* vfs_deletefile: */ RF_deletefile_adouble, 1029 /* vfs_renamefile: */ RF_renamefile_adouble, 1030 /* vfs_copyfile: */ RF_copyfile_adouble, 1031 NULL 1032}; 1033 1034static struct vfs_ops netatalk_adouble_osx = { 1035 /* vfs_validupath: */ validupath_osx, 1036 /* vfs_chown: */ RF_chown_adouble, 1037 /* vfs_renamedir: */ RF_renamedir_osx, 1038 /* vfs_deletecurdir: */ RF_deletecurdir_osx, 1039 /* vfs_setfilmode: */ RF_setfilmode_adouble, 1040 /* vfs_setdirmode: */ RF_setdirmode_osx, 1041 /* vfs_setdirunixmode:*/ RF_setdirunixmode_osx, 1042 /* vfs_setdirowner: */ RF_setdirowner_osx, 1043 /* vfs_deletefile: */ RF_deletefile_adouble, 1044 /* vfs_renamefile: */ RF_renamefile_osx, 1045 /* vfs_copyfile: */ NULL, 1046 NULL 1047}; 1048 1049/* samba sfm format. ad_path shouldn't be set her */ 1050static struct vfs_ops netatalk_adouble_sfm = { 1051 /* vfs_validupath: */ validupath_adouble, 1052 /* vfs_chown: */ RF_chown_ads, 1053 /* vfs_renamedir: */ RF_renamedir_adouble, 1054 /* vfs_deletecurdir: */ RF_deletecurdir_ads, 1055 /* vfs_setfilmode: */ RF_setfilmode_ads, 1056 /* vfs_setdirmode: */ RF_setdirmode_ads, 1057 /* vfs_setdirunixmode:*/ RF_setdirunixmode_ads, 1058 /* vfs_setdirowner: */ RF_setdirowner_ads, 1059 /* vfs_deletefile: */ RF_deletefile_ads, 1060 /* vfs_renamefile: */ RF_renamefile_ads, 1061 /* vfs_copyfile: */ NULL, 1062 NULL 1063}; 1064 1065/* 1066 * Secondary vfs modules for Extended Attributes 1067 */ 1068 1069static struct vfs_ops netatalk_ea_adouble = { 1070 /* vfs_validupath: */ NULL, 1071 /* vfs_chown: */ ea_chown, 1072 /* vfs_renamedir: */ NULL, /* ok */ 1073 /* vfs_deletecurdir: */ NULL, /* ok */ 1074 /* vfs_setfilmode: */ ea_chmod_file, 1075 /* vfs_setdirmode: */ NULL, /* ok */ 1076 /* vfs_setdirunixmode:*/ ea_chmod_dir, 1077 /* vfs_setdirowner: */ NULL, /* ok */ 1078 /* vfs_deletefile: */ ea_deletefile, 1079 /* vfs_renamefile: */ ea_renamefile, 1080 /* vfs_copyfile */ ea_copyfile, 1081#ifdef HAVE_ACLS 1082 /* vfs_acl: */ NULL, 1083 /* vfs_remove_acl */ NULL, 1084#endif 1085 /* vfs_getsize */ get_easize, 1086 /* vfs_getcontent */ get_eacontent, 1087 /* vfs_list */ list_eas, 1088 /* vfs_set */ set_ea, 1089 /* vfs_remove */ remove_ea 1090}; 1091 1092static struct vfs_ops netatalk_ea_sys = { 1093 /* validupath: */ NULL, 1094 /* rf_chown: */ NULL, 1095 /* rf_renamedir: */ NULL, 1096 /* rf_deletecurdir: */ NULL, 1097 /* rf_setfilmode: */ NULL, 1098 /* rf_setdirmode: */ NULL, 1099 /* rf_setdirunixmode: */ NULL, 1100 /* rf_setdirowner: */ NULL, 1101 /* rf_deletefile: */ NULL, 1102 /* rf_renamefile: */ NULL, 1103 /* vfs_copyfile: */ sys_ea_copyfile, 1104#ifdef HAVE_ACLS 1105 /* rf_acl: */ NULL, 1106 /* rf_remove_acl */ NULL, 1107#endif 1108 /* ea_getsize */ sys_get_easize, 1109 /* ea_getcontent */ sys_get_eacontent, 1110 /* ea_list */ sys_list_eas, 1111 /* ea_set */ sys_set_ea, 1112 /* ea_remove */ sys_remove_ea 1113}; 1114 1115/* 1116 * Tertiary VFS modules for ACLs 1117 */ 1118 1119#ifdef HAVE_SOLARIS_ACLS 1120static struct vfs_ops netatalk_solaris_acl_adouble = { 1121 /* validupath: */ NULL, 1122 /* rf_chown: */ NULL, 1123 /* rf_renamedir: */ NULL, 1124 /* rf_deletecurdir: */ NULL, 1125 /* rf_setfilmode: */ NULL, 1126 /* rf_setdirmode: */ NULL, 1127 /* rf_setdirunixmode: */ NULL, 1128 /* rf_setdirowner: */ NULL, 1129 /* rf_deletefile: */ NULL, 1130 /* rf_renamefile: */ NULL, 1131 /* vfs_copyfile */ NULL, 1132 /* rf_acl: */ RF_solaris_acl, 1133 /* rf_remove_acl */ RF_solaris_remove_acl, 1134 NULL 1135}; 1136#endif 1137 1138#ifdef HAVE_POSIX_ACLS 1139static struct vfs_ops netatalk_posix_acl_adouble = { 1140 /* validupath: */ NULL, 1141 /* rf_chown: */ NULL, 1142 /* rf_renamedir: */ NULL, 1143 /* rf_deletecurdir: */ NULL, 1144 /* rf_setfilmode: */ NULL, 1145 /* rf_setdirmode: */ NULL, 1146 /* rf_setdirunixmode: */ NULL, 1147 /* rf_setdirowner: */ NULL, 1148 /* rf_deletefile: */ NULL, 1149 /* rf_renamefile: */ NULL, 1150 /* vfs_copyfile */ NULL, 1151 /* rf_acl: */ RF_posix_acl, 1152 /* rf_remove_acl */ RF_posix_remove_acl, 1153 NULL 1154}; 1155#endif 1156 1157/* ---------------- */ 1158void initvol_vfs(struct vol *vol) 1159{ 1160 vol->vfs = &vfs_master_funcs; 1161 1162 /* Default adouble stuff */ 1163 if (vol->v_adouble == AD_VERSION2_OSX) { 1164 vol->vfs_modules[0] = &netatalk_adouble_osx; 1165 vol->ad_path = ad_path_osx; 1166 } 1167 else if (vol->v_adouble == AD_VERSION1_SFM) { 1168 vol->vfs_modules[0] = &netatalk_adouble_sfm; 1169 vol->ad_path = ad_path_sfm; 1170 } 1171 else { 1172 vol->vfs_modules[0] = &netatalk_adouble; 1173 vol->ad_path = ad_path; 1174 } 1175 1176 /* Extended Attributes */ 1177 if (vol->v_vfs_ea == AFPVOL_EA_SYS) { 1178 LOG(log_debug, logtype_afpd, "initvol_vfs: enabling EA support with native EAs"); 1179 vol->vfs_modules[1] = &netatalk_ea_sys; 1180 } else if (vol->v_vfs_ea == AFPVOL_EA_AD) { 1181 LOG(log_debug, logtype_afpd, "initvol_vfs: enabling EA support with adouble files"); 1182 vol->vfs_modules[1] = &netatalk_ea_adouble; 1183 } else { 1184 LOG(log_debug, logtype_afpd, "initvol_vfs: volume without EA support"); 1185 } 1186 1187 /* ACLs */ 1188#ifdef HAVE_SOLARIS_ACLS 1189 vol->vfs_modules[2] = &netatalk_solaris_acl_adouble; 1190#endif 1191#ifdef HAVE_POSIX_ACLS 1192 vol->vfs_modules[2] = &netatalk_posix_acl_adouble; 1193#endif 1194 1195} 1196