• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-WNDR4500v2-V1.0.0.60_1.0.38/ap/gpl/timemachine/netatalk-2.2.5/libatalk/vfs/
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, &param, 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, &param, 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