First try for a netatalk vfs layer

current schemes
adouble=v1,v2 classic adouble format
adouble=osx  ._<filename> OSX resource fork.
adouble=ads  NT like alternate data stream. 

Note for ads:
* cf. patch.vfs for samba ADS vfs layer and patch.samba.xx for samba tree patch.

* It's using Afp_AfpInfo name (MS SFM name) but it's not yet compatible with SFM.
  from cdrecord source code Afp_AfpInfo is the raw HFS data, we are storing an appledouble file.

* Server side copy and Macintosh copy only deal with resource fork, other NT ADS are lost.
  unfixable for Macintosh copy but doable for server side.

* It's ok for rename, delete, chown and chmod.

* Copy from a NT box should work.

* Last but not least : only on a new volume!

TODO
indirection for metadata, aka stored in EA, a different file, whatever.

diff -Nur -X .cvsignore -x CVS ../src.dev2/etc/afpd/Makefile.am ./etc/afpd/Makefile.am
--- ../src.dev2/etc/afpd/Makefile.am	Mon Feb  9 22:45:51 2004
+++ ./etc/afpd/Makefile.am	Fri Jun 18 19:15:47 2004
@@ -8,14 +8,14 @@
 	 file.c enumerate.c desktop.c filedir.c fork.c appl.c gettok.c \
 	 mangle.c status.c afp_options.c afp_asp.c afp_dsi.c messages.c  \
 	 afp_config.c nfsquota.c quota.c uam.c afs.c uid.c afp_util.c \
-         catsearch.c afprun.c
+         catsearch.c afprun.c vfs_adouble.c
 
 afpd_LDADD =  $(top_builddir)/libatalk/cnid/libcnid.la $(top_builddir)/libatalk/libatalk.la
 afpd_LDFLAGS = -export-dynamic
 
 noinst_HEADERS = auth.h afp_config.h desktop.h directory.h file.h \
 	 filedir.h fork.h globals.h icon.h mangle.h misc.h status.h switch.h \
-	 uam_auth.h uid.h unix.h volume.h
+	 uam_auth.h uid.h unix.h volume.h afp_vfs.h
 
 LIBS = @LIBS@ @PAM_LIBS@ @QUOTA_LIBS@ @SLP_LIBS@
 
diff -Nur -X .cvsignore -x CVS ../src.dev2/etc/afpd/afp_vfs.h ./etc/afpd/afp_vfs.h
--- ../src.dev2/etc/afpd/afp_vfs.h	Thu Jan  1 00:00:00 1970
+++ ./etc/afpd/afp_vfs.h	Wed Jun 23 03:56:15 2004
@@ -0,0 +1,49 @@
+/*
+   Copyright (c) 2004 Didier Gautheron
+ 
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+ 
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+ 
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+   vfs layer for afp
+*/
+
+#ifndef _AFP_VFS_H
+#define _AFP_VFS_H
+
+#include <atalk/adouble.h>
+struct vol;
+
+struct vfs_ops {
+    /* low level adouble fn */
+    char *(*ad_path)(const char *, int);
+
+    /* */
+    int (*validupath)(const struct vol *, const char *);
+    int (*rf_chown)(const struct vol *, const char *path, uid_t owner, gid_t group);
+    int (*rf_renamedir)(const struct vol *, const char *oldpath, const char *newpath);
+    int (*rf_deletecurdir)(const struct vol *);
+    int (*rf_setfilmode)(const struct vol *, const char * name, mode_t mode, struct stat *st);
+    int (*rf_setdirmode)(const struct vol *, const char * name, mode_t mode, struct stat *st);
+    int (*rf_setdirunixmode)(const struct vol *, const char * name, mode_t mode, struct stat *st);
+
+    int (*rf_setdirowner)(const struct vol *, const char *path, uid_t owner, gid_t group);
+
+    int (*rf_deletefile)(const struct vol *, const char * );
+    int (*rf_renamefile)(const struct vol *, const char *oldpath, const char *newpath);
+
+};
+
+void initvol_vfs(struct vol *vol);
+
+#endif
diff -Nur -X .cvsignore -x CVS ../src.dev2/etc/afpd/directory.c ./etc/afpd/directory.c
--- ../src.dev2/etc/afpd/directory.c	Mon Jul 12 08:46:03 2004
+++ ./etc/afpd/directory.c	Sat Jun 19 14:07:14 2004
@@ -610,7 +610,7 @@
    system rmdir with afp error code.
    ENOENT is not an error.
  */
-static int netatalk_rmdir(const char *name)
+int netatalk_rmdir(const char *name)
 {
     if (rmdir(name) < 0) {
         switch ( errno ) {
@@ -2075,15 +2075,11 @@
         }
     }
 
-    if (vol->v_adouble == AD_VERSION2_OSX) {
-        /* We simply move the corresponding ad file as well */
-        char   tempbuf[258]="._";
-        rename(vol->ad_path(src,0),strcat(tempbuf,dst));
-    }
+    vol->vfs->rf_renamedir(vol, src, dst);
 
     len = strlen( newname );
     /* rename() succeeded so we need to update our tree even if we can't open
-     * .Parent
+     * metadata
     */
     
     ad_init(&ad, vol->v_adouble);
@@ -2132,12 +2128,9 @@
     return( AFP_OK );
 }
 
-#define DOT_APPLEDOUBLE_LEN 13
 /* delete an empty directory */
-int deletecurdir( vol, path, pathlen )
+int deletecurdir( vol)
 const struct vol	*vol;
-char *path;
-int pathlen;
 {
     struct dirent *de;
     struct stat st;
@@ -2162,42 +2155,9 @@
             return  AFPERR_OLOCK;
         }
     }
