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, &param, 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, &param, 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