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