-
-    if (vol->v_adouble == AD_VERSION2_OSX) {
-       
-        if ((err = netatalk_unlink(vol->ad_path(".",0) )) ) {
-            return err;
-        }
-    }
-    else {
-        /* delete stray .AppleDouble files. this happens to get .Parent files
-           as well. */
-        if ((dp = opendir(".AppleDouble"))) {
-            strcpy(path, ".AppleDouble/");
-            while ((de = readdir(dp))) {
-                /* skip this and previous directory */
-                if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
-                    continue;
-
-                /* bail if the file exists in the current directory.
-                 * note: this will not fail with dangling symlinks */
-                if (stat(de->d_name, &st) == 0) {
-                    closedir(dp);
-                    return AFPERR_DIRNEMPT;
-                }
-
-                strcpy(path + DOT_APPLEDOUBLE_LEN, de->d_name);
-                if ((err = netatalk_unlink(path))) {
-                    closedir(dp);
-                    return err;
-                }
-            }
-            closedir(dp);
-        }
-
-        if ( (err = netatalk_rmdir( ".AppleDouble" ))  ) {
-            return err;
-        }
+    err = vol->vfs->rf_deletecurdir(vol);
+    if (err) {
+        return err;
     }
 
     /* now get rid of dangling symlinks */
diff -Nur -X .cvsignore -x CVS ../src.dev2/etc/afpd/directory.h ./etc/afpd/directory.h
--- ../src.dev2/etc/afpd/directory.h	Mon May 10 18:40:32 2004
+++ ./etc/afpd/directory.h	Sat Jun 19 03:23:18 2004
@@ -196,7 +196,7 @@
 
 extern struct dir       *dirinsert __P((struct vol *, struct dir *));
 extern int              movecwd __P((const struct vol *, struct dir *));
-extern int              deletecurdir __P((const struct vol *, char *, int));
+extern int              deletecurdir __P((const struct vol *));
 extern struct path      *cname __P((const struct vol *, struct dir *,
                              char **));
 extern mode_t           mtoumode __P((struct maccess *));
@@ -215,6 +215,7 @@
 extern int  check_access __P((char *name , int mode));
 extern int file_access   __P((struct path *path, int mode));
 
+extern int netatalk_rmdir __P((const char *name));
 extern int netatalk_unlink __P((const char *name));
 
 /* from enumerate.c */
diff -Nur -X .cvsignore -x CVS ../src.dev2/etc/afpd/enumerate.c ./etc/afpd/enumerate.c
--- ../src.dev2/etc/afpd/enumerate.c	Mon Jul 12 08:46:03 2004
+++ ./etc/afpd/enumerate.c	Thu Jun 24 04:26:35 2004
@@ -166,7 +166,7 @@
     if (!strcmp(name, "..") || !strcmp(name, "."))
         return NULL;
 
-    if (!vol->validupath(vol, name))
+    if (!vol->vfs->validupath(vol, name))
         return NULL;
 
     /* check for vetoed filenames */
diff -Nur -X .cvsignore -x CVS ../src.dev2/etc/afpd/file.c ./etc/afpd/file.c
--- ../src.dev2/etc/afpd/file.c	Tue Jun 15 22:53:54 2004
+++ ./etc/afpd/file.c	Mon Jun 21 00:21:24 2004
@@ -901,7 +901,7 @@
 
     /* second try with adouble open 
     */
-    if (ad_open( upath, vol_noadouble(vol) | ADFLAGS_HF,
+    if ( ad_open( upath, vol_noadouble(vol) | ADFLAGS_HF,
                  O_RDWR|O_CREAT, 0666, adp) < 0) {
         /* for some things, we don't need an adouble header */
         if (f_bitmap & ~(1<<FILPBIT_MDATE)) {
@@ -1020,7 +1020,6 @@
 char	*src, *dst, *newname;
 struct adouble    *adp;
 {
-    char	adsrc[ MAXPATHLEN + 1];
     int		rc;
 
 #ifdef DEBUG
@@ -1055,38 +1054,10 @@
         }
     }
 
-    strcpy( adsrc, vol->ad_path( src, 0 ));
-
-    if (unix_rename( adsrc, vol->ad_path( dst, 0 )) < 0 ) {
-        struct stat st;
+    if (vol->vfs->rf_renamefile(vol, src, dst) < 0 ) {
         int err;
         
         err = errno;        
-	if (errno == ENOENT) {
-	    struct adouble    ad;
-
-            if (stat(adsrc, &st)) /* source has no ressource fork, */
-                return AFP_OK;
-            
-            /* We are here  because :
-             * -there's no dest folder. 
-             * -there's no .AppleDouble in the dest folder.
-             * if we use the struct adouble passed in parameter it will not
-             * create .AppleDouble if the file is already opened, so we
-             * use a diff one, it's not a pb,ie it's not the same file, yet.
-             */
-            ad_init(&ad, vol->v_adouble); 
-            if (!ad_open(dst, ADFLAGS_HF, O_RDWR | O_CREAT, 0666, &ad)) {
-            	ad_close(&ad, ADFLAGS_HF);
-    	        if (!unix_rename( adsrc, vol->ad_path( dst, 0 )) ) 
-                   err = 0;
-                else 
-                   err = errno;
-            }
-            else { /* it's something else, bail out */
-	        err = errno;
-	    }
-	}
 	/* try to undo the data fork rename,
 	 * we know we are on the same device 
 	*/
@@ -1436,6 +1407,7 @@
     if (ret_err) {
         deletefile(d_vol, dst, 0);
     }
+    /* ADS here */
 
     /* set dest modification date to src date */
     if (!stat(src, &st)) {
@@ -1562,14 +1534,12 @@
     if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
         err = AFPERR_BUSY;
     }
-    else if (!(err = netatalk_unlink( vol->ad_path( file, ADFLAGS_HF)) ) &&
-             !(err = netatalk_unlink( file )) ) {
+    else if (!(err = vol->vfs->rf_deletefile(vol, file)) && !(err = netatalk_unlink( file )) ) {
         cnid_t id;
         if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) 
         {
             cnid_delete(vol->v_cdb, id);
         }
-
     }
     if (adp)
         ad_close( &ad, adflags );  /* ad_close removes locks if any */
diff -Nur -X .cvsignore -x CVS ../src.dev2/etc/afpd/filedir.c ./etc/afpd/filedir.c
--- ../src.dev2/etc/afpd/filedir.c	Mon May 10 18:40:32 2004
+++ ./etc/afpd/filedir.c	Sat Jun 19 15:09:08 2004
@@ -73,7 +73,7 @@
         return AFPERR_NOOBJ ;
     }
 
-    adpath = vol->ad_path( upath, ADFLAGS_HF );
+    adpath = vol->vfs->ad_path( upath, ADFLAGS_HF );
     /* FIXME dirsearch doesn't move cwd to did ! */
     if (( dir = dirlookup( vol, did )) == NULL ) {
         LOG(log_error, logtype_afpd, "matchfile2dirperms: Unable to get directory info.");
@@ -313,7 +313,7 @@
     if ((vol->v_flags & AFPVOL_NOHEX) && strchr(name, '/'))
         return AFPERR_PARAM;
 
-    if (!vol->validupath(vol, name))
+    if (!vol->vfs->validupath(vol, name))
         return AFPERR_EXIST;
 
     /* check for vetoed filenames */
@@ -582,7 +582,7 @@
     	    rc = AFPERR_ACCESS;
     	}
     	else {
-            rc = deletecurdir( vol, obj->oldtmp, AFPOBJ_TMPSIZ);
+            rc = deletecurdir( vol);
         }
     } else if (of_findname(s_path)) {
         rc = AFPERR_BUSY;
@@ -764,7 +764,7 @@
                 int  admode = ad_mode("", 0777);
 
                 setfilmode(upath, admode, NULL);
-                setfilmode(vol->ad_path( upath, ADFLAGS_HF ), ad_hf_mode(admode), NULL);
+                vol->vfs->rf_setfilmode(vol, upath, admode, NULL);
             }
         setvoltime(obj, vol );
     }
diff -Nur -X .cvsignore -x CVS ../src.dev2/etc/afpd/unix.c ./etc/afpd/unix.c
--- ../src.dev2/etc/afpd/unix.c	Tue Jun 15 22:53:55 2004
+++ ./etc/afpd/unix.c	Wed Jun 23 04:04:01 2004
@@ -260,8 +260,8 @@
    rwx-wx-wx or rwx-wx-- 
    rwx----wx (is not asked by a Mac with OS >= 8.0 ?)
 */
-static int stickydirmode(name, mode, dropbox)
-char * name;
+int stickydirmode(name, mode, dropbox)
+const char * name;
 const mode_t mode;
 const int dropbox;
 {
@@ -405,12 +405,12 @@
     if (setfilmode( path->u_name, mode, &path->st) < 0)
         return -1;
     /* we need to set write perm if read set for resource fork */
-    return setfilmode(vol->ad_path( path->u_name, ADFLAGS_HF ), ad_hf_mode(mode), &path->st);
+    return vol->vfs->rf_setfilmode(vol, path->u_name, mode, &path->st);
 }
 
 /* --------------------- */
 int setfilmode(name, mode, st)
-char * name;
+const char * name;
 mode_t mode;
 struct stat *st;
 {
@@ -436,29 +436,18 @@
 const char       *name;
 const mode_t     mode;
 {
-char *adouble = vol->ad_path( name, ADFLAGS_DIR );
 
     int dropbox = (vol->v_flags & AFPVOL_DROPBOX);
 
     if (dir_rx_set(mode)) {
-    	/* extending right? dir first then .AppleDouble */
+    	/* extending right? dir first then .AppleDouble in rf_setdirmode */
     	if ( stickydirmode(name, DIRBITS | mode, dropbox) < 0 )
         	return -1;
-    	if (vol->v_adouble != AD_VERSION2_OSX) {
-            if (stickydirmode(ad_dir(adouble), DIRBITS | mode, dropbox) < 0 && !vol_noadouble(vol)) {
-                return  -1 ;
-            }
-        }
     }
-    if (setfilmode(adouble, ad_hf_mode(mode), NULL) < 0 && !vol_noadouble(vol)) {
+    if (vol->vfs->rf_setdirunixmode(vol, name, mode, NULL) < 0 && !vol_noadouble(vol)) {
         return  -1 ;
     }
     if (!dir_rx_set(mode)) {
-    	if (vol->v_adouble != AD_VERSION2_OSX) {
-            if (stickydirmode(ad_dir(adouble), DIRBITS | mode, dropbox) < 0 && !vol_noadouble(vol)) {
-                return  -1 ;
-            }
-        }
     	if ( stickydirmode(name, DIRBITS | mode, dropbox) < 0 )
             return -1;
     }
@@ -471,26 +460,17 @@
 const char       *name;
 const mode_t mode;
 {
-    char		buf[ MAXPATHLEN + 1];
     struct stat		st;
-    char		*m;
     struct dirent	*dirp;
     DIR			*dir;
     int                 osx = vol->v_adouble == AD_VERSION2_OSX;
     int                 hf_mode = ad_hf_mode(mode);
     int                 dropbox = (vol->v_flags & AFPVOL_DROPBOX);
-    char                *adouble = vol->ad_path( name, ADFLAGS_DIR );
-    char                *adouble_p = ad_dir(adouble);
     
     if (dir_rx_set(mode)) {
-    	/* extending right? dir first then .AppleDouble */
+    	/* extending right? dir first */
     	if ( stickydirmode(name, DIRBITS | mode, dropbox) < 0 )
         	return -1;
-    	if (!osx) {
-            if (stickydirmode(adouble_p, DIRBITS | mode, dropbox) < 0 && !vol_noadouble(vol)) {
-                return  -1 ;
-            }
-        }
     }
     
     if (( dir = opendir( name )) == NULL ) {
@@ -516,61 +496,13 @@
                 return -1;
            }
         }
-#if 0
-        /* Don't change subdir perm */
-        else if (S_ISDIR(st.st_mode)) {
-                if (stickydirmode(dirp->d_name, DIRBITS | mode, dropbox) < 0)
-                    return (-1);
-            } else if (stickydirmode(dirp->d_name, mode, dropbox) < 0)
-                return (-1);
-        }
-#endif
     }
     closedir( dir );
     
-    if (osx) {
-        goto setdirmode_noadouble;
-    }
-    
-    /* change perm of .AppleDouble's files
-    */
-    if (( dir = opendir( adouble_p )) == NULL ) {
-        if (vol_noadouble(vol))
-            goto setdirmode_noadouble;
-        LOG(log_error, logtype_afpd, "setdirmode: opendir %s: %s", fullpathname(".AppleDouble"),strerror(errno) );
-        return( -1 );
-    }
-    strcpy( buf, adouble_p);
-    strcat( buf, "/" );
-    m = strchr( buf, '\0' );
-    for ( dirp = readdir( dir ); dirp != NULL; dirp = readdir( dir )) {
-        if ( strcmp( dirp->d_name, "." ) == 0 ||
-                strcmp( dirp->d_name, ".." ) == 0 ) {
-            continue;
-        }
-        *m = '\0';
-        strcat( buf, dirp->d_name );
-
-        if ( stat( buf, &st ) < 0 ) {
-            LOG(log_error, logtype_afpd, "setdirmode: stat %s: %s", buf, strerror(errno) );
-            continue;
-        }
-        if (!S_ISDIR(st.st_mode)) {
-           if (setfilmode(buf, hf_mode , &st) < 0) {
-               /* FIXME what do we do then? */
-           }
-        }
-    } /* end for */
-    closedir( dir );
-
-    if (!dir_rx_set(mode)) {
-        /* XXX: need to preserve special modes */
-        if (stickydirmode(adouble_p, DIRBITS | mode, dropbox) < 0 ) {
-                return  -1 ;
-        }
+    if (vol->vfs->rf_setdirmode(vol, name, mode, NULL) < 0 && !vol_noadouble(vol)) {
+        return  -1 ;
     }
 
-setdirmode_noadouble:
     if (!dir_rx_set(mode)) {
     	if ( stickydirmode(name, DIRBITS | mode, dropbox) < 0 )
         	return -1;
@@ -578,6 +510,7 @@
     return( 0 );
 }
 
+/* ----------------------------- */
 int setdeskowner( uid, gid )
 const uid_t	uid;
 const gid_t	gid;
@@ -648,8 +581,6 @@
 const gid_t	gid;
 struct path* path;
 {
-    struct stat st;
-    char  *ad_p;
 
     if (!path->st_valid) {
         of_stat(path);
@@ -665,22 +596,15 @@
 	return -1;
     }
 
-    ad_p = vol->ad_path( path->u_name, ADFLAGS_HF );
-
-    if ( stat( ad_p, &st ) < 0 ) {
-	/* ignore */
-        return 0;
-    }
-    if ( chown( ad_p, uid, gid ) < 0 &&
-            errno != EPERM ) {
-        LOG(log_debug, logtype_afpd, "setfilowner: chown %d/%d %s: %s",
-            uid, gid, ad_p, strerror(errno) );
+    if (vol->vfs->rf_chown(vol, path->u_name, uid, gid ) < 0 && errno != EPERM) {
+        LOG(log_debug, logtype_afpd, "setfilowner: rf_chown %d/%d %s: %s",
+            uid, gid, path->u_name, strerror(errno) );
         return -1;
     }
+
     return 0;
 }
 
-
 /* --------------------------------- 
  * uid/gid == 0 need to be handled as special cases. they really mean
  * that user/group should inherit from other, but that doesn't fit
@@ -692,15 +616,10 @@
 const uid_t	uid;
 const gid_t	gid;
 {
-    char		buf[ MAXPATHLEN + 1];
     struct stat		st;
-    char		*m;
     struct dirent	*dirp;
     DIR			*dir;
     int                 osx = vol->v_adouble == AD_VERSION2_OSX;
-    int                 noadouble = vol_noadouble(vol);
-    char                *adouble; 
-    char                *adouble_p;
 
     if (( dir = opendir( name )) == NULL ) {
         return( -1 );
@@ -723,56 +642,15 @@
         }
     }
     closedir( dir );
-    
-    if (osx) {
-       goto setdirowner_noadouble;
-    }
 
-    adouble = vol->ad_path( name, ADFLAGS_DIR );
-    adouble_p = ad_dir(adouble);
-    if (( dir = opendir( adouble_p )) == NULL ) {
-        if (noadouble)
-            goto setdirowner_noadouble;
-        return( -1 );
-    }
-    strcpy( buf, adouble_p );
-    strcat( buf, "/" );
-    m = strchr( buf, '\0' );
-    for ( dirp = readdir( dir ); dirp != NULL; dirp = readdir( dir )) {
-        if ( strcmp( dirp->d_name, "." ) == 0 ||
-                strcmp( dirp->d_name, ".." ) == 0 ) {
-            continue;
-        }
-        *m = '\0';
-        strcat( buf, dirp->d_name );
-        if ( chown( buf, uid, gid ) < 0 && errno != EPERM ) {
-            LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s",
-                uid, gid, fullpathname(buf), strerror(errno) );
-            /* return ( -1 ); Sometimes this is okay */
-        }
-    }
-    closedir( dir );
-
-    /*
-     * We cheat: we know that chown doesn't do anything.
-     */
-    if ( stat( ".AppleDouble", &st ) < 0 ) {
-        LOG(log_error, logtype_afpd, "setdirowner: stat %s: %s", fullpathname(".AppleDouble"), strerror(errno) );
-        return( -1 );
-    }
-    if ( gid && gid != st.st_gid && chown( ".AppleDouble", uid, gid ) < 0 &&
-            errno != EPERM ) {
-        LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s",
-            uid, gid,fullpathname(".AppleDouble"), strerror(errno) );
-        /* return ( -1 ); Sometimes this is okay */
+    if (vol->vfs->rf_setdirowner(vol, name, uid, gid) < 0) {
+        return -1;
     }
-
-setdirowner_noadouble:
+    
     if ( stat( ".", &st ) < 0 ) {
         return( -1 );
     }
-    if ( gid && gid != st.st_gid && chown( ".", uid, gid ) < 0 &&
-            errno != EPERM ) {
+    if ( gid && gid != st.st_gid && chown( ".", uid, gid ) < 0 && errno != EPERM ) {
         LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s",
             uid, gid, fullpathname("."), strerror(errno) );
     }
diff -Nur -X .cvsignore -x CVS ../src.dev2/etc/afpd/unix.h ./etc/afpd/unix.h
--- ../src.dev2/etc/afpd/unix.h	Mon May 10 18:40:33 2004
+++ ./etc/afpd/unix.h	Wed Jun 23 03:43:28 2004
@@ -221,11 +221,12 @@
 extern int setdirmode       __P((const struct vol *, const char *, const mode_t));
 extern int setdeskowner     __P((const uid_t, const gid_t));
 extern int setdirowner      __P((const struct vol *, const char *, const uid_t, const gid_t));
-extern int setfilmode       __P((char *, mode_t , struct stat *));
+extern int setfilmode       __P((const char *, mode_t , struct stat *));
 extern int setfilunixmode   __P((const struct vol *, struct path*, const mode_t));
 extern int setfilowner      __P((const struct vol *, const uid_t, const gid_t, struct path*));
 extern int unix_rename      __P((const char *oldpath, const char *newpath));
 extern int dir_rx_set       __P((mode_t mode));
+extern int stickydirmode    __P((const char * name, const mode_t mode, const int dropbox));
 
 extern void accessmode      __P((char *, struct maccess *, struct dir *, struct stat *));
 extern char *fullpathname   __P((const char *));
diff -Nur -X .cvsignore -x CVS ../src.dev2/etc/afpd/vfs_adouble.c ./etc/afpd/vfs_adouble.c
--- ../src.dev2/etc/afpd/vfs_adouble.c	Thu Jan  1 00:00:00 1970
+++ ./etc/afpd/vfs_adouble.c	Wed Jun 30 19:31:49 2004
@@ -0,0 +1,749 @@
+/*
+    Copyright (c) 2004 Didier Gautheron
+ 
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+ 
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+ 
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ 
+*/
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#ifdef STDC_HEADERS
+#include <string.h>
+#endif
+
+#include <stdio.h>
+    
+#include <atalk/adouble.h>
+#include <atalk/logger.h>
+#include <atalk/util.h>
+
+#include "directory.h"
+#include "volume.h"
+#include "unix.h"
+
+struct perm {
+    uid_t uid;
+    gid_t gid;
+};
+
+typedef int (*rf_loop)(struct dirent *, char *, void *, int );
+
+/* ----------------------------- */
+static int 
+for_each_adouble(const char *from, const char *name, rf_loop fn, void *data, int flag)
+{
+    char            buf[ MAXPATHLEN + 1];
+    char            *m;
+    DIR             *dp;
+    struct dirent   *de;
+    int             ret;
+    
+
+    if (NULL == ( dp = opendir( name)) ) {
+        if (!flag) {
+            LOG(log_error, logtype_afpd, "%s: opendir %s: %s", from, fullpathname(name),strerror(errno) );
+            return -1;
+        }
+        return 0;
+    }
+    strlcpy( buf, name, sizeof(buf));
+    strlcat( buf, "/", sizeof(buf) );
+    m = strchr( buf, '\0' );
+    ret = 0;
+    while ((de = readdir(dp))) {
+        if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) {
+                continue;
+        }
+        
+        strlcat(buf, de->d_name, sizeof(buf));
+        if (fn && (ret = fn(de, buf, data, flag))) {
+           closedir(dp);
+           return ret;
+        }
+        *m = 0;
+    }
+    closedir(dp);
+    return ret;
+}
+
+/* ------------------------------ */
+static int ads_chown_loop(struct dirent *de, char *name, void *data, int flag)
+{
+    struct perm   *owner  = data;
+    
+    if (chown( name , owner->uid, owner->gid ) < 0) {
+        return -1;
+    }
+    return 0;
+}
+
+static int RF_chown_ads(const struct vol *vol, const char *path, uid_t uid, gid_t gid)
+
+{
+    struct        stat st;
+    char          *ad_p;
+    struct perm   owner;
+    
+    owner.uid = uid;
+    owner.gid = gid;
+
+
+    ad_p = ad_dir(vol->vfs->ad_path(path, ADFLAGS_HF ));
+
+    if ( stat( ad_p, &st ) < 0 ) {
+	/* ignore */
+        return 0;
+    }
+    
+    if (chown( ad_p, uid, gid ) < 0) {
+    	return -1;
+    }
+    return for_each_adouble("chown_ads", ad_p, ads_chown_loop, &owner, 1);
+}
+
+/* --------------------------------- */
+static int deletecurdir_ads1_loop(struct dirent *de, char *name, void *data, int flag)
+{
+    return netatalk_unlink(name);
+}
+
+static int ads_delete_rf(char *name) 
+{
+    int err;
+
+    if ((err = for_each_adouble("deletecurdir", name, deletecurdir_ads1_loop, NULL, 1))) 
+        return err;
+    return netatalk_rmdir(name);
+}
+
+static int deletecurdir_ads_loop(struct dirent *de, char *name, void *data, int flag)
+{
+    struct stat st;
+    
+    /* bail if the file exists in the current directory.
+     * note: this will not fail with dangling symlinks */
+    
+    if (stat(de->d_name, &st) == 0) {
+        return AFPERR_DIRNEMPT;
+    }
+    return ads_delete_rf(name);
+}
+
+static int RF_deletecurdir_ads(const struct vol *vol)
+{
+    int err;
+    
+    /* delete stray .AppleDouble files. this happens to get .Parent files as well. */
+    if ((err = for_each_adouble("deletecurdir", ".AppleDouble", deletecurdir_ads_loop, NULL, 1))) 
+        return err;
+    return netatalk_rmdir( ".AppleDouble" );
+}
+
+/* ------------------- */
+struct set_mode {
+    mode_t mode;
+    struct stat *st;
+};
+
+static int ads_setfilmode_loop(struct dirent *de, char *name, void *data, int flag)
+{
+    struct set_mode *param = data;
+
+    return setfilmode(name, param->mode, param->st);
+}
+
+static int ads_setfilmode(const char * name, mode_t mode, struct stat *st)
+{
+    mode_t dir_mode = mode;
+    mode_t file_mode = ad_hf_mode(mode);
+    struct set_mode param;
+
+    if ((dir_mode & (S_IRUSR | S_IWUSR )))
+        dir_mode |= S_IXUSR;
+    if ((dir_mode & (S_IRGRP | S_IWGRP )))
+        dir_mode |= S_IXGRP;
+    if ((dir_mode & (S_IROTH | S_IWOTH )))
+        dir_mode |= S_IXOTH;	
+    
+	/* change folder */
+	dir_mode |= DIRBITS;
+    if (dir_rx_set(dir_mode)) {
+        if (chmod( name,  dir_mode ) < 0)
+            return -1;
+    }
+    param.st = st;
+    param.mode = file_mode;
+    if (for_each_adouble("setfilmode_ads", name, ads_setfilmode_loop, &param, 0) < 0)
+        return -1;
+
+    if (!dir_rx_set(dir_mode)) {
+        if (chmod( name,  dir_mode ) < 0)
+            return -1;
+    }
+
+    return 0;
+}
+
+static int RF_setfilmode_ads(const struct vol *vol, const char * name, mode_t mode, struct stat *st)
+{
+    return ads_setfilmode(ad_dir(vol->vfs->ad_path( name, ADFLAGS_HF )), mode, st);
+}
+
+/* ------------------- */
+static int RF_setdirunixmode_ads(const struct vol *vol, const char * name, mode_t mode, struct stat *st)
+{
+    char *adouble = vol->vfs->ad_path( name, ADFLAGS_DIR );
+    char   ad_p[ MAXPATHLEN + 1];
+    int dropbox = (vol->v_flags & AFPVOL_DROPBOX);
+
+    strlcpy(ad_p,ad_dir(adouble), MAXPATHLEN + 1);
+
+    if (dir_rx_set(mode)) {
+
+        /* .AppleDouble */
+        if (stickydirmode(ad_dir(ad_p), DIRBITS | mode, dropbox) < 0 && !vol_noadouble(vol)) 
+            return -1;
+
+        /* .AppleDouble/.Parent */
+        if (stickydirmode(ad_p, DIRBITS | mode, dropbox) < 0 && !vol_noadouble(vol)) 
+            return -1;
+    }
+
+    if (ads_setfilmode(ad_dir(vol->vfs->ad_path( name, ADFLAGS_DIR)), mode, st) < 0)
+        return -1;
+
+    if (!dir_rx_set(mode)) {
+        if (stickydirmode(ad_p, DIRBITS | mode, dropbox) < 0 && !vol_noadouble(vol)) 
+            return  -1 ;
+        if (stickydirmode(ad_dir(ad_p), DIRBITS | mode, dropbox) < 0 && !vol_noadouble(vol)) 
+            return -1;
+    }
+    return 0;
+}
+
+/* ------------------- */
+struct dir_mode {
+    mode_t mode;
+    int    dropbox;
+};
+
+static int setdirmode_ads_loop(struct dirent *de, char *name, void *data, int flag)
+{
+
+    struct dir_mode *param = data;
+    int    ret = 0; /* 0 ignore error, -1 */
+
+    if (dir_rx_set(param->mode)) {
+        if (stickydirmode(name, DIRBITS | param->mode, param->dropbox) < 0) {
+            if (flag) {
+                return 0;
+            }
+            return ret;
+        }
+    }
+    if (ads_setfilmode(name, param->mode, NULL) < 0)
+        return ret;
+
+    if (!dir_rx_set(param->mode)) {
+        if (stickydirmode(name, DIRBITS | param->mode, param->dropbox) < 0) {
+            if (flag) {
+                return 0;
+            }
+            return ret;
+        }
+    }
+    return 0;
+}
+
+static int RF_setdirmode_ads(const struct vol *vol, const char * name, mode_t mode, struct stat *st)
+{
+    char *adouble = vol->vfs->ad_path( name, ADFLAGS_DIR );
+    char   ad_p[ MAXPATHLEN + 1];
+    struct dir_mode param;
+
+    param.mode = mode;
+    param.dropbox = (vol->v_flags & AFPVOL_DROPBOX);
+
+    strlcpy(ad_p,ad_dir(adouble), sizeof(ad_p));
+
+    if (dir_rx_set(mode)) {
+        /* .AppleDouble */
+        if (stickydirmode(ad_dir(ad_p), DIRBITS | mode, param.dropbox) < 0 && !vol_noadouble(vol)) 
+            return -1;
+    }
+
+    if (for_each_adouble("setdirmode_ads", ad_dir(ad_p), setdirmode_ads_loop, &param, vol_noadouble(vol)))
+        return -1;
+
+    if (!dir_rx_set(mode)) {
+        if (stickydirmode(ad_dir(ad_p), DIRBITS | mode, param.dropbox) < 0 && !vol_noadouble(vol)) 
+            return -1;
+    }
+    return 0;
+}
+
+/* ------------------- */
+static int setdirowner_ads1_loop(struct dirent *de, char *name, void *data, int flag)
+{
+    struct perm   *owner  = data;
+
+    if ( chown( name, owner->uid, owner->gid ) < 0 && errno != EPERM ) {
+         LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s",
+                owner->uid, owner->gid, fullpathname(name), strerror(errno) );
+         /* return ( -1 ); Sometimes this is okay */
+    }
+    return 0;
+}
+
+static int setdirowner_ads_loop(struct dirent *de, char *name, void *data, int flag)
+{
+    struct perm   *owner  = data;
+
+    if (for_each_adouble("setdirowner", name, setdirowner_ads1_loop, data, flag) < 0)
+        return -1;
+
+    if ( chown( name, owner->uid, owner->gid ) < 0 && errno != EPERM ) {
+         LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s",
+                owner->uid, owner->gid, fullpathname(name), strerror(errno) );
+         /* return ( -1 ); Sometimes this is okay */
+    }
+    return 0;
+}
+
+static int RF_setdirowner_ads(const struct vol *vol, const char *name, uid_t uid, gid_t gid)
+{
+    int           noadouble = vol_noadouble(vol);
+    char          adouble_p[ MAXPATHLEN + 1];
+    struct stat   st;
+    struct perm   owner;
+    
+    owner.uid = uid;
+    owner.gid = gid;
+
+    strlcpy(adouble_p, ad_dir(vol->vfs->ad_path( name, ADFLAGS_DIR )), sizeof(adouble_p));
+
+    if (for_each_adouble("setdirowner", ad_dir(adouble_p), setdirowner_ads_loop, &owner, noadouble)) 
+        return -1;
+
+    /*
+     * We cheat: we know that chown doesn't do anything.
+     */
+    if ( stat( ".AppleDouble", &st ) < 0) {
+        if (errno == ENOENT && noadouble)
+            return 0;
+        LOG(log_error, logtype_afpd, "setdirowner: stat %s: %s", fullpathname(".AppleDouble"), strerror(errno) );
+        return -1;
+    }
+    if ( gid && gid != st.st_gid && chown( ".AppleDouble", uid, gid ) < 0 && errno != EPERM ) {
+        LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s",
+            uid, gid,fullpathname(".AppleDouble"), strerror(errno) );
+        /* return ( -1 ); Sometimes this is okay */
+    }
+    return 0;
+}
+
+/* ------------------- */
+static int RF_deletefile_ads(const struct vol *vol, const char *file )
+{
+    char *ad_p = ad_dir(vol->vfs->ad_path(file, ADFLAGS_HF ));
+
+    return ads_delete_rf(ad_p);
+}
+
+/* --------------------------- */
+int RF_renamefile_ads(const struct vol *vol, const char *src, const char *dst)
+{
+    char  adsrc[ MAXPATHLEN + 1];
+    int   err = 0;
+
+    strcpy( adsrc, ad_dir(vol->vfs->ad_path( src, 0 )));
+    if (unix_rename( adsrc, ad_dir(vol->vfs->ad_path( dst, 0 ))) < 0) {
+        struct stat st;
+
+        err = errno;
+        if (errno == ENOENT) {
+	        struct adouble    ad;
+
+            if (stat(adsrc, &st)) /* source has no ressource fork, */
+                return AFP_OK;
+            
+            /* We are here  because :
+             * -there's no dest folder. 
+             * -there's no .AppleDouble in the dest folder.
+             * if we use the struct adouble passed in parameter it will not
+             * create .AppleDouble if the file is already opened, so we
+             * use a diff one, it's not a pb,ie it's not the same file, yet.
+             */
+            ad_init(&ad, vol->v_adouble); 
+            if (!ad_open(dst, ADFLAGS_HF, O_RDWR | O_CREAT, 0666, &ad)) {
+            	ad_close(&ad, ADFLAGS_HF);
+
+            	/* We must delete it */
+            	RF_deletefile_ads(vol, dst );
+    	        if (!unix_rename( adsrc, ad_dir(vol->vfs->ad_path( dst, 0 ))) ) 
+                   err = 0;
+                else 
+                   err = errno;
+            }
+            else { /* it's something else, bail out */
+	            err = errno;
+	        }
+	    }
+	}
+	if (err) {
+		errno = err;
+		return -1;
+	}
+	return 0;
+}
+
+/* ===================================================
+ classic adouble format 
+*/
+
+static int validupath_adouble(const struct vol *vol, const char *name) 
+{
+    return (vol->v_flags & AFPVOL_USEDOTS) ? strncasecmp(name,".Apple", 6) && strcasecmp(name, ".Parent")
+                                           : name[0] != '.';
+}
+
+/* ----------------- */
+static int RF_chown_adouble(const struct vol *vol, const char *path, uid_t uid, gid_t gid)
+
+{
+    struct stat st;
+    char        *ad_p;
+
+    ad_p = vol->vfs->ad_path(path, ADFLAGS_HF );
+
+    if ( stat( ad_p, &st ) < 0 )
+        return 0; /* ignore */
+
+    return chown( ad_p, uid, gid );
+}
+
+/* ----------------- */
+int RF_renamedir_adouble(const struct vol *vol, const char *oldpath, const char *newpath)
+{
+    return 0;
+}
+
+/* ----------------- */
+static int deletecurdir_adouble_loop(struct dirent *de, char *name, void *data, int flag)
+{
+    struct stat st;
+    int         err;
+    
+    /* bail if the file exists in the current directory.
+     * note: this will not fail with dangling symlinks */
+    
+    if (stat(de->d_name, &st) == 0)
+        return AFPERR_DIRNEMPT;
+
+    if ((err = netatalk_unlink(name)))
+        return err;
+
+    return 0;
+}
+
+static int RF_deletecurdir_adouble(const struct vol *vol)
+{
+    int err;
+
+    /* delete stray .AppleDouble files. this happens to get .Parent files
+       as well. */
+    if ((err = for_each_adouble("deletecurdir", ".AppleDouble", deletecurdir_adouble_loop, NULL, 1))) 
+        return err;
+    return netatalk_rmdir( ".AppleDouble" );
+}
+
+/* ----------------- */
+static int adouble_setfilmode(const char * name, mode_t mode, struct stat *st)
+{
+    return setfilmode(name, ad_hf_mode(mode), st);
+}
+
+static int RF_setfilmode_adouble(const struct vol *vol, const char * name, mode_t mode, struct stat *st)
+{
+    return adouble_setfilmode(vol->vfs->ad_path( name, ADFLAGS_HF ), mode, st);
+}
+
+/* ----------------- */
+static int RF_setdirunixmode_adouble(const struct vol *vol, const char * name, mode_t mode, struct stat *st)
+{
+    char *adouble = vol->vfs->ad_path( name, ADFLAGS_DIR );
+    int  dropbox = (vol->v_flags & AFPVOL_DROPBOX);
+
+    if (dir_rx_set(mode)) {
+        if (stickydirmode(ad_dir(adouble), DIRBITS | mode, dropbox) < 0 && !vol_noadouble(vol)) 
+            return -1;
+    }
+
+    if (adouble_setfilmode(vol->vfs->ad_path( name, ADFLAGS_DIR ), mode, st) < 0) 
+        return -1;
+
+    if (!dir_rx_set(mode)) {
+        if (stickydirmode(ad_dir(adouble), DIRBITS | mode, dropbox) < 0 && !vol_noadouble(vol)) 
+            return  -1 ;
+    }
+    return 0;
+}
+
+/* ----------------- */
+static int setdirmode_adouble_loop(struct dirent *de, char *name, void *data, int flag)
+{
+    int         hf_mode = *(int *)data;
+    struct stat st;
+
+    if ( stat( name, &st ) < 0 ) {
+        if (flag)
+            return 0;
+        LOG(log_error, logtype_afpd, "setdirmode: stat %s: %s", name, strerror(errno) );
+    }
+    else if (!S_ISDIR(st.st_mode)) {
+        if (setfilmode(name, hf_mode , &st) < 0) {
+               /* FIXME what do we do then? */
+        }
+    }
+    return 0;
+}
+
+static int RF_setdirmode_adouble(const struct vol *vol, const char * name, mode_t mode, struct stat *st1)
+{
+    int   dropbox = (vol->v_flags & AFPVOL_DROPBOX);
+    int   hf_mode = ad_hf_mode(mode);
+    char  *adouble = vol->vfs->ad_path( name, ADFLAGS_DIR );
+    char  *adouble_p = ad_dir(adouble);
+
+    if (dir_rx_set(mode)) {
+        if (stickydirmode(ad_dir(adouble), DIRBITS | mode, dropbox) < 0 && !vol_noadouble(vol)) 
+            return -1;
+    }
+
+    if (for_each_adouble("setdirmode", adouble_p, setdirmode_adouble_loop, &hf_mode, vol_noadouble(vol)))
+        return -1;
+
+    if (!dir_rx_set(mode)) {
+        if (stickydirmode(ad_dir(adouble), DIRBITS | mode, dropbox) < 0 && !vol_noadouble(vol)) 
+            return  -1 ;
+    }
+    return 0;
+}
+
+/* ----------------- */
+static int setdirowner_adouble_loop(struct dirent *de, char *name, void *data, int flag)
+{
+    struct perm   *owner  = data;
+
+    if ( chown( name, owner->uid, owner->gid ) < 0 && errno != EPERM ) {
+         LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s",
+                owner->uid, owner->gid, fullpathname(name), strerror(errno) );
+         /* return ( -1 ); Sometimes this is okay */
+    }
+    return 0;
+}
+
+static int RF_setdirowner_adouble(const struct vol *vol, const char *name, uid_t uid, gid_t gid)
+
+{
+    int           noadouble = vol_noadouble(vol);
+    char          *adouble_p;
+    struct stat   st;
+    struct perm   owner;
+    
+    owner.uid = uid;
+    owner.gid = gid;
+
+    adouble_p = ad_dir(vol->vfs->ad_path( name, ADFLAGS_DIR ));
+
+    if (for_each_adouble("setdirowner", adouble_p, setdirowner_adouble_loop, &owner, noadouble)) 
+        return -1;
+
+    /*
+     * We cheat: we know that chown doesn't do anything.
+     */
+    if ( stat( ".AppleDouble", &st ) < 0) {
+        if (errno == ENOENT && noadouble)
+            return 0;
+        LOG(log_error, logtype_afpd, "setdirowner: stat %s: %s", fullpathname(".AppleDouble"), strerror(errno) );
+        return -1;
+    }
+    if ( gid && gid != st.st_gid && chown( ".AppleDouble", uid, gid ) < 0 && errno != EPERM ) {
+        LOG(log_debug, logtype_afpd, "setdirowner: chown %d/%d %s: %s",
+            uid, gid,fullpathname(".AppleDouble"), strerror(errno) );
+        /* return ( -1 ); Sometimes this is okay */
+    }
+    return 0;
+}
+
+/* ----------------- */
+static int RF_deletefile_adouble(const struct vol *vol, const char *file )
+{
+	return netatalk_unlink(vol->vfs->ad_path( file, ADFLAGS_HF));
+}
+
+/* ----------------- */
+int RF_renamefile_adouble(const struct vol *vol, const char *src, const char *dst)
+{
+    char  adsrc[ MAXPATHLEN + 1];
+    int   err = 0;
+
+    strcpy( adsrc, vol->vfs->ad_path( src, 0 ));
+    if (unix_rename( adsrc, vol->vfs->ad_path( dst, 0 )) < 0) {
+        struct stat st;
+
+        err = errno;
+        if (errno == ENOENT) {
+	        struct adouble    ad;
+
+            if (stat(adsrc, &st)) /* source has no ressource fork, */
+                return AFP_OK;
+            
+            /* We are here  because :
+             * -there's no dest folder. 
+             * -there's no .AppleDouble in the dest folder.
+             * if we use the struct adouble passed in parameter it will not
+             * create .AppleDouble if the file is already opened, so we
+             * use a diff one, it's not a pb,ie it's not the same file, yet.
+             */
+            ad_init(&ad, vol->v_adouble); 
+            if (!ad_open(dst, ADFLAGS_HF, O_RDWR | O_CREAT, 0666, &ad)) {
+            	ad_close(&ad, ADFLAGS_HF);
+    	        if (!unix_rename( adsrc, vol->vfs->ad_path( dst, 0 )) ) 
+                   err = 0;
+                else 
+                   err = errno;
+            }
+            else { /* it's something else, bail out */
+	            err = errno;
+	        }
+	    }
+	}
+	if (err) {
+		errno = err;
+		return -1;
+	}
+	return 0;
+}
+
+struct vfs_ops netatalk_adouble = {
+    /* ad_path:           */ ad_path,
+    /* validupath:        */ validupath_adouble,
+    /* rf_chown:          */ RF_chown_adouble,
+    /* rf_renamedir:      */ RF_renamedir_adouble,
+    /* rf_deletecurdir:   */ RF_deletecurdir_adouble,
+    /* rf_setfilmode:     */ RF_setfilmode_adouble,
+    /* rf_setdirmode:     */ RF_setdirmode_adouble,
+    /* rf_setdirunixmode: */ RF_setdirunixmode_adouble,
+    /* rf_setdirowner:    */ RF_setdirowner_adouble,
+    /* rf_deletefile:     */ RF_deletefile_adouble,
+    /* rf_renamefile:     */ RF_renamefile_adouble,
+};
+
+/* =======================================
+ osx adouble format 
+ */
+static int validupath_osx(const struct vol *vol, const char *name) 
+{
+    return strncasecmp(name,".Apple", 6) && strncasecmp(name,"._", 2);
+}
+
+/* ---------------- */
+int RF_renamedir_osx(const struct vol *vol, const char *oldpath, const char *newpath)
+{
+    /* We simply move the corresponding ad file as well */
+    char   tempbuf[258]="._";
+    return rename(vol->vfs->ad_path(oldpath,0),strcat(tempbuf,newpath));
+}
+
+/* ---------------- */
+int RF_deletecurdir_osx(const struct vol *vol)
+{
+    return netatalk_unlink( vol->vfs->ad_path(".",0) );
+}
+
+/* ---------------- */
+static int RF_setdirunixmode_osx(const struct vol *vol, const char * name, mode_t mode, struct stat *st)
+{
+    return adouble_setfilmode(vol->vfs->ad_path( name, ADFLAGS_DIR ), mode, st);
+}
+
+/* ---------------- */
+static int RF_setdirmode_osx(const struct vol *vol, const char * name, mode_t mode, struct stat *st)
+{
+    return 0;
+}
+
+/* ---------------- */
+static int RF_setdirowner_osx(const struct vol *vol, const char *path, uid_t uid, gid_t gid)
+{
+	return 0;
+}
+
+/* ---------------- */
+int RF_renamefile_osx(const struct vol *vol, const char *src, const char *dst)
+{
+    char  adsrc[ MAXPATHLEN + 1];
+
+    strcpy( adsrc, vol->vfs->ad_path( src, 0 ));
+    return unix_rename( adsrc, vol->vfs->ad_path( dst, 0 ));
+}
+
+struct vfs_ops netatalk_adouble_osx = {
+    /* ad_path:          */ ad_path_osx,
+    /* validupath:       */ validupath_osx,
+    /* rf_chown:         */ RF_chown_adouble,
+    /* rf_renamedir:     */ RF_renamedir_osx,
+    /* rf_deletecurdir:  */ RF_deletecurdir_osx,
+    /* rf_setfilmode:    */ RF_setfilmode_adouble,
+    /* rf_setdirmode:    */ RF_setdirmode_osx,
+    /* rf_setdirunixmode:*/ RF_setdirunixmode_osx,
+    /* rf_setdirowner:   */ RF_setdirowner_osx,
+    /* rf_deletefile:    */ RF_deletefile_adouble,
+    /* rf_renamefile:    */ RF_renamefile_osx,
+};
+
+/* =======================================
+   samba ads format 
+ */
+struct vfs_ops netatalk_adouble_ads = {
+    /* ad_path:          */ ad_path_ads,
+    /* validupath:       */ validupath_adouble,
+    /* rf_chown:         */ RF_chown_ads,
+    /* rf_renamedir:     */ RF_renamedir_adouble,
+    /* rf_deletecurdir:  */ RF_deletecurdir_ads,
+    /* rf_setfilmode:    */ RF_setfilmode_ads,
+    /* rf_setdirmode:    */ RF_setdirmode_ads,
+    /* rf_setdirunixmode:*/ RF_setdirunixmode_ads,
+    /* rf_setdirowner:   */ RF_setdirowner_ads,
+    /* rf_deletefile:    */ RF_deletefile_ads,
+    /* rf_renamefile:    */ RF_renamefile_ads,
+};
+
+/* ---------------- */
+void initvol_vfs(struct vol *vol)
+{
+    if (vol->v_adouble == AD_VERSION2_OSX) {
+        vol->vfs = &netatalk_adouble_osx;
+    }
+    else if (vol->v_adouble == AD_VERSION1_ADS) {
+        vol->vfs = &netatalk_adouble_ads;
+    }
+    else {
+        vol->vfs = &netatalk_adouble;
+    }
+}
+
diff -Nur -X .cvsignore -x CVS ../src.dev2/etc/afpd/volume.c ./etc/afpd/volume.c
--- ../src.dev2/etc/afpd/volume.c	Mon Jul 12 08:46:03 2004
+++ ./etc/afpd/volume.c	Mon Jul 12 00:29:11 2004
@@ -427,6 +427,8 @@
             options[VOLOPT_ADOUBLE].i_value = AD_VERSION2;
         else if (strcasecmp(val + 1, "osx") == 0)
             options[VOLOPT_ADOUBLE].i_value = AD_VERSION2_OSX;
