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