2 Copyright (c) 2004 Didier Gautheron
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.
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.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #endif /* HAVE_CONFIG_H */
29 #include <atalk/adouble.h>
30 #include <atalk/logger.h>
31 #include <atalk/util.h>
33 #include "directory.h"
42 typedef int (*rf_loop)(struct dirent *, char *, void *, int );
44 /* ----------------------------- */
46 for_each_adouble(const char *from, const char *name, rf_loop fn, void *data, int flag)
48 char buf[ MAXPATHLEN + 1];
55 if (NULL == ( dp = opendir( name)) ) {
57 LOG(log_error, logtype_afpd, "%s: opendir %s: %s", from, fullpathname(name),strerror(errno) );
62 strlcpy( buf, name, sizeof(buf));
63 strlcat( buf, "/", sizeof(buf) );
64 m = strchr( buf, '\0' );
66 while ((de = readdir(dp))) {
67 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) {
71 strlcat(buf, de->d_name, sizeof(buf));
72 if (fn && (ret = fn(de, buf, data, flag))) {
82 /* ------------------------------ */
83 static int ads_chown_loop(struct dirent *de _U_, char *name, void *data, int flag _U_)
85 struct perm *owner = data;
87 if (chown( name , owner->uid, owner->gid ) < 0) {
93 static int RF_chown_ads(const struct vol *vol, const char *path, uid_t uid, gid_t gid)
104 ad_p = ad_dir(vol->vfs->ad_path(path, ADFLAGS_HF ));
106 if ( stat( ad_p, &st ) < 0 ) {
111 if (chown( ad_p, uid, gid ) < 0) {
114 return for_each_adouble("chown_ads", ad_p, ads_chown_loop, &owner, 1);
117 /* --------------------------------- */
118 static int deletecurdir_ads1_loop(struct dirent *de _U_, char *name, void *data _U_, int flag _U_)
120 return netatalk_unlink(name);
123 static int ads_delete_rf(char *name)
127 if ((err = for_each_adouble("deletecurdir", name, deletecurdir_ads1_loop, NULL, 1)))
130 * it's a problem for a nfs mounted folder, there's .nfsxxx around
131 * for linux the following line solve it.
132 * but it could fail if rm .nfsxxx create a new .nfsyyy :(
134 if ((err = for_each_adouble("deletecurdir", name, deletecurdir_ads1_loop, NULL, 1)))
136 return netatalk_rmdir(name);
139 static int deletecurdir_ads_loop(struct dirent *de, char *name, void *data _U_, int flag _U_)
143 /* bail if the file exists in the current directory.
144 * note: this will not fail with dangling symlinks */
146 if (stat(de->d_name, &st) == 0) {
147 return AFPERR_DIRNEMPT;
149 return ads_delete_rf(name);
152 static int RF_deletecurdir_ads(const struct vol *vol _U_)
156 /* delete stray .AppleDouble files. this happens to get .Parent files as well. */
157 if ((err = for_each_adouble("deletecurdir", ".AppleDouble", deletecurdir_ads_loop, NULL, 1)))
159 return netatalk_rmdir( ".AppleDouble" );
162 /* ------------------- */
168 static int ads_setfilmode_loop(struct dirent *de _U_, char *name, void *data, int flag _U_)
170 struct set_mode *param = data;
172 return setfilmode(name, param->mode, param->st);
175 static int ads_setfilmode(const char * name, mode_t mode, struct stat *st)
177 mode_t dir_mode = mode;
178 mode_t file_mode = ad_hf_mode(mode);
179 struct set_mode param;
181 if ((dir_mode & (S_IRUSR | S_IWUSR )))
183 if ((dir_mode & (S_IRGRP | S_IWGRP )))
185 if ((dir_mode & (S_IROTH | S_IWOTH )))
190 if (dir_rx_set(dir_mode)) {
191 if (chmod( name, dir_mode ) < 0)
195 param.mode = file_mode;
196 if (for_each_adouble("setfilmode_ads", name, ads_setfilmode_loop, ¶m, 0) < 0)
199 if (!dir_rx_set(dir_mode)) {
200 if (chmod( name, dir_mode ) < 0)
207 static int RF_setfilmode_ads(const struct vol *vol, const char * name, mode_t mode, struct stat *st)
209 return ads_setfilmode(ad_dir(vol->vfs->ad_path( name, ADFLAGS_HF )), mode, st);
212 /* ------------------- */
213 static int RF_setdirunixmode_ads(const struct vol *vol, const char * name, mode_t mode, struct stat *st)
215 char *adouble = vol->vfs->ad_path( name, ADFLAGS_DIR );
216 char ad_p[ MAXPATHLEN + 1];
217 int dropbox = (vol->v_flags & AFPVOL_DROPBOX);
219 strlcpy(ad_p,ad_dir(adouble), MAXPATHLEN + 1);
221 if (dir_rx_set(mode)) {
224 if (stickydirmode(ad_dir(ad_p), DIRBITS | mode, dropbox) < 0 && !vol_noadouble(vol))
227 /* .AppleDouble/.Parent */
228 if (stickydirmode(ad_p, DIRBITS | mode, dropbox) < 0 && !vol_noadouble(vol))
232 if (ads_setfilmode(ad_dir(vol->vfs->ad_path( name, ADFLAGS_DIR)), mode, st) < 0)
235 if (!dir_rx_set(mode)) {
236 if (stickydirmode(ad_p, DIRBITS | mode, dropbox) < 0 && !vol_noadouble(vol))
238 if (stickydirmode(ad_dir(ad_p), DIRBITS | mode, dropbox) < 0 && !vol_noadouble(vol))
244 /* ------------------- */
250 static int setdirmode_ads_loop(struct dirent *de _U_, char *name, void *data, int flag)
253 struct dir_mode *param = data;
254 int ret = 0; /* 0 ignore error, -1 */
256 if (dir_rx_set(param->mode)) {
257 if (stickydirmode(name, DIRBITS | param->mode, param->dropbox) < 0) {
264 if (ads_setfilmode(name, param->mode, NULL) < 0)
267 if (!dir_rx_set(param->mode)) {
268 if (stickydirmode(name, DIRBITS | param->mode, param->dropbox) < 0) {
278 static int RF_setdirmode_ads(const struct vol *vol, const char * name, mode_t mode, struct stat *st _U_)
280 char *adouble = vol->vfs->ad_path( name, ADFLAGS_DIR );
281 char ad_p[ MAXPATHLEN + 1];
282 struct dir_mode param;
285 param.dropbox = (vol->v_flags & AFPVOL_DROPBOX);
287 strlcpy(ad_p,ad_dir(adouble), sizeof(ad_p));
289 if (dir_rx_set(mode)) {
291 if (stickydirmode(ad_dir(ad_p), DIRBITS | mode, param.dropbox) < 0 && !vol_noadouble(vol))
295 if (for_each_adouble("setdirmode_ads", ad_dir(ad_p), setdirmode_ads_loop, ¶m, vol_noadouble(vol)))
298 if (!dir_rx_set(mode)) {
299 if (stickydirmode(ad_dir(ad_p), DIRBITS | mode, param.dropbox) < 0 && !vol_noadouble(vol))
305 /* ------------------- */
306 static int setdirowner_ads1_loop(struct dirent *de _U_, char *name, void *data, int flag _U_)
308 struct perm *owner = data;
310 if ( chown( name, owner->uid, owner->gid ) < 0 && errno != EPERM ) {
311 LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s",
312 owner->uid, owner->gid, fullpathname(name), strerror(errno) );
313 /* return ( -1 ); Sometimes this is okay */
318 static int setdirowner_ads_loop(struct dirent *de _U_, char *name, void *data, int flag)
320 struct perm *owner = data;
322 if (for_each_adouble("setdirowner", name, setdirowner_ads1_loop, data, flag) < 0)
325 if ( chown( name, owner->uid, owner->gid ) < 0 && errno != EPERM ) {
326 LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s",
327 owner->uid, owner->gid, fullpathname(name), strerror(errno) );
328 /* return ( -1 ); Sometimes this is okay */
333 static int RF_setdirowner_ads(const struct vol *vol, const char *name, uid_t uid, gid_t gid)
335 int noadouble = vol_noadouble(vol);
336 char adouble_p[ MAXPATHLEN + 1];
343 strlcpy(adouble_p, ad_dir(vol->vfs->ad_path( name, ADFLAGS_DIR )), sizeof(adouble_p));
345 if (for_each_adouble("setdirowner", ad_dir(adouble_p), setdirowner_ads_loop, &owner, noadouble))
349 * We cheat: we know that chown doesn't do anything.
351 if ( stat( ".AppleDouble", &st ) < 0) {
352 if (errno == ENOENT && noadouble)
354 LOG(log_error, logtype_afpd, "setdirowner: stat %s: %s", fullpathname(".AppleDouble"), strerror(errno) );
357 if ( gid && gid != st.st_gid && chown( ".AppleDouble", uid, gid ) < 0 && errno != EPERM ) {
358 LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s",
359 uid, gid,fullpathname(".AppleDouble"), strerror(errno) );
360 /* return ( -1 ); Sometimes this is okay */
365 /* ------------------- */
366 static int RF_deletefile_ads(const struct vol *vol, const char *file )
368 char *ad_p = ad_dir(vol->vfs->ad_path(file, ADFLAGS_HF ));
370 return ads_delete_rf(ad_p);
373 /* --------------------------- */
374 int RF_renamefile_ads(const struct vol *vol, const char *src, const char *dst)
376 char adsrc[ MAXPATHLEN + 1];
379 strcpy( adsrc, ad_dir(vol->vfs->ad_path( src, 0 )));
380 if (unix_rename( adsrc, ad_dir(vol->vfs->ad_path( dst, 0 ))) < 0) {
384 if (errno == ENOENT) {
387 if (stat(adsrc, &st)) /* source has no ressource fork, */
390 /* We are here because :
391 * -there's no dest folder.
392 * -there's no .AppleDouble in the dest folder.
393 * if we use the struct adouble passed in parameter it will not
394 * create .AppleDouble if the file is already opened, so we
395 * use a diff one, it's not a pb,ie it's not the same file, yet.
397 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
398 if (!ad_open(dst, ADFLAGS_HF, O_RDWR | O_CREAT, 0666, &ad)) {
399 ad_close(&ad, ADFLAGS_HF);
401 /* We must delete it */
402 RF_deletefile_ads(vol, dst );
403 if (!unix_rename( adsrc, ad_dir(vol->vfs->ad_path( dst, 0 ))) )
408 else { /* it's something else, bail out */
420 /* ===================================================
421 classic adouble format
424 static int validupath_adouble(const struct vol *vol, const char *name)
426 return (vol->v_flags & AFPVOL_USEDOTS) ? strncasecmp(name,".Apple", 6) && strcasecmp(name, ".Parent")
430 /* ----------------- */
431 static int RF_chown_adouble(const struct vol *vol, const char *path, uid_t uid, gid_t gid)
437 ad_p = vol->vfs->ad_path(path, ADFLAGS_HF );
439 if ( stat( ad_p, &st ) < 0 )
440 return 0; /* ignore */
442 return chown( ad_p, uid, gid );
445 /* ----------------- */
446 int RF_renamedir_adouble(const struct vol *vol _U_, const char *oldpath _U_, const char *newpath _U_)
451 /* ----------------- */
452 static int deletecurdir_adouble_loop(struct dirent *de, char *name, void *data _U_, int flag _U_)
457 /* bail if the file exists in the current directory.
458 * note: this will not fail with dangling symlinks */
460 if (stat(de->d_name, &st) == 0)
461 return AFPERR_DIRNEMPT;
463 if ((err = netatalk_unlink(name)))
469 static int RF_deletecurdir_adouble(const struct vol *vol _U_)
473 /* delete stray .AppleDouble files. this happens to get .Parent files
475 if ((err = for_each_adouble("deletecurdir", ".AppleDouble", deletecurdir_adouble_loop, NULL, 1)))
477 return netatalk_rmdir( ".AppleDouble" );
480 /* ----------------- */
481 static int adouble_setfilmode(const char * name, mode_t mode, struct stat *st)
483 return setfilmode(name, ad_hf_mode(mode), st);
486 static int RF_setfilmode_adouble(const struct vol *vol, const char * name, mode_t mode, struct stat *st)
488 return adouble_setfilmode(vol->vfs->ad_path( name, ADFLAGS_HF ), mode, st);
491 /* ----------------- */
492 static int RF_setdirunixmode_adouble(const struct vol *vol, const char * name, mode_t mode, struct stat *st)
494 char *adouble = vol->vfs->ad_path( name, ADFLAGS_DIR );
495 int dropbox = (vol->v_flags & AFPVOL_DROPBOX);
497 if (dir_rx_set(mode)) {
498 if (stickydirmode(ad_dir(adouble), DIRBITS | mode, dropbox) < 0 && !vol_noadouble(vol))
502 if (adouble_setfilmode(vol->vfs->ad_path( name, ADFLAGS_DIR ), mode, st) < 0)
505 if (!dir_rx_set(mode)) {
506 if (stickydirmode(ad_dir(adouble), DIRBITS | mode, dropbox) < 0 && !vol_noadouble(vol))
512 /* ----------------- */
513 static int setdirmode_adouble_loop(struct dirent *de _U_, char *name, void *data, int flag)
515 mode_t hf_mode = *(mode_t *)data;
518 if ( stat( name, &st ) < 0 ) {
521 LOG(log_error, logtype_afpd, "setdirmode: stat %s: %s", name, strerror(errno) );
523 else if (!S_ISDIR(st.st_mode)) {
524 if (setfilmode(name, hf_mode , &st) < 0) {
525 /* FIXME what do we do then? */
531 static int RF_setdirmode_adouble(const struct vol *vol, const char * name, mode_t mode, struct stat *st _U_)
533 int dropbox = (vol->v_flags & AFPVOL_DROPBOX);
534 mode_t hf_mode = ad_hf_mode(mode);
535 char *adouble = vol->vfs->ad_path( name, ADFLAGS_DIR );
536 char *adouble_p = ad_dir(adouble);
538 if (dir_rx_set(mode)) {
539 if (stickydirmode(ad_dir(adouble), DIRBITS | mode, dropbox) < 0 && !vol_noadouble(vol))
543 if (for_each_adouble("setdirmode", adouble_p, setdirmode_adouble_loop, &hf_mode, vol_noadouble(vol)))
546 if (!dir_rx_set(mode)) {
547 if (stickydirmode(ad_dir(adouble), DIRBITS | mode, dropbox) < 0 && !vol_noadouble(vol))
553 /* ----------------- */
554 static int setdirowner_adouble_loop(struct dirent *de _U_, char *name, void *data, int flag _U_)
556 struct perm *owner = data;
558 if ( chown( name, owner->uid, owner->gid ) < 0 && errno != EPERM ) {
559 LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s",
560 owner->uid, owner->gid, fullpathname(name), strerror(errno) );
561 /* return ( -1 ); Sometimes this is okay */
566 static int RF_setdirowner_adouble(const struct vol *vol, const char *name, uid_t uid, gid_t gid)
569 int noadouble = vol_noadouble(vol);
577 adouble_p = ad_dir(vol->vfs->ad_path( name, ADFLAGS_DIR ));
579 if (for_each_adouble("setdirowner", adouble_p, setdirowner_adouble_loop, &owner, noadouble))
583 * We cheat: we know that chown doesn't do anything.
585 if ( stat( ".AppleDouble", &st ) < 0) {
586 if (errno == ENOENT && noadouble)
588 LOG(log_error, logtype_afpd, "setdirowner: stat %s: %s", fullpathname(".AppleDouble"), strerror(errno) );
591 if ( gid && gid != st.st_gid && chown( ".AppleDouble", uid, gid ) < 0 && errno != EPERM ) {
592 LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s",
593 uid, gid,fullpathname(".AppleDouble"), strerror(errno) );
594 /* return ( -1 ); Sometimes this is okay */
599 /* ----------------- */
600 static int RF_deletefile_adouble(const struct vol *vol, const char *file )
602 return netatalk_unlink(vol->vfs->ad_path( file, ADFLAGS_HF));
605 /* ----------------- */
606 int RF_renamefile_adouble(const struct vol *vol, const char *src, const char *dst)
608 char adsrc[ MAXPATHLEN + 1];
611 strcpy( adsrc, vol->vfs->ad_path( src, 0 ));
612 if (unix_rename( adsrc, vol->vfs->ad_path( dst, 0 )) < 0) {
616 if (errno == ENOENT) {
619 if (stat(adsrc, &st)) /* source has no ressource fork, */
622 /* We are here because :
623 * -there's no dest folder.
624 * -there's no .AppleDouble in the dest folder.
625 * if we use the struct adouble passed in parameter it will not
626 * create .AppleDouble if the file is already opened, so we
627 * use a diff one, it's not a pb,ie it's not the same file, yet.
629 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
630 if (!ad_open(dst, ADFLAGS_HF, O_RDWR | O_CREAT, 0666, &ad)) {
631 ad_close(&ad, ADFLAGS_HF);
632 if (!unix_rename( adsrc, vol->vfs->ad_path( dst, 0 )) )
637 else { /* it's something else, bail out */
649 struct vfs_ops netatalk_adouble = {
650 /* ad_path: */ ad_path,
651 /* validupath: */ validupath_adouble,
652 /* rf_chown: */ RF_chown_adouble,
653 /* rf_renamedir: */ RF_renamedir_adouble,
654 /* rf_deletecurdir: */ RF_deletecurdir_adouble,
655 /* rf_setfilmode: */ RF_setfilmode_adouble,
656 /* rf_setdirmode: */ RF_setdirmode_adouble,
657 /* rf_setdirunixmode: */ RF_setdirunixmode_adouble,
658 /* rf_setdirowner: */ RF_setdirowner_adouble,
659 /* rf_deletefile: */ RF_deletefile_adouble,
660 /* rf_renamefile: */ RF_renamefile_adouble,
663 /* =======================================
666 static int validupath_osx(const struct vol *vol _U_, const char *name)
668 return strncasecmp(name,".Apple", 6) && strncasecmp(name,"._", 2);
671 /* ---------------- */
672 int RF_renamedir_osx(const struct vol *vol, const char *oldpath, const char *newpath)
674 /* We simply move the corresponding ad file as well */
675 char tempbuf[258]="._";
676 return rename(vol->vfs->ad_path(oldpath,0),strcat(tempbuf,newpath));
679 /* ---------------- */
680 int RF_deletecurdir_osx(const struct vol *vol)
682 return netatalk_unlink( vol->vfs->ad_path(".",0) );
685 /* ---------------- */
686 static int RF_setdirunixmode_osx(const struct vol *vol, const char * name, mode_t mode, struct stat *st)
688 return adouble_setfilmode(vol->vfs->ad_path( name, ADFLAGS_DIR ), mode, st);
691 /* ---------------- */
693 RF_setdirmode_osx(const struct vol *vol _U_, const char *name _U_, mode_t mode _U_, struct stat *st _U_)
698 /* ---------------- */
700 RF_setdirowner_osx(const struct vol *vol _U_, const char *path _U_, uid_t uid _U_, gid_t gid _U_)
705 /* ---------------- */
706 int RF_renamefile_osx(const struct vol *vol, const char *src, const char *dst)
708 char adsrc[ MAXPATHLEN + 1];
710 strcpy( adsrc, vol->vfs->ad_path( src, 0 ));
711 return unix_rename( adsrc, vol->vfs->ad_path( dst, 0 ));
714 struct vfs_ops netatalk_adouble_osx = {
715 /* ad_path: */ ad_path_osx,
716 /* validupath: */ validupath_osx,
717 /* rf_chown: */ RF_chown_adouble,
718 /* rf_renamedir: */ RF_renamedir_osx,
719 /* rf_deletecurdir: */ RF_deletecurdir_osx,
720 /* rf_setfilmode: */ RF_setfilmode_adouble,
721 /* rf_setdirmode: */ RF_setdirmode_osx,
722 /* rf_setdirunixmode:*/ RF_setdirunixmode_osx,
723 /* rf_setdirowner: */ RF_setdirowner_osx,
724 /* rf_deletefile: */ RF_deletefile_adouble,
725 /* rf_renamefile: */ RF_renamefile_osx,
728 /* =======================================
731 struct vfs_ops netatalk_adouble_ads = {
732 /* ad_path: */ ad_path_ads,
733 /* validupath: */ validupath_adouble,
734 /* rf_chown: */ RF_chown_ads,
735 /* rf_renamedir: */ RF_renamedir_adouble,
736 /* rf_deletecurdir: */ RF_deletecurdir_ads,
737 /* rf_setfilmode: */ RF_setfilmode_ads,
738 /* rf_setdirmode: */ RF_setdirmode_ads,
739 /* rf_setdirunixmode:*/ RF_setdirunixmode_ads,
740 /* rf_setdirowner: */ RF_setdirowner_ads,
741 /* rf_deletefile: */ RF_deletefile_ads,
742 /* rf_renamefile: */ RF_renamefile_ads,
745 /* ---------------- */
746 void initvol_vfs(struct vol *vol)
748 if (vol->v_adouble == AD_VERSION2_OSX) {
749 vol->vfs = &netatalk_adouble_osx;
751 else if (vol->v_adouble == AD_VERSION1_ADS) {
752 vol->vfs = &netatalk_adouble_ads;
755 vol->vfs = &netatalk_adouble;