+        else if (strcasecmp(val + 1, "ads") == 0)
+            options[VOLOPT_ADOUBLE].i_value = AD_VERSION1_ADS;
 #endif
     } else if (optionok(tmp, "options:", val)) {
         char *p;
@@ -523,34 +525,6 @@
     }
 }
 
-/* ----------------- 
- * FIXME should be define elsewhere
-*/
-static int validupath_adouble(const struct vol *vol, const char *name) 
-{
-    return (vol->v_flags & AFPVOL_USEDOTS) ? strncasecmp(name,".Apple", 6) && strcasecmp(name, ".Parent")
-                                           : name[0] != '.';
-}                                           
-
-/* ----------------- */
-static int validupath_osx(const struct vol *vol, const char *name) 
-{
-    return strncasecmp(name,".Apple", 6) && strncasecmp(name,"._", 2);
-}             
-
-/* ---------------- */
-static void initvoladouble(struct vol *vol)
-{
-    if (vol->v_adouble == AD_VERSION2_OSX) {
-        vol->validupath  = validupath_osx;
-        vol->ad_path     = ad_path_osx;
-    }
-    else {
-        vol->validupath  = validupath_adouble;
-        vol->ad_path     = ad_path;
-    }
-}
-
 /* ------------------------------- */
 static int creatvol(AFPObj *obj, struct passwd *pwd, 
                     char *path, char *name, 
@@ -653,7 +627,8 @@
 	    volume->v_adouble = options[VOLOPT_ADOUBLE].i_value;
 	else 
 	    volume->v_adouble = AD_VERSION;
-	initvoladouble(volume);
+
+	initvol_vfs(volume);
 #ifdef FORCE_UIDGID
         if (options[VOLOPT_FORCEUID].c_value) {
             volume->v_forceuid = strdup(options[VOLOPT_FORCEUID].c_value);
@@ -2231,6 +2206,9 @@
             break;
         case AD_VERSION2_OSX:
             strlcat(buf, "ADOUBLE_VER:osx\n", sizeof(buf));
+            break;
+        case AD_VERSION1_ADS:
+            strlcat(buf, "ADOUBLE_VER:ads\n", sizeof(buf));
             break;
     }
 
diff -Nur -X .cvsignore -x CVS ../src.dev2/etc/afpd/volume.h ./etc/afpd/volume.h
--- ../src.dev2/etc/afpd/volume.h	Mon Jul 12 08:46:03 2004
+++ ./etc/afpd/volume.h	Fri Jun 25 22:01:56 2004
@@ -14,6 +14,7 @@
 
 #include "atalk/unicode.h"
 #include "globals.h"
+#include "afp_vfs.h"
 
 #define AFPVOL_NAMELEN   27
 
@@ -75,6 +76,7 @@
     int                 v_preexec_close;
     
     /* adouble indirection */
+    struct vfs_ops      *vfs;
     int                 (*validupath)(const struct vol *, const char *);
     char                *(*ad_path)(const char *, int);
 };
