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