+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, ¶m, 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, ¶m, 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);