diff -Nur -X .cvsignore -x CVS ../src.dev2/include/atalk/adouble.h ./include/atalk/adouble.h
--- ../src.dev2/include/atalk/adouble.h	Tue Jun 15 01:08:28 2004
+++ ./include/atalk/adouble.h	Sun Jun 20 22:33:26 2004
@@ -82,6 +82,7 @@
 #define AD_VERSION1	0x00010000
 #define AD_VERSION2	0x00020000
 #define AD_VERSION2_OSX	0x00020001
+#define AD_VERSION1_ADS	0x00010002
 #define AD_VERSION	AD_VERSION2
 
 /*
@@ -252,6 +253,7 @@
                                         the header parameter size is too small.
                                      */
     char                *(*ad_path)(const char *, int);
+    int                 (*ad_mkrf)(char *);
                            
 #ifdef USE_MMAPPED_HEADERS
     char                *ad_data;
@@ -364,6 +366,7 @@
 extern char *ad_dir       __P((const char *));
 extern char *ad_path      __P((const char *, int));
 extern char *ad_path_osx  __P((const char *, int));
+extern char *ad_path_ads  __P((const char *, int));
 
 extern int ad_mode        __P((const char *, int));
 extern int ad_mkdir       __P((const char *, int));
diff -Nur -X .cvsignore -x CVS ../src.dev2/libatalk/adouble/ad_open.c ./libatalk/adouble/ad_open.c
--- ../src.dev2/libatalk/adouble/ad_open.c	Mon Jul 12 02:01:45 2004
+++ ./libatalk/adouble/ad_open.c	Mon Jul 12 02:12:25 2004
@@ -697,6 +697,25 @@
     return( pathbuf );
 }
 
