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