+/* -------------------- */
+static int ad_mkrf(char *path)
+{
+    char *slash;
+    /*
+     * Probably .AppleDouble doesn't exist, try to mkdir it.
+     */
+     if (NULL == ( slash = strrchr( path, '/' )) ) {
+         return -1;
+     }
+     *slash = '\0';
+     errno = 0;
+     if ( ad_mkdir( path, 0777 ) < 0 ) {
+          return -1;
+     }
+     *slash = '/';
+     return 0;
+}
+
 /* ---------------------------------------
  * Put the resource fork where it needs to be:
  * ._name
@@ -729,8 +748,97 @@
     strlcat( pathbuf, slash, MAXPATHLEN +1);
     return pathbuf;
 }
+/* -------------------- */
+static int ad_mkrf_osx(char *path)
+{
+    return 0;
+}
 
-/*
+/* ---------------------------------------
+ * Put the .AppleDouble where it needs to be:
+ *
+ *	    /	a/.AppleDouble/b/Afp_AfpInfo
+ *	a/b     
+ *	    \	b/.AppleDouble/.Parent/Afp_AfpInfo
+ *
+ */
+char *
+ad_path_ads( path, adflags )
+    const char	*path;
+    int		adflags;
+{
+    static char	pathbuf[ MAXPATHLEN + 1];
+    char	c, *slash, buf[MAXPATHLEN + 1];
+    size_t      l;
+
+    l = strlcpy(buf, path, MAXPATHLEN +1);
+    if ( adflags & ADFLAGS_DIR ) {
+	strcpy( pathbuf, buf);
+	if ( *buf != '\0' && l < MAXPATHLEN) {
+	    pathbuf[l++] = '/';
+	    pathbuf[l] = 0;
+	}
+	slash = ".Parent";
+    } else {
+	if (NULL != ( slash = strrchr( buf, '/' )) ) {
+	    c = *++slash;
+	    *slash = '\0';
+	    strcpy( pathbuf, buf);
+	    *slash = c;
+	} else {
+	    pathbuf[ 0 ] = '\0';
+	    slash = buf;
+	}
+    }
+    strlcat( pathbuf, ".AppleDouble/", MAXPATHLEN +1);
+    strlcat( pathbuf, slash, MAXPATHLEN +1);
+
+    strlcat( pathbuf, "/Afp_AfpInfo", MAXPATHLEN +1);
+
+#if 0
+    if ((adflags & ADFLAGS_HF)) {
+        strlcat( pathbuf, "Afp_AfpInfo", MAXPATHLEN +1);
+    else {
+        strlcat( pathbuf, "Afp_Resource", MAXPATHLEN +1);
+    }
+#endif      
+    return( pathbuf );
+}
+
+/* -------------------- */
+static int ad_mkrf_ads(char *path)
+{
+    char *slash;
+    /*
+     * Probably .AppleDouble doesn't exist, try to mkdir it.
+     */
+     if (NULL == ( slash = strrchr( path, '/' )) ) {
+         return -1;
+     }
+     *slash = 0;
+     errno = 0;
+     if ( ad_mkdir( path, 0777 ) < 0 ) {
+         if ( errno == ENOENT ) {
+             char *slash1;
+             
+             if (NULL == ( slash1 = strrchr( path, '/' )) ) 
+                 return -1;
+             errno = 0;
+             *slash1 = 0;
+             if ( ad_mkdir( path, 0777 ) < 0 ) 
+                  return -1;
+             *slash1 = '/';
+             if ( ad_mkdir( path, 0777 ) < 0 )
+                 return -1;
+         }
+         else
+            return -1;
+     }     
+     *slash = '/';
+     return 0;
+}
+
+/* -------------------------
  * Support inherited protection modes for AppleDouble files.  The supplied
  * mode is ANDed with the parent directory's mask value in lieu of "umask",
  * and that value is returned.
@@ -914,10 +1022,16 @@
     memset( ad, 0, sizeof( struct adouble ) );
     ad->ad_flags = flags;
     if (flags == AD_VERSION2_OSX) {
-        ad->ad_path     = ad_path_osx;
+        ad->ad_path = ad_path_osx;
+        ad->ad_mkrf = ad_mkrf_osx;
+    }
+    else if (flags == AD_VERSION1_ADS) {
+        ad->ad_path = ad_path_ads;
+        ad->ad_mkrf = ad_mkrf_ads;
     }
     else {
-        ad->ad_path     = ad_path;
+        ad->ad_path = ad_path;
+        ad->ad_mkrf = ad_mkrf;
     }
 }
 
@@ -931,7 +1045,7 @@
     struct adouble	*ad;
 {
     struct stat         st;
-    char		*slash, *ad_p;
+    char		*ad_p;
     int			hoflags, admode;
     int                 st_invalid;
     int                 open_df = 0;
@@ -1031,19 +1145,9 @@
 	    st_invalid = ad_mode_st(ad_p, &admode, &st);
 	    admode = ad_hf_mode(admode); 
 	    if ( errno == ENOENT && !(adflags & ADFLAGS_NOADOUBLE) && ad->ad_flags != AD_VERSION2_OSX) {
-		/*
-		 * Probably .AppleDouble doesn't exist, try to
-		 * mkdir it.
-		 */
-		if (NULL == ( slash = strrchr( ad_p, '/' )) ) {
-		    return ad_error(ad, adflags);
-		}
-		*slash = '\0';
-		errno = 0;
-		if ( ad_mkdir( ad_p, 0777 ) < 0 ) {
+	    	if (ad->ad_mkrf( ad_p) < 0) {
 		    return ad_error(ad, adflags);
-		}
-		*slash = '/';
+	    	}
 		admode = mode;
 		st_invalid = ad_mode_st(ad_p, &admode, &st);
 		admode = ad_hf_mode(admode); 
