]> arthur.barton.de Git - netatalk.git/commitdiff
Patch for a NT ADS resource fork. It's both a patch for netatalk and samba cf
authordidg <didg>
Tue, 13 Jul 2004 03:52:58 +0000 (03:52 +0000)
committerdidg <didg>
Tue, 13 Jul 2004 03:52:58 +0000 (03:52 +0000)
patach.afp_vfs

contrib/patches/patch.afp_vfs [new file with mode: 0644]
contrib/patches/patch.samba.3.0.5pre2-SVN [new file with mode: 0644]
contrib/patches/patch.samba.3.0a20 [new file with mode: 0644]
contrib/patches/patch.vfs [new file with mode: 0644]

diff --git a/contrib/patches/patch.afp_vfs b/contrib/patches/patch.afp_vfs
new file mode 100644 (file)
index 0000000..f2c08ee
--- /dev/null
@@ -0,0 +1,1678 @@
+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); 
diff --git a/contrib/patches/patch.samba.3.0.5pre2-SVN b/contrib/patches/patch.samba.3.0.5pre2-SVN
new file mode 100644 (file)
index 0000000..8b17608
--- /dev/null
@@ -0,0 +1,453 @@
+Index: source/smbd/nttrans.c
+===================================================================
+--- source/smbd/nttrans.c      (revision 1473)
++++ source/smbd/nttrans.c      (working copy)
+@@ -661,11 +661,16 @@
+                        * Check to see if this is a mac fork of some kind.
+                        */
+-                      if( strchr_m(fname, ':')) {
+-                              END_PROFILE(SMBntcreateX);
+-                              return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
+-                      }
+-
++                        if( !strchr_m(fname, ':')) {
++                                /* it's not an alternate stream */
++                                END_PROFILE(SMBntcreateX);
++                                return(ERROR_DOS(ERRDOS,ERRbadfid));
++                        }
++                        else if (-1 == SMB_VFS_LISTADS(conn, NULL, NULL, 0)) {
++                                /* fs have no support for alternate streams */
++                                END_PROFILE(SMBntcreateX);
++                                return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
++                        }
+                       /*
+                         we need to handle the case when we get a
+                         relative open relative to a file and the
+@@ -673,26 +678,29 @@
+                         (hint from demyn plantenberg)
+                       */
+-                      END_PROFILE(SMBntcreateX);
+-                      return(ERROR_DOS(ERRDOS,ERRbadfid));
++                      /*
++                       * Copy in the base name.
++                       */
++                      pstrcpy( fname, dir_fsp->fsp_name );
++                      dir_name_len = strlen(fname);
+               }
++              else { /* it's a dir */
++                      /*
++                       * Copy in the base directory name.
++                       */
+-              /*
+-               * Copy in the base directory name.
+-               */
++                      pstrcpy( fname, dir_fsp->fsp_name );
++                      dir_name_len = strlen(fname);
+-              pstrcpy( fname, dir_fsp->fsp_name );
+-              dir_name_len = strlen(fname);
++                      /*
++                       * Ensure it ends in a '\'.
++                       */
+-              /*
+-               * Ensure it ends in a '\'.
+-               */
+-
+-              if(fname[dir_name_len-1] != '\\' && fname[dir_name_len-1] != '/') {
+-                      pstrcat(fname, "/");
+-                      dir_name_len++;
+-              }
+-
++                      if(fname[dir_name_len-1] != '\\' && fname[dir_name_len-1] != '/') {
++                              pstrcat(fname, "/");
++                              dir_name_len++;
++                      }
++                }
+               srvstr_get_path(inbuf, rel_fname, smb_buf(inbuf), sizeof(rel_fname), 0, STR_TERMINATE, &status,False);
+               if (!NT_STATUS_IS_OK(status)) {
+                       END_PROFILE(SMBntcreateX);
+@@ -709,7 +717,6 @@
+               /* 
+                * Check to see if this is a mac fork of some kind.
+                */
+-
+               if( strchr_m(fname, ':')) {
+                       
+ #ifdef HAVE_SYS_QUOTAS
+@@ -725,8 +732,11 @@
+                                */
+                       } else {
+ #endif
+-                              END_PROFILE(SMBntcreateX);
+-                              return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
++                              if (-1 == SMB_VFS_LISTADS(conn, NULL, NULL, 0)) {  
++                                      END_PROFILE(SMBntcreateX);
++                                      return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
++                              }
++
+ #ifdef HAVE_SYS_QUOTAS
+                       }
+ #endif
+@@ -1235,12 +1245,10 @@
+                       }
+                       /*
+-                       * Check to see if this is a mac fork of some kind.
++                       * Check to see if this is a mac fork of some kind. FIXME
+                        */
+-
+-                      if( strchr_m(fname, ':'))
++                      if( strchr_m(fname, ':') && -1 == SMB_VFS_LISTADS(conn, NULL, NULL, 0))
+                               return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
+-
+                       return ERROR_DOS(ERRDOS,ERRbadfid);
+               }
+@@ -1278,7 +1286,7 @@
+                * Check to see if this is a mac fork of some kind.
+                */
+-              if( strchr_m(fname, ':'))
++              if( strchr_m(fname, ':') && -1 == SMB_VFS_LISTADS(conn, NULL, NULL, 0))
+                       return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
+       }
+Index: source/smbd/vfs.c
+===================================================================
+--- source/smbd/vfs.c  (revision 1473)
++++ source/smbd/vfs.c  (working copy)
+@@ -142,7 +142,10 @@
+               vfswrap_fremovexattr,
+               vfswrap_setxattr,
+               vfswrap_lsetxattr,
+-              vfswrap_fsetxattr
++              vfswrap_fsetxattr,
++
++              /* alternate streams operations. */
++              vfswrap_listads
+       }
+ };
+Index: source/smbd/vfs-wrap.c
+===================================================================
+--- source/smbd/vfs-wrap.c     (revision 1473)
++++ source/smbd/vfs-wrap.c     (working copy)
+@@ -1029,3 +1029,14 @@
+ {
+       return sys_fsetxattr(fd, name, value, size, flags);
+ }
++
++/****************************************************************
++ Alternate stream operations.
++*****************************************************************/
++
++ssize_t vfswrap_listads(struct vfs_handle_struct *handle, struct connection_struct *conn,const char *path, char *list, size_t size)
++{
++        errno = ENOSYS;
++        return -1;
++}
++
+Index: source/smbd/trans2.c
+===================================================================
+--- source/smbd/trans2.c       (revision 1473)
++++ source/smbd/trans2.c       (working copy)
+@@ -406,6 +406,159 @@
+ }
+ /****************************************************************************
++ ****************************************************************************
++ Return a linked list of the alternate streams Plus the total size
++****************************************************************************/
++struct ads_list {
++      struct ads_list *next, *prev;
++      struct ads_struct ads;
++};
++
++static struct ads_list *get_ads_list(TALLOC_CTX *mem_ctx, connection_struct *conn, files_struct *fsp, const char *fname, size_t *pads_total_len)
++{
++      /* Get a list of all ads with size, lax namesize is 64k. */
++      size_t ads_namelist_size = 4096;
++      char *ads_namelist;
++      char *p;
++      ssize_t sizeret;
++      int i;
++      struct ads_list *ads_list_head = NULL;
++
++      *pads_total_len = 0;
++
++      DEBUG(10,("get_ads_list\n" ));
++      
++      for (i = 0, ads_namelist = talloc(mem_ctx, ads_namelist_size); i < 6;
++                      ads_namelist = talloc_realloc(mem_ctx, ads_namelist, ads_namelist_size), i++) {
++
++              sizeret = SMB_VFS_LISTADS(conn, fname, ads_namelist, ads_namelist_size);
++              if (sizeret == -1 && errno == ERANGE) {
++                      ads_namelist_size *= 2;
++              } else {
++                      break;
++              }
++      }
++
++      if (sizeret == -1)
++              return NULL;
++
++      DEBUG(10,("get_ads_list: ads_namelist size = %d\n", sizeret ));
++
++      if (sizeret) { 
++              for (p = ads_namelist; p - ads_namelist < sizeret; p += strlen(p) +1) {
++                      struct ads_list *listp, *tmp;
++                      SMB_STRUCT_STAT sbuf;
++                      char *t;
++                      
++                      listp = talloc(mem_ctx, sizeof(struct ads_list));
++                      if (!listp)
++                              return NULL;
++
++                      listp->ads.name = talloc_strdup(mem_ctx, p);
++                      if (!listp->ads.name)
++                              return NULL;
++                      
++                      listp->ads.size = 0;
++                      listp->ads.allocation_size = 0;
++
++                      t = talloc_asprintf(mem_ctx, "%s%s", fname, p);
++                      if (!t)
++                              return NULL;
++                      if (!SMB_VFS_STAT(conn, t ,&sbuf)) {
++                              listp->ads.size = get_file_size(sbuf);
++                              listp->ads.allocation_size = get_allocation_size(NULL,&sbuf);
++                      }
++                      /* FIXME get ride of this */
++                      {
++                      fstring dos_ads_name;
++                              
++                              push_ascii_fstring(dos_ads_name, listp->ads.name);
++                              *pads_total_len += strlen(dos_ads_name) + 1 + 24;
++                              DEBUG(10,("get_ads_list: total_len = %u, %s, size = %llu\n",
++                                              *pads_total_len, dos_ads_name, listp->ads.size ));
++                      }
++                      DLIST_ADD_END(ads_list_head, listp, tmp);
++              }
++      }
++
++      DEBUG(10,("get_ads_list: total_len = %u\n", *pads_total_len));
++      return ads_list_head;
++}
++
++/****************************************************************************
++ Fill a qfilepathinfo buffer with alternate streams. 
++ Returns the length of the buffer that was filled.
++****************************************************************************/
++
++static unsigned int fill_ads_buffer(char *pdata, unsigned int total_data_size,
++      connection_struct *conn, files_struct *fsp, const char *fname)
++{
++      unsigned int ret_data_size = 0;
++      char *p = pdata;
++      size_t total_ads_len;
++      TALLOC_CTX *mem_ctx;
++      struct ads_list *ads_list;
++
++      SMB_ASSERT(total_data_size >= 24);
++
++      mem_ctx = talloc_init("fill_ads_buffer");
++      if (!mem_ctx) {
++              return 0;
++      }
++
++      ads_list = get_ads_list(mem_ctx, conn, fsp, fname, &total_ads_len);
++      if (!ads_list) {
++              talloc_destroy(mem_ctx);
++              return 0;
++      }
++
++      if (total_ads_len > total_data_size) {
++              talloc_destroy(mem_ctx);
++              return 0;
++      }
++
++      for (p = pdata; ads_list; ads_list = ads_list->next) {
++#if 0
++              size_t dos_namelen;
++              fstring dos_ads_name;
++
++              push_ascii_fstring(dos_ads_name, ads_list->ads.name);
++              dos_namelen = strlen(dos_ads_name);
++              if (dos_namelen > 255 || dos_namelen == 0) {
++                      break;
++              }
++              if (dos_namelen + 24 > total_data_size) {
++                      break;
++              }
++#endif
++              /* We know we have room. */
++              size_t byte_len = dos_PutUniCode(p +24, ads_list->ads.name, -1, False);
++              size_t off = SMB_ROUNDUP(24 +byte_len, 8); 
++              
++              SIVAL(p,0,0); /* from ethereal next entry offset */
++              SIVAL(p,4, byte_len); /* Byte length of unicode string :filename:$DATA */
++              SOFF_T(p,8, ads_list->ads.size);
++              SOFF_T(p,16, ads_list->ads.allocation_size);
++              if (ads_list->next) {
++                  SIVAL(p,0, off);
++              }
++              else {
++                  /* don't pad the last one */
++                  off = 24 +byte_len;
++              }
++
++              total_data_size -= off;
++              p += off;
++      }
++
++      ret_data_size = PTR_DIFF(p, pdata);
++      DEBUG(10,("fill_ads_buffer: data_size = %u, total_ads_len = %u\n",
++                      ret_data_size, total_ads_len ));
++      talloc_destroy(mem_ctx);
++      return ret_data_size;
++}
++
++/****************************************************************************
+   Send the required number of replies back.
+   We assume all fields other than the data fields are
+   set correctly for the type of call.
+@@ -2653,7 +2806,7 @@
+                       data_size = 4;
+                       break;
+-#if 0
++#if 1
+               /*
+                * NT4 server just returns "invalid query" to this - if we try to answer
+                * it then NTws gets a BSOD! (tridge).
+@@ -2663,16 +2816,24 @@
+ #endif
+               case SMB_FILE_STREAM_INFORMATION:
+                       DEBUG(10,("call_trans2qfilepathinfo: SMB_FILE_STREAM_INFORMATION\n"));
+-                      if (mode & aDIR) {
+-                              data_size = 0;
+-                      } else {
+-                              size_t byte_len = dos_PutUniCode(pdata+24,"::$DATA", 0xE, False);
+-                              SIVAL(pdata,0,0); /* ??? */
+-                              SIVAL(pdata,4,byte_len); /* Byte length of unicode string ::$DATA */
+-                              SOFF_T(pdata,8,file_size);
+-                              SIVAL(pdata,16,allocation_size);
+-                              SIVAL(pdata,20,0); /* ??? */
+-                              data_size = 24 + byte_len;
++                      {
++                              size_t off;
++ 
++                              if (mode & aDIR) {
++                                      off = 0;
++                              } else {
++                                      size_t byte_len = dos_PutUniCode(pdata+24,"::$DATA", 0xE, False);
++                              
++                                      off = SMB_ROUNDUP(24 +byte_len, 8); /* FIXME or 8 ? */
++                                      SIVAL(pdata,0,0); /* from ethereal next entry offset */
++                                      SIVAL(pdata,4,byte_len); /* Byte length of unicode string ::$DATA */
++                                      SOFF_T(pdata,8,file_size);
++                                      SOFF_T(pdata,16,allocation_size);
++                              }
++                              if ((data_size = fill_ads_buffer(pdata +off, data_size, conn, fsp, fname))) {
++                                      SIVAL(pdata,0,off);
++                              }
++                              data_size += off;
+                       }
+                       break;
+Index: source/include/vfs_macros.h
+===================================================================
+--- source/include/vfs_macros.h        (revision 1473)
++++ source/include/vfs_macros.h        (working copy)
+@@ -119,6 +119,9 @@
+ #define SMB_VFS_LSETXATTR(conn,path,name,value,size,flags) ((conn)->vfs.ops.lsetxattr((conn)->vfs.handles.lsetxattr,(conn),(path),(name),(value),(size),(flags)))
+ #define SMB_VFS_FSETXATTR(fsp,fd,name,value,size,flags) ((fsp)->conn->vfs.ops.fsetxattr((fsp)->conn->vfs.handles.fsetxattr,(fsp),(fd),(name),(value),(size),(flags)))
++/* ADS operations. */
++#define SMB_VFS_LISTADS(conn,path,list,size) ((conn)->vfs.ops.listads((conn)->vfs.handles.listads,(conn),(path),(list),(size)))
++
+ /*******************************************************************
+  Don't access conn->vfs_opaque.ops directly!!!
+  Use this macros!
+@@ -217,6 +220,9 @@
+ #define SMB_VFS_OPAQUE_LSETXATTR(conn,path,name,value,size,flags) ((conn)->vfs_opaque.ops.lsetxattr((conn)->vfs_opaque.handles.lsetxattr,(conn),(path),(name),(value),(size),(flags)))
+ #define SMB_VFS_OPAQUE_FSETXATTR(fsp,fd,name,value,size,flags) ((fsp)->conn->vfs_opaque.ops.fsetxattr((fsp)->conn->vfs_opaque.handles.fsetxattr,(fsp),(fd),(name),(value),(size),(flags)))
++/* ADS operations. */
++#define SMB_VFS_OPAQUE_LISTADS(conn,path,list,size) ((conn)->vfs_opaque.ops.listads((conn)->vfs_opaque.handles.listads,(conn),(path),(list),(size)))
++
+ /*******************************************************************
+  Don't access handle->vfs_next.ops.* directly!!!
+  Use this macros!
+@@ -315,4 +321,7 @@
+ #define SMB_VFS_NEXT_LSETXATTR(handle,conn,path,name,value,size,flags) ((handle)->vfs_next.ops.lsetxattr((handle)->vfs_next.handles.lsetxattr,(conn),(path),(name),(value),(size),(flags)))
+ #define SMB_VFS_NEXT_FSETXATTR(handle,fsp,fd,name,value,size,flags) ((handle)->vfs_next.ops.fsetxattr((handle)->vfs_next.handles.fsetxattr,(fsp),(fd),(name),(value),(size),(flags)))
++/* ADS operations. */
++#define SMB_VFS_NEXT_LISTADS(handle,conn,path,list,size) ((handle)->vfs_next.ops.listads((handle)->vfs_next.handles.listads,(conn),(path),(list),(size)))
++
+ #endif /* _VFS_MACROS_H */
+Index: source/include/vfs.h
+===================================================================
+--- source/include/vfs.h       (revision 1473)
++++ source/include/vfs.h       (working copy)
+@@ -55,7 +55,8 @@
+ /* Changed to version 8 includes EA calls. JRA. */
+ /* Changed to version 9 to include the get_shadow_data call. --metze */
+ /* Changed to version 10 to include pread/pwrite calls. */
+-#define SMB_VFS_INTERFACE_VERSION 10
++/* Changed to version 11 to include alternate data streams. */
++#define SMB_VFS_INTERFACE_VERSION 11
+ /* to bug old modules witch are trying to compile with the old functions */
+@@ -185,6 +186,9 @@
+       SMB_VFS_OP_SETXATTR,
+       SMB_VFS_OP_LSETXATTR,
+       SMB_VFS_OP_FSETXATTR,
++      
++      /* alternate stream */
++      SMB_VFS_OP_LISTADS,
+       /* This should always be last enum value */
+       
+@@ -294,6 +298,9 @@
+               int (*lsetxattr)(struct vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name, const void *value, size_t size, int flags);
+               int (*fsetxattr)(struct vfs_handle_struct *handle, struct files_struct *fsp,int filedes, const char *name, const void *value, size_t size, int flags);
++              /* alternate stream operations. */
++              ssize_t (*listads)(struct vfs_handle_struct *handle, struct connection_struct *conn,const char *path, char *list, size_t size);
++
+       } ops;
+       struct vfs_handles_pointers {
+@@ -394,6 +401,8 @@
+               struct vfs_handle_struct *lsetxattr;
+               struct vfs_handle_struct *fsetxattr;
++              /* alternate stream operations. */
++              struct vfs_handle_struct *listads;
+       } handles;
+ };
+Index: source/include/smb.h
+===================================================================
+--- source/include/smb.h       (revision 1473)
++++ source/include/smb.h       (working copy)
+@@ -1703,6 +1703,12 @@
+       DATA_BLOB value;
+ };
++struct ads_struct {
++      SMB_BIG_UINT size;
++      SMB_BIG_UINT allocation_size;
++      char *name;
++};
++
+ /* EA names used internally in Samba. KEEP UP TO DATE with prohibited_ea_names in trans2.c !. */
+ #define SAMBA_POSIX_INHERITANCE_EA_NAME "user.SAMBA_PAI"
+ /* EA to use for DOS attributes */
diff --git a/contrib/patches/patch.samba.3.0a20 b/contrib/patches/patch.samba.3.0a20
new file mode 100644 (file)
index 0000000..6d84621
--- /dev/null
@@ -0,0 +1,407 @@
+diff -ur ../smb3.0a20.orig/source/include/smb.h ./source/include/smb.h
+--- ../smb3.0a20.orig/source/include/smb.h     Mon Jan  6 18:04:22 2003
++++ ./source/include/smb.h     Fri Jun  4 05:34:14 2004
+@@ -1652,4 +1652,10 @@
+ extern struct poptOption popt_common_debug[];
++struct ads_struct {
++      SMB_BIG_UINT size;
++      SMB_BIG_UINT allocation_size;
++      char *name;
++};
++
+ #endif /* _SMB_H */
+diff -ur ../smb3.0a20.orig/source/include/smb_macros.h ./source/include/smb_macros.h
+--- ../smb3.0a20.orig/source/include/smb_macros.h      Mon Jan  6 18:04:22 2003
++++ ./source/include/smb_macros.h      Fri Jun  4 18:24:20 2004
+@@ -291,4 +291,8 @@
+ #define vfs_chdir(conn,fname) ((conn)->vfs_ops.chdir((conn),fname))
++/*******************************************************************
++ A wrapper for vfs_listads().
++********************************************************************/
++#define vfs_listads(conn,path,list,size)  ((conn)->vfs_ops.listads((conn),(path),(list),(size))) 
+ #endif /* _SMB_MACROS_H */
+diff -ur ../smb3.0a20.orig/source/include/vfs.h ./source/include/vfs.h
+--- ../smb3.0a20.orig/source/include/vfs.h     Mon Jan  6 18:04:23 2003
++++ ./source/include/vfs.h     Fri Jun  4 16:30:14 2004
+@@ -45,7 +45,8 @@
+ /* Changed to version 3 for POSIX acl extensions. JRA. */
+ /* Changed to version 4 for cascaded VFS interface. Alexander Bokovoy. */
+ /* Changed to version 5 for sendfile addition. JRA. */
+-#define SMB_VFS_INTERFACE_VERSION 5
++/* Changed to version 11 to include alternate data streams. */
++#define SMB_VFS_INTERFACE_VERSION 11
+ /* Version of supported cascaded interface backward copmatibility.
+@@ -173,6 +174,9 @@
+       int (*sys_acl_free_text)(struct connection_struct *conn, char *text);
+       int (*sys_acl_free_acl)(struct connection_struct *conn, SMB_ACL_T posix_acl);
+       int (*sys_acl_free_qualifier)(struct connection_struct *conn, void *qualifier, SMB_ACL_TAG_T tagtype);
++
++      /* alternate stream operations. */
++      ssize_t (*listads)(/* struct vfs_handle_struct *handle, */ struct connection_struct *conn,const char *path, char *list, size_t size);
+ };
+ struct vfs_options {
+@@ -269,6 +273,9 @@
+       SMB_VFS_OP_SYS_ACL_FREE_ACL,
+       SMB_VFS_OP_SYS_ACL_FREE_QUALIFIER,
+       
++      /* alternate stream */
++      SMB_VFS_OP_LISTADS,
++
+       /* This should always be last enum value */
+       
+       SMB_VFS_OP_LAST
+diff -ur ../smb3.0a20.orig/source/smbd/nttrans.c ./source/smbd/nttrans.c
+--- ../smb3.0a20.orig/source/smbd/nttrans.c    Mon Jan  6 18:05:44 2003
++++ ./source/smbd/nttrans.c    Fri Jul  2 07:38:38 2004
+@@ -52,6 +52,8 @@
+       FILE_GENERIC_ALL
+ };
++#define SMB_VFS_LISTADS vfs_listads
++
+ /****************************************************************************
+  Send the required number of replies back.
+  We assume all fields other than the data fields are
+@@ -625,30 +627,40 @@
+                        * Check to see if this is a mac fork of some kind.
+                        */
+-                      if( strchr_m(fname, ':')) {
++                      if( !strchr_m(fname, ':')) {
++                              /* it's not an alternate stream */
++                              END_PROFILE(SMBntcreateX);
++                              return(ERROR_DOS(ERRDOS,ERRbadfid));
++                      }
++                      else if (-1 == SMB_VFS_LISTADS(conn, NULL, NULL, 0)) {
++                              /* fs have no support for alternate streams */
+                               END_PROFILE(SMBntcreateX);
+                               return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
+                       }
+-                      END_PROFILE(SMBntcreateX);
+-                      return(ERROR_DOS(ERRDOS,ERRbadfid));
+-              }
+-              /*
+-               * Copy in the base directory name.
+-               */
++                      /*
++                      * Copy in the base name.
++                      */
++                      pstrcpy( fname, dir_fsp->fsp_name );
++                      dir_name_len = strlen(fname);
++              }
++              else { /* it's a dir */
++                      /*
++                       * Copy in the base directory name.
++                       */
+-              pstrcpy( fname, dir_fsp->fsp_name );
+-              dir_name_len = strlen(fname);
++                      pstrcpy( fname, dir_fsp->fsp_name );
++                      dir_name_len = strlen(fname);
+-              /*
+-               * Ensure it ends in a '\'.
+-               */
+-
+-              if(fname[dir_name_len-1] != '\\' && fname[dir_name_len-1] != '/') {
+-                      pstrcat(fname, "\\");
+-                      dir_name_len++;
+-              }
++                      /*
++                       * Ensure it ends in a '\'.
++                       */
++                      if(fname[dir_name_len-1] != '\\' && fname[dir_name_len-1] != '/') {
++                              pstrcat(fname, "\\");
++                              dir_name_len++;
++                      }
++                }
+               srvstr_pull_buf(inbuf, &fname[dir_name_len], smb_buf(inbuf), sizeof(fname)-dir_name_len, STR_TERMINATE);
+       } else {
+               srvstr_pull_buf(inbuf, fname, smb_buf(inbuf), sizeof(fname), STR_TERMINATE);
+@@ -656,8 +668,7 @@
+               /* 
+                * Check to see if this is a mac fork of some kind.
+                */
+-
+-              if( strchr_m(fname, ':')) {
++              if( strchr_m(fname, ':') && -1 == SMB_VFS_LISTADS(conn, NULL, NULL, 0)) {
+                       END_PROFILE(SMBntcreateX);
+                       return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
+               }
+@@ -1138,12 +1149,10 @@
+                       srvstr_pull(inbuf, fname, params+53, sizeof(fname), total_parameter_count-53, STR_TERMINATE);
+                       /*
+-                       * Check to see if this is a mac fork of some kind.
++                       * Check to see if this is a mac fork of some kind. FIXME ADS
+                        */
+-
+-                      if( strchr_m(fname, ':'))
++                      if( strchr_m(fname, ':') && -1 == SMB_VFS_LISTADS(conn, NULL, NULL, 0))
+                               return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
+-
+                       return ERROR_DOS(ERRDOS,ERRbadfid);
+               }
+@@ -1172,7 +1181,7 @@
+                * Check to see if this is a mac fork of some kind.
+                */
+-              if( strchr_m(fname, ':'))
++              if( strchr_m(fname, ':')  && -1 == SMB_VFS_LISTADS(conn, NULL, NULL, 0))
+                       return ERROR_NT(NT_STATUS_OBJECT_PATH_NOT_FOUND);
+       }
+diff -ur ../smb3.0a20.orig/source/smbd/trans2.c ./source/smbd/trans2.c
+--- ../smb3.0a20.orig/source/smbd/trans2.c     Mon Jan  6 18:05:48 2003
++++ ./source/smbd/trans2.c     Thu Jul  1 03:06:42 2004
+@@ -50,6 +50,162 @@
+       return ret;
+ }
++#define SMB_VFS_STAT vfs_stat
++#define SMB_VFS_LISTADS vfs_listads
++
++/****************************************************************************
++ ****************************************************************************
++ Return a linked list of the alternate streams Plus the total size
++****************************************************************************/
++struct ads_list {
++      struct ads_list *next, *prev;
++      struct ads_struct ads;
++};
++
++static struct ads_list *get_ads_list(TALLOC_CTX *mem_ctx, connection_struct *conn, files_struct *fsp, const char *fname, size_t *pads_total_len)
++{
++      /* Get a list of all ads with size, lax namesize is 64k. */
++      size_t ads_namelist_size = 4096;
++      char *ads_namelist;
++      char *p;
++      ssize_t sizeret;
++      int i;
++      struct ads_list *ads_list_head = NULL;
++
++      *pads_total_len = 0;
++
++      DEBUG(10,("get_ads_list\n" ));
++      
++      for (i = 0, ads_namelist = talloc(mem_ctx, ads_namelist_size); i < 6;
++                      ads_namelist = talloc_realloc(mem_ctx, ads_namelist, ads_namelist_size), i++) {
++
++              sizeret = SMB_VFS_LISTADS(conn, fname, ads_namelist, ads_namelist_size);
++              if (sizeret == -1 && errno == ERANGE) {
++                      ads_namelist_size *= 2;
++              } else {
++                      break;
++              }
++      }
++
++      if (sizeret == -1)
++              return NULL;
++
++      DEBUG(10,("get_ads_list: ads_namelist size = %d\n", sizeret ));
++
++      if (sizeret) { 
++              for (p = ads_namelist; p - ads_namelist < sizeret; p += strlen(p) +1) {
++                      struct ads_list *listp, *tmp;
++                      SMB_STRUCT_STAT sbuf;
++                      char *t;
++                      
++                      listp = talloc(mem_ctx, sizeof(struct ads_list));
++                      if (!listp)
++                              return NULL;
++
++                      listp->ads.name = talloc_strdup(mem_ctx, p);
++                      if (!listp->ads.name)
++                              return NULL;
++                      
++                      listp->ads.size = 0;
++                      listp->ads.allocation_size = 0;
++
++                      t = talloc_asprintf(mem_ctx, "%s%s", fname, p);
++                      if (!t)
++                              return NULL;
++                      if (!SMB_VFS_STAT(conn, t ,&sbuf)) {
++                              listp->ads.size = get_file_size(sbuf);
++                              listp->ads.allocation_size = get_allocation_size(NULL,&sbuf);
++                      }
++                      /* FIXME get ride of this */
++                      {
++                      fstring dos_ads_name;
++                              
++                              push_ascii_fstring(dos_ads_name, listp->ads.name);
++                              *pads_total_len += strlen(dos_ads_name) + 1 + 24;
++                              DEBUG(10,("get_ads_list: total_len = %u, %s, size = %llu\n",
++                                              *pads_total_len, dos_ads_name, listp->ads.size ));
++                      }
++                      DLIST_ADD_END(ads_list_head, listp, tmp);
++              }
++      }
++
++      DEBUG(10,("get_ads_list: total_len = %u\n", *pads_total_len));
++      return ads_list_head;
++}
++
++/****************************************************************************
++ Fill a qfilepathinfo buffer with alternate streams. 
++ Returns the length of the buffer that was filled.
++****************************************************************************/
++
++static unsigned int fill_ads_buffer(char *pdata, unsigned int total_data_size,
++      connection_struct *conn, files_struct *fsp, const char *fname)
++{
++      unsigned int ret_data_size = 0;
++      char *p = pdata;
++      size_t total_ads_len;
++      TALLOC_CTX *mem_ctx;
++      struct ads_list *ads_list;
++
++      SMB_ASSERT(total_data_size >= 24);
++
++      mem_ctx = talloc_init(/*"fill_ads_buffer"*/);
++      if (!mem_ctx) {
++              return 0;
++      }
++
++      ads_list = get_ads_list(mem_ctx, conn, fsp, fname, &total_ads_len);
++      if (!ads_list) {
++              talloc_destroy(mem_ctx);
++              return 0;
++      }
++
++      if (total_ads_len > total_data_size) {
++              talloc_destroy(mem_ctx);
++              return 0;
++      }
++
++      for (p = pdata; ads_list; ads_list = ads_list->next) {
++#if 0
++              size_t dos_namelen;
++              fstring dos_ads_name;
++
++              push_ascii_fstring(dos_ads_name, ads_list->ads.name);
++              dos_namelen = strlen(dos_ads_name);
++              if (dos_namelen > 255 || dos_namelen == 0) {
++                      break;
++              }
++              if (dos_namelen + 24 > total_data_size) {
++                      break;
++              }
++#endif
++              /* We know we have room. */
++              size_t byte_len = dos_PutUniCode(p +24, ads_list->ads.name, -1, False);
++              size_t off = SMB_ROUNDUP(24 +byte_len, 8); 
++              
++              SIVAL(p,0,0); /* from ethereal next entry offset */
++              SIVAL(p,4, byte_len); /* Byte length of unicode string :filename:$DATA */
++              SOFF_T(p,8, ads_list->ads.size);
++              SOFF_T(p,16, ads_list->ads.allocation_size);
++              if (ads_list->next) {
++                  SIVAL(p,0, off);
++              }
++              else {
++                  /* don't pad the last one */
++                  off = 24 +byte_len;
++              }
++
++              total_data_size -= off;
++              p += off;
++      }
++
++      ret_data_size = PTR_DIFF(p, pdata);
++      DEBUG(10,("fill_ads_buffer: data_size = %u, total_ads_len = %u\n",
++                      ret_data_size, total_ads_len ));
++      talloc_destroy(mem_ctx);
++      return ret_data_size;
++}
++
+ /****************************************************************************
+   Send the required number of replies back.
+   We assume all fields other than the data fields are
+@@ -1934,7 +2090,7 @@
+                               break;
+                       }
+               
+-#if 0
++#if 1
+               /*
+                * NT4 server just returns "invalid query" to this - if we try to answer
+                * it then NTws gets a BSOD! (tridge).
+@@ -1943,16 +2099,24 @@
+               case SMB_QUERY_FILE_STREAM_INFO:
+ #endif
+               case SMB_FILE_STREAM_INFORMATION:
+-                      if (mode & aDIR) {
+-                              data_size = 0;
+-                      } else {
+-                              size_t byte_len = dos_PutUniCode(pdata+24,"::$DATA", 0xE, False);
+-                              SIVAL(pdata,0,0); /* ??? */
+-                              SIVAL(pdata,4,byte_len); /* Byte length of unicode string ::$DATA */
+-                              SOFF_T(pdata,8,file_size);
+-                              SIVAL(pdata,16,allocation_size);
+-                              SIVAL(pdata,20,0); /* ??? */
+-                              data_size = 24 + byte_len;
++                      {
++                              size_t off;
++
++                              if (mode & aDIR) {
++                                      off = 0;
++                              } else {
++                                      size_t byte_len = dos_PutUniCode(pdata+24,"::$DATA", 0xE, False);
++                              
++                                      off = SMB_ROUNDUP(24 +byte_len, 8); /* FIXME or 8 ? */
++                                      SIVAL(pdata,0,0); /* from ethereal next entry offset */
++                                      SIVAL(pdata,4,byte_len); /* Byte length of unicode string ::$DATA */
++                                      SOFF_T(pdata,8,file_size);
++                                      SOFF_T(pdata,16,allocation_size);
++                              }
++                              if ((data_size = fill_ads_buffer(pdata +off, data_size, conn, fsp, fname))) {
++                                      SIVAL(pdata,0,off);
++                              }
++                              data_size += off;
+                       }
+                       break;
+diff -ur ../smb3.0a20.orig/source/smbd/vfs-wrap.c ./source/smbd/vfs-wrap.c
+--- ../smb3.0a20.orig/source/smbd/vfs-wrap.c   Mon Jan  6 18:05:49 2003
++++ ./source/smbd/vfs-wrap.c   Fri Jun  4 16:33:16 2004
+@@ -739,3 +739,14 @@
+ {
+       return sys_acl_free_qualifier(qualifier, tagtype);
+ }
++
++/****************************************************************
++ Alternate stream operations.
++*****************************************************************/
++
++ssize_t vfswrap_listads(/* struct vfs_handle_struct *handle, */ struct connection_struct *conn,const char *path, char *list, size_t size)
++{
++        errno = ENOSYS;
++        return -1;
++}
++
+Only in ./source/smbd: vfs-wrap.c.orig
+diff -ur ../smb3.0a20.orig/source/smbd/vfs.c ./source/smbd/vfs.c
+--- ../smb3.0a20.orig/source/smbd/vfs.c        Mon Jan  6 18:05:48 2003
++++ ./source/smbd/vfs.c        Fri Jun  4 05:40:09 2004
+@@ -124,7 +124,10 @@
+       vfswrap_sys_acl_get_perm,
+       vfswrap_sys_acl_free_text,
+       vfswrap_sys_acl_free_acl,
+-      vfswrap_sys_acl_free_qualifier
++      vfswrap_sys_acl_free_qualifier,
++
++      /* alternate streams operations. */
++      vfswrap_listads
+ };
+ /****************************************************************************
diff --git a/contrib/patches/patch.vfs b/contrib/patches/patch.vfs
new file mode 100644 (file)
index 0000000..57c60f7
--- /dev/null
@@ -0,0 +1,1083 @@
+diff -Nur vfs/Makefile vfs.new/Makefile
+--- vfs/Makefile       Thu Jan  1 00:00:00 1970
++++ vfs.new/Makefile   Mon Jul 12 10:48:56 2004
+@@ -0,0 +1,40 @@
++##########################################################################
++# Makefile for Samba VFS modules 
++###########################################################################
++
++CC=gcc -g
++LIBTOOL=/usr/bin/libtool
++# REPLACE with samba source 
++SMB=/u/redhat/paris/cvs/samba/smb3.0a20
++
++# REPLACE with samba build folder
++BUILD=/mnt/hdd/build/smb.1.3
++
++CFLAGS=-Wall -I $(BUILD)/include \
++-I$(SMB)/source -I$(SMB)/source/include -I$(SMB)/source/ubiqx -I$(SMB)/source/smbwrapper
++
++
++LDFLAGS=-shared
++
++VFS_OBJS=vfs_ads.so
++
++SHELL=/bin/sh
++
++default: $(VFS_OBJS)
++
++# Pattern rules
++
++%.so: %.lo
++      @echo Linking $<
++      @$(LIBTOOL) --mode=link $(CC) -o $@ $< $(LDFLAGS)
++
++%.lo: %.c
++      @echo Compiling $<
++      @$(LIBTOOL) --mode=compile $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
++
++# Misc targets
++
++clean:
++      rm -rf .libs */.libs
++      rm -f core *~ *% *.bak *.o */*.o *.lo $(VFS_OBJS)
++
+diff -Nur vfs/README vfs.new/README
+--- vfs/README Thu Jan  1 00:00:00 1970
++++ vfs.new/README     Tue Jul 13 02:21:25 2004
+@@ -0,0 +1,34 @@
++This a vfs for NT ADS 
++you must set SMB and BUILD variables in Makefile.
++
++old smb.conf
++[test_ads]
++   comment = test ADS Mac/PC directory
++   path=/home/test_ads/
++#  /.AppleD* is mandatory 
++   veto files = /.AppleD*/Network Trash Folder/Icon\r/
++   delete veto files = True
++# full path to vfs_ads.so 
++   vfs object = /usr/src/samba/vfs/vfs_ads.so
++   browseable = yes
++   writable = yes
++
++new one (current svn tree)
++copy vfs_ads.so as ads.so in <prefix>/lib/vfs/
++eg
++cp vfs_ads.so /opt/lib/vfs/ads.so
++
++smb.conf
++[test_ads]
++   comment = test ADS Mac/PC directory
++   path=/home/test_ads/
++ 
++#  /.AppleD* is mandatory 
++   veto files = /.AppleD*/Network Trash Folder/Icon\r/
++   delete veto files = True
++   vfs objects = ads
++   browseable = yes
++   writable = yes
++
++
++Didier
+diff -Nur vfs/vfs_ads.c vfs.new/vfs_ads.c
+--- vfs/vfs_ads.c      Thu Jan  1 00:00:00 1970
++++ vfs.new/vfs_ads.c  Mon Jul 12 10:46:27 2004
+@@ -0,0 +1,997 @@
++/* 
++ * CAP VFS module for Samba 3.x Version 0.3
++ *
++ * Copyright (C) Tim Potter, 1999-2000
++ * Copyright (C) Alexander Bokovoy, 2002-2003
++ * Copyright (C) Stefan (metze) Metzmacher, 2003
++ * Copyright (C) TAKAHASHI Motonobu (monyo), 2003
++ *
++ * 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.
++ *
++ * modified for alternate data stream
++ * Copyright (C) Didier Gautheron 2004
++ * 
++ * this module should compile with old 3.0 API and 2004-07 svn API
++ */
++
++
++#include "includes.h"
++
++#undef DBGC_CLASS
++#define DBGC_CLASS DBGC_VFS
++
++#define ADS_FOLDER    ".AppleDouble"
++#define ADOUBLEMODE   0777
++
++/* FIXME found a better test */
++#ifdef        SMB_VFS_OP
++#define ADS_NEW_MODULE
++
++/* for current svn tree */
++#define ADS_TALLOC_INIT(a) talloc_init(a)
++
++#define HANDLE_PARAMETER vfs_handle_struct *handle,
++#define HANDLE handle,
++
++/* ------------------- */
++#else
++
++#define ADS_TALLOC_INIT(a) talloc_init()
++
++#define HANDLE_PARAMETER 
++#define HANDLE
++
++/* VFS operations */
++static struct vfs_ops default_vfs_ops;   /* For passthrough operation */
++static struct smb_vfs_handle_struct *ads_handle; 
++
++#define SMB_VFS_NEXT_DISK_FREE(a,b,c,d,e,f,g)       default_vfs_ops.disk_free(b,c,d,e,f,g)
++#define SMB_VFS_NEXT_OPENDIR(a,b,c)                 default_vfs_ops.opendir(b,c)
++#define SMB_VFS_NEXT_READDIR(a,b,c)                 default_vfs_ops.readdir(b,c)
++#define SMB_VFS_NEXT_MKDIR(a,b,c,d)                 default_vfs_ops.mkdir(b,c,d)
++#define SMB_VFS_NEXT_RMDIR(a,b,c)                   default_vfs_ops.rmdir(b,c)
++#define SMB_VFS_NEXT_OPEN(a,b,c,d,e)                default_vfs_ops.open(b,c,d,e)
++#define SMB_VFS_NEXT_RENAME(a,b,c,d)                default_vfs_ops.rename(b,c,d)
++#define SMB_VFS_NEXT_STAT(a,b,c,d)                  default_vfs_ops.stat(b,c,d)
++#define SMB_VFS_NEXT_LSTAT(a,b,c,d)                 default_vfs_ops.lstat(b,c,d)
++#define SMB_VFS_NEXT_UNLINK(a,b,c)                  default_vfs_ops.unlink(b,c)
++#define SMB_VFS_NEXT_CHMOD(a,b,c,d)                 default_vfs_ops.chmod(b,c,d)
++#define SMB_VFS_NEXT_CHOWN(a,b,c,d,e)               default_vfs_ops.chown(b,c,d,e) 
++#define SMB_VFS_NEXT_CHDIR(a,b,c)                   default_vfs_ops.chdir(b,c)
++#define SMB_VFS_NEXT_UTIME(a,b,c,d)                 default_vfs_ops.utime(b,c,d)
++#define SMB_VFS_NEXT_SYMLINK(a,b,c,d)               default_vfs_ops.symlink(b,c,d)
++#define SMB_VFS_NEXT_READLINK(a,b,c,d,e)            default_vfs_ops.readlink(b,c,d,e)
++#define SMB_VFS_NEXT_LINK(a,b,c,d)                  default_vfs_ops.link(b,c,d)
++#define SMB_VFS_NEXT_MKNOD(a,b,c,d,e)               default_vfs_ops.mknod(b,c,d,e)
++#define SMB_VFS_NEXT_REALPATH(a,b,c,d)              default_vfs_ops.realpath(b,c,d)
++#define SMB_VFS_NEXT_SET_NT_ACL(a,b,c,d,e)          default_vfs_ops.set_nt_acl(b,c,d,e)
++#define SMB_VFS_NEXT_CHMOD_ACL(a,b,c,d)             default_vfs_ops.chmod_acl(b,c,d)
++#define SMB_VFS_NEXT_SYS_ACL_GET_FILE(a,b,c,d)      default_vfs_ops.sys_acl_get_file(b,c,d)
++#define SMB_VFS_NEXT_SYS_ACL_SET_FILE(a,b,c,d,e)    default_vfs_ops.sys_acl_set_file(b,c,d,e)
++#define SMB_VFS_NEXT_SYS_ACL_DELETE_DEF_FILE(a,b,c) default_vfs_ops.sys_acl_delete_def_file(b,c)
++/* ads functions */
++
++#endif
++
++/* ------------------------- 
++ * format
++ * .AppleDouble/filename/stream name
++ * 
++ * return the *LAST* '/' in path
++ */
++static int ads_get_path_ptr(char *path)
++{
++      int i   = 0;
++      int ptr = 0;
++      
++      for (i = 0; path[i]; i ++) {
++              if (path[i] == '/')
++                      ptr = i;
++      }
++      
++      return ptr;
++}
++
++/* ------------------------------
++ * return the *FIRST* ':' in path
++*/
++static int ads_get_stream_ptr(const char *path)
++{
++      int i   = 0;
++      int ptr = 0;
++      
++      for (i = 0; path[i]; i ++) {
++              if (path[i] == ':') {
++                      ptr = i;
++                      break;
++              }
++      }
++      return ptr;
++}
++
++/* ---------------- 
++ * fname is only a filename
++*/
++
++static char *ads_canonical_dir(TALLOC_CTX *ctx, const char *path, const char *fname, int isdir)
++{
++    if (isdir) {
++        return talloc_asprintf(ctx, "%s/%s/%s/.Parent", path, fname, ADS_FOLDER);
++    }
++    return talloc_asprintf(ctx, "%s/%s/%s", path, ADS_FOLDER, fname);
++    
++}
++
++/* ---------------- 
++ * return directory pathname for an alternate data stream
++ * fname is *NOT* an altername name (ie foo:bar)
++*/
++static char *ads_dir(TALLOC_CTX *ctx, const char *path, const char *fname, int isdir)
++{
++    int ptr0 = 0;
++    int ptr1 = 0;
++    char *temp;
++
++#if 0
++    if (fname[0] == '.') ptr0 ++;
++    if (fname[1] == '/') ptr0 ++;
++#endif    
++    temp = talloc_asprintf(ctx, "%s/%s", path, &fname[ptr0]);
++    ptr1 = ads_get_path_ptr(temp);
++    temp[ptr1] = '\0';
++    return ads_canonical_dir(ctx, temp, &temp[ptr1 + 1], isdir);
++}
++
++/* ----------------------------------
++ * build the pathname for stream, create folder if (mode & O_CREAT)
++ * return -1 on error
++ * 0 it's not a stream
++ * 1 it's a stream
++ *
++ * main_path : file fullpathname with :$DATA removed
++ * ads_path: unix pathname 
++ * if it's not an ADS then main_path == ads_path
++ *
++ */
++static int ads_build_paths(TALLOC_CTX *ctx, const char *path, const char *fname,
++                              char **ads_path, char **main_path, SMB_STRUCT_STAT **main_info, int flag)
++{
++        int ret = 0;
++      int ptr0 = 0;
++      int ptr1 = 0;
++      int ptr2 = 0;
++      int ptr3 = 0;
++      char *dname = 0;
++      char *name  = 0;
++      SMB_STRUCT_STAT ads_info;
++
++      if (!ctx || !path || !fname || !ads_path || !main_path || !main_info || !*main_info)
++              return -1;
++#if 1
++      DEBUG(3, ("ADS: PATH: %s[%s]\n", path, fname));
++#endif
++      if (strstr(path, ADS_FOLDER) || strstr(fname, ADS_FOLDER)) {
++              DEBUG(1, ("ADS: path %s[%s] already contains %s\n", path, fname, ADS_FOLDER));
++              return -1;
++      }
++
++#if 0
++      if (fname[0] == '.') ptr0 ++;
++      if (fname[1] == '/') ptr0 ++;
++#endif
++
++      *main_path = talloc_asprintf(ctx, "%s/%s", path, &fname[ptr0]);
++      *ads_path = NULL;
++
++      /* get pointer to last '/' */
++      ptr1 = ads_get_path_ptr(*main_path);
++      ptr2 = ads_get_stream_ptr(*main_path +ptr1 +1);
++      /* FIXME
++       * what about ::$DATA or :name:$DATA
++      */
++
++      if (ptr2) {
++          /* it's an alternate stream */
++          ptr2 += ptr1 +1;
++          (*main_path)[ptr2] = 0;
++          ptr3 = ads_get_stream_ptr(*main_path +ptr2 +1);
++          if (ptr3) {
++              ptr3 += ptr2 +1;
++              /* check it's $DATA */
++              if (!strcmp("$DATA", &(*main_path)[ptr3+1])) {
++                  (*main_path)[ptr3] = 0;
++              }
++          }
++
++          DEBUG(3, ("ADS: MAIN DATA %s\n", *main_path));
++
++          if (sys_lstat(*main_path, *main_info) < 0) {
++              /* if we can't get the main file give up */
++              return -1;
++          }
++          (*main_path)[ptr2] = ':';
++          dname = talloc_strdup(ctx, *main_path);
++          dname[ptr1] = '\0'; 
++          name = *main_path;
++          name[ptr2] = '\0';
++          if (S_ISDIR((*main_info)->st_mode)) {
++              *ads_path = talloc_asprintf(ctx, "%s/%s/%s/.Parent/%s", dname, &name[ptr1 + 1], ADS_FOLDER, &name[ptr2 + 1]);
++          }
++          else {
++              *ads_path = talloc_asprintf(ctx, "%s/%s/%s/%s", dname, ADS_FOLDER, &name[ptr1 + 1], &name[ptr2 + 1]);
++          }
++          /* XXX are we always the right user ?*/
++          if (sys_lstat(*ads_path, &ads_info) < 0) {
++              int st_ret;
++              /* */
++              if (errno == ENOENT && (flag & O_CREAT))  {
++                  char *ads_base = ads_canonical_dir(ctx, dname, &name[ptr1 + 1], S_ISDIR((*main_info)->st_mode));
++                  mode_t mode;
++                  
++                  st_ret = mkdir(ads_base, 0777);
++                  if (st_ret < 0) {
++                      if (errno == ENOENT) {
++                          char *ads_double;
++                          if (S_ISDIR((*main_info)->st_mode)) {
++                              ads_double = talloc_asprintf(ctx, "%s/%s/%s", dname, &name[ptr1 + 1], ADS_FOLDER);
++                          }
++                          else {
++                              ads_double = talloc_asprintf(ctx, "%s/%s", dname, ADS_FOLDER);
++                          }
++                          if (mkdir(ads_double, 0777) < 0)
++                              return -1;
++                          if ((st_ret = mkdir(ads_base, 0777)) < 0)
++                              return -1;
++                          
++                          /* we just created .AppleDouble/file/ update mode with dir search 
++                           * XXX what about acl?
++                          */
++                          mode = (*main_info)->st_mode;
++                          if ((mode & (S_IRUSR | S_IWUSR )))
++                              mode |= S_IXUSR;
++                          if ((mode & (S_IRGRP | S_IWGRP )))
++                              mode |= S_IXGRP;
++                          if ((mode & (S_IROTH | S_IWOTH ))) 
++                              mode |= S_IXOTH;
++                          chmod(ads_base, mode);
++                      }
++                      else 
++                          errno = ENOENT;
++                  }
++              }
++              else 
++                  return -1;
++          }
++          ret = 1;
++      }
++      else {
++          *ads_path = *main_path;
++          if (sys_lstat(*main_path, *main_info) < 0) {
++              *main_info = NULL;
++          }
++      }
++#if 1
++      DEBUG(3, ("ADS: DEBUG:[%s] [%s]\n", *main_path, *ads_path)); 
++#endif
++      return ret;
++}
++
++/* ------------------------ */
++static SMB_BIG_UINT ads_disk_free(HANDLE_PARAMETER connection_struct *conn, const char *path,
++      BOOL small_query, SMB_BIG_UINT *bsize,
++      SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize)
++{
++      return SMB_VFS_NEXT_DISK_FREE(handle, conn, path, small_query, bsize, dfree, dsize);
++}
++
++static DIR *ads_opendir(HANDLE_PARAMETER connection_struct *conn, const char *fname)
++{
++      return SMB_VFS_NEXT_OPENDIR(handle, conn, fname);
++}
++
++static struct dirent *ads_readdir(HANDLE_PARAMETER connection_struct *conn, DIR *dirp)
++{
++        struct dirent *result;
++      DEBUG(3,("ads: ads_readdir\n"));
++      result = SMB_VFS_NEXT_READDIR(handle, conn, dirp);
++      if (result) {
++        DEBUG(3,("ads: ads_readdir: %s\n", result->d_name));
++        }
++        return result;
++}
++
++/* ------------------------- */
++static int ads_mkdir(HANDLE_PARAMETER connection_struct *conn, const char *path, mode_t mode)
++{
++      return SMB_VFS_NEXT_MKDIR(handle, conn, path, mode);
++}
++
++/* ------------------------- */
++static int unlink_file(const char *path)
++{
++int ret = 0;
++         
++      become_root();
++        ret = unlink(path);
++        unbecome_root();
++        return ret;
++}
++
++/* ------------------------- */
++static int unlink_folder(const char *path)
++{
++int ret = 0;
++         
++      become_root();
++        ret = rmdir(path);
++        unbecome_root();
++        return ret;
++}
++
++/* ------------------------- 
++   remove all files in an AppleDouble folder
++*/
++static void rrmdir(TALLOC_CTX *ctx, char *path)
++{
++        int n;
++        char *dpath;
++        struct dirent **namelist;
++ 
++        if (!path) return;
++ 
++        n = scandir(path, &namelist, 0, alphasort);
++        if (n < 0) {
++                return;
++        } 
++        while (n --) {
++              if (strcmp(namelist[n]->d_name, ".") == 0 || strcmp(namelist[n]->d_name, "..") == 0) {
++                      free(namelist[n]);
++                        continue;
++                }
++                if ((dpath = talloc_asprintf(ctx, "%s/%s",path, namelist[n]->d_name))) {
++                      unlink_file(dpath);
++                }
++                free(namelist[n]);
++        }
++        free(namelist);
++        unlink_folder(path);
++}
++
++/* --------------------------- */
++static void rrm_adsdir(TALLOC_CTX *ctx, char *path)
++{
++        int n;
++        char *dpath;
++        struct dirent **namelist;
++ 
++        if (!path) return;
++ 
++        n = scandir(path, &namelist, 0, alphasort);
++        if (n < 0) {
++                return;
++        } 
++        while (n --) {
++              if (strcmp(namelist[n]->d_name, ".") == 0 || strcmp(namelist[n]->d_name, "..") == 0) {
++                      free(namelist[n]);
++                        continue;
++                }
++                if ((dpath = talloc_asprintf(ctx, "%s/%s",path, namelist[n]->d_name))) {
++                      rrmdir(ctx, dpath);
++                }
++                free(namelist[n]);
++        }
++        free(namelist);
++        unlink_folder(path);
++}
++
++/* ------------------------- 
++ * XXX 
++ * if in smb.conf there's :
++ * delete veto files = True
++ * veto files = /.AppleD* /
++*/
++static int ads_rmdir( HANDLE_PARAMETER connection_struct *conn, const char *path)
++{
++        BOOL add = False;
++        TALLOC_CTX *ctx = 0;
++        char *dpath;
++        int  ret = 0;
++ 
++        if (!conn || !conn->origpath || !path) goto exit_rmdir;
++
++      /* .AppleD* */
++        strstr(path, ADS_FOLDER) ? (add = False) : (add = True);
++ 
++        if (!(ctx = ADS_TALLOC_INIT("ads_rmdir")))
++                goto exit_rmdir;
++ 
++        if (!(dpath = talloc_asprintf(ctx, "%s/%s%s",conn->origpath, path, add ? "/"ADS_FOLDER : "")))
++              goto exit_rmdir;
++              
++      /* remove folder .AppleDouble */
++        rrm_adsdir(ctx, dpath);
++ 
++exit_rmdir:
++      ret = SMB_VFS_NEXT_RMDIR(handle, conn, path);
++        talloc_destroy(ctx);
++
++      return ret;
++}
++
++/* ------------------------- */
++static int ads_open(HANDLE_PARAMETER connection_struct *conn, const char *fname, int flags, mode_t mode)
++{
++      int ret = 0;
++      char *ads_path = 0;
++      char *main_path = 0;
++      TALLOC_CTX *ctx;
++      SMB_STRUCT_STAT st;
++      SMB_STRUCT_STAT *main_info = &st;
++      
++      DEBUG(3,("ads: ads_open for %s %x\n", fname, flags));
++      if (!(ctx = ADS_TALLOC_INIT("ads_open")))
++              return -1;
++      /* convert to */
++      if (ads_build_paths(ctx, conn->origpath, fname, &ads_path, &main_path, &main_info, flags) < 0) {
++          talloc_destroy(ctx);
++          return -1;
++      }
++
++      ret = SMB_VFS_NEXT_OPEN(handle, conn, ads_path, flags, mode);
++      talloc_destroy(ctx);
++      return ret;
++
++}
++
++static int isDir(SMB_STRUCT_STAT *st)
++{
++     if (st == NULL) {
++         return 0;
++     }
++     return S_ISDIR(st->st_mode);
++}
++
++/* ------------------------- */
++static int ads_rename(HANDLE_PARAMETER connection_struct *conn, const char *old, const char *new)
++{
++      int ret = 0;
++      TALLOC_CTX *ctx;
++      char *ads_path = 0;
++      char *main_path = 0;
++      SMB_STRUCT_STAT st;
++      SMB_STRUCT_STAT *main_info = &st;
++      
++      DEBUG(3,("ads: ads_rename %s --> %sx\n", old, new));
++      
++      if (!(ctx = ADS_TALLOC_INIT("ads_rename")))
++          return -1;
++
++      if (ads_build_paths(ctx, conn->origpath, old, &ads_path, &main_path, &main_info, 0) < 0) {
++          talloc_destroy(ctx);
++          return -1;
++      }
++
++      if (ads_path != main_path) {
++          /* you can't rename an ads ! */
++          talloc_destroy(ctx);
++          errno = EINVAL;
++          return -1;
++      }
++
++      ret = SMB_VFS_NEXT_RENAME(handle, conn, old, new);
++      if (!ret && !isDir(main_info)) {
++          char *ads_old  = ads_dir(ctx, conn->origpath, old, 0);
++          char *ads_new  = ads_dir(ctx, conn->origpath, new, 0);
++          SMB_VFS_NEXT_RENAME(handle, conn, ads_old, ads_new);
++      }
++
++      talloc_destroy(ctx);
++      return ret;
++}
++
++/* ------------------------- 
++ * For an ADS what do we need to return , ADS ? main DATA?
++*/
++static int ads_stat(HANDLE_PARAMETER connection_struct *conn, const char *fname, SMB_STRUCT_STAT *sbuf)
++{
++      int ret = 0;
++      char *ads_path = 0;
++      char *main_path = 0;
++      TALLOC_CTX *ctx;
++      SMB_STRUCT_STAT st;
++      SMB_STRUCT_STAT *main_info = &st;
++
++      DEBUG(3,("ads: ads_stat for %s\n", fname));
++
++      if (!(ctx = ADS_TALLOC_INIT("ads_stat")))
++          return -1;
++      /* which inode ?
++      */
++      if (ads_build_paths(ctx, conn->origpath, fname, &ads_path, &main_path, &main_info, 0) < 0) {
++          talloc_destroy(ctx);
++          return -1;
++      }
++
++      ret = SMB_VFS_NEXT_STAT(handle, conn, ads_path, sbuf);
++      talloc_destroy(ctx);
++      return ret;
++}
++
++/* ------------------------- */
++static int ads_lstat(HANDLE_PARAMETER connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf)
++{
++      int ret = 0;
++      char *ads_path = 0;
++      char *main_path = 0;
++      TALLOC_CTX *ctx;
++      SMB_STRUCT_STAT st;
++      SMB_STRUCT_STAT *main_info = &st;
++      
++      if (!(ctx = ADS_TALLOC_INIT("ads_lstat")))
++              return -1;
++      /* which inode ?
++      */
++      if (ads_build_paths(ctx, conn->origpath, path, &ads_path, &main_path, &main_info, 0) < 0) {
++          talloc_destroy(ctx);
++          return -1;
++      }
++
++      return SMB_VFS_NEXT_LSTAT(handle, conn, ads_path, sbuf);
++      talloc_destroy(ctx);
++      return ret;
++}
++
++/* ------------------------- */
++static int ads_unlink(HANDLE_PARAMETER connection_struct *conn, const char *path)
++{
++      int ret = 0;
++
++      char *ads_path = 0;
++      char *main_path = 0;
++      TALLOC_CTX *ctx;
++      SMB_STRUCT_STAT st;
++      SMB_STRUCT_STAT *main_info = &st;
++      
++      DEBUG(3,("ads: ads_unlink %s\n", path));
++      if (!(ctx = ADS_TALLOC_INIT("ads_unlink")))
++              return -1;
++
++      if (ads_build_paths(ctx, conn->origpath, path, &ads_path, &main_path, &main_info, 0) < 0) {
++          talloc_destroy(ctx);
++          return -1;
++      }
++
++      ret = SMB_VFS_NEXT_UNLINK(handle, conn, ads_path);
++      /*
++         if data stream
++            for each stream
++                unlink
++      */
++      if (!ret && ads_path == main_path) {
++          char *ads_base  = ads_dir(ctx, conn->origpath, path, isDir(main_info));
++          struct dirent *dent = 0;
++          DIR *dir = opendir(ads_base);
++          
++          if (dir) {
++              char *dpath;
++              
++              while (NULL != (dent = readdir(dir))) {
++                  if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0)
++                      continue;
++                  if (!(dpath = talloc_asprintf(ctx, "%s/%s", ads_base, dent->d_name)))
++                      continue;
++                  /* XXX need to be root ?  */
++                  SMB_VFS_NEXT_UNLINK(handle, conn, dpath);               
++              }
++              closedir(dir);
++              rmdir(ads_base);
++          }
++      }
++
++      talloc_destroy(ctx);
++      return ret;
++}
++
++/* ------------------------- */
++static int ads_chmod(HANDLE_PARAMETER connection_struct *conn, const char *path, mode_t mode)
++{
++      int ret = 0;
++      char *ads_path = 0;
++      char *main_path = 0;
++      TALLOC_CTX *ctx;
++      SMB_STRUCT_STAT st;
++      SMB_STRUCT_STAT *main_info = &st;
++
++      DEBUG(3,("ads: ads_chmod %s\n", path));
++      /* if stream 
++             error ?, change only the stream
++      */
++      if (!(ctx = ADS_TALLOC_INIT("ads_chmod")))
++              return -1;
++
++      if (ads_build_paths(ctx, conn->origpath, path, &ads_path, &main_path, &main_info, 0) < 0) {
++          talloc_destroy(ctx);
++          return -1;
++      }
++
++      ret = SMB_VFS_NEXT_CHMOD(handle, conn, ads_path, mode);
++      /*
++         if data stream
++            for each stream
++                chmod
++      */
++      if (!ret && ads_path == main_path) {
++          char *ads_base  = ads_dir(ctx, conn->origpath, path, isDir(main_info));
++          struct dirent *dent = 0;
++          DIR *dir = opendir(ads_base);
++          
++          if (dir) {
++              char *dpath;
++              
++              while (NULL != (dent = readdir(dir))) {
++                  if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0)
++                      continue;
++                  if (!(dpath = talloc_asprintf(ctx, "%s/%s", ads_base, dent->d_name)))
++                      continue;
++                  /* XXX need to be root ? */
++                  SMB_VFS_NEXT_CHMOD(handle, conn, dpath, mode);
++              }
++              closedir(dir);
++              /* XXX need to change ads_base too*/
++          }
++      }
++
++      talloc_destroy(ctx);
++      return ret;
++}
++
++/* ------------------------- */
++static int ads_chown(HANDLE_PARAMETER connection_struct *conn, const char *path, uid_t uid, gid_t gid)
++{
++      int ret = 0;
++      char *ads_path = 0;
++      char *main_path = 0;
++      TALLOC_CTX *ctx;
++      SMB_STRUCT_STAT st;
++      SMB_STRUCT_STAT *main_info = &st;
++
++      DEBUG(3,("ads: ads_chown %s\n", path));
++      /* if stream 
++             error ?, change only the stream
++      */
++      if (!(ctx = ADS_TALLOC_INIT("ads_chown")))
++              return -1;
++
++      if (ads_build_paths(ctx, conn->origpath, path, &ads_path, &main_path, &main_info, 0) < 0) {
++          talloc_destroy(ctx);
++          return -1;
++      }
++
++      ret = SMB_VFS_NEXT_CHOWN(handle, conn, ads_path, uid, gid);
++      /* if data stream
++            for each stream
++                chmod
++      */
++      if (!ret && ads_path == main_path) {
++          char *ads_base  = ads_dir(ctx, conn->origpath, path, isDir(main_info));
++          struct dirent *dent = 0;
++          DIR *dir = opendir(ads_base);
++          
++          if (dir) {
++              char *dpath;
++              
++              while (NULL != (dent = readdir(dir))) {
++                  if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0)
++                      continue;
++                  if (!(dpath = talloc_asprintf(ctx, "%s/%s", ads_base, dent->d_name)))
++                      continue;
++                  /* XXX need to be root ?, what do we do in case of error? */
++                  SMB_VFS_NEXT_CHOWN(handle, conn, dpath, uid, gid);
++              }
++              closedir(dir);
++              SMB_VFS_NEXT_CHOWN(handle, conn, ads_path, uid, gid);
++          }
++      }
++
++      talloc_destroy(ctx);
++      return ret;
++}
++
++/* ------------------------- */
++static int ads_chdir(HANDLE_PARAMETER connection_struct *conn, const char *path)
++{
++      DEBUG(3,("ads: ads_chdir for %s\n", path));
++      return SMB_VFS_NEXT_CHDIR(handle, conn, path);
++}
++
++static int ads_utime(HANDLE_PARAMETER connection_struct *conn, const char *path, struct utimbuf *times)
++{
++      return SMB_VFS_NEXT_UTIME(handle, conn, path, times);
++}
++
++
++static BOOL ads_symlink(HANDLE_PARAMETER connection_struct *conn, const char *oldpath, const char *newpath)
++{
++      return SMB_VFS_NEXT_SYMLINK(handle, conn, oldpath, newpath);
++}
++
++static BOOL ads_readlink(HANDLE_PARAMETER connection_struct *conn, const char *path, char *buf, size_t bufsiz)
++{
++      return SMB_VFS_NEXT_READLINK(handle, conn, path, buf, bufsiz);
++}
++
++static int ads_link( HANDLE_PARAMETER connection_struct *conn, const char *oldpath, const char *newpath)
++{
++      return SMB_VFS_NEXT_LINK(handle, conn, oldpath, newpath);
++}
++
++static int ads_mknod(HANDLE_PARAMETER connection_struct *conn, const char *path, mode_t mode, SMB_DEV_T dev)
++{
++      return SMB_VFS_NEXT_MKNOD(handle, conn, path, mode, dev);
++}
++
++static char *ads_realpath(HANDLE_PARAMETER connection_struct *conn, const char *path, char *resolved_path)
++{
++      return SMB_VFS_NEXT_REALPATH(handle, conn, path, resolved_path);
++}
++
++static BOOL ads_set_nt_acl(HANDLE_PARAMETER files_struct *fsp, const char *name, uint32 security_info_sent, struct security_descriptor_info *psd)
++{
++      return SMB_VFS_NEXT_SET_NT_ACL(handle, fsp, name, security_info_sent, psd);
++}
++
++static int ads_chmod_acl(HANDLE_PARAMETER connection_struct *conn, const char *name, mode_t mode)
++{
++      /* If the underlying VFS doesn't have ACL support... */
++#ifdef ADS_NEW_MODULE
++        if (!handle->vfs_next.ops.chmod_acl) {                                                                          
++#else
++      if (!default_vfs_ops.chmod_acl) {
++#endif
++              errno = ENOSYS;
++              return -1;
++      }
++      return SMB_VFS_NEXT_CHMOD_ACL(handle, conn, name, mode);
++}
++
++static SMB_ACL_T ads_sys_acl_get_file(HANDLE_PARAMETER connection_struct *conn, const char *path_p, SMB_ACL_TYPE_T type)
++{
++      return SMB_VFS_NEXT_SYS_ACL_GET_FILE(handle, conn, path_p, type);
++}
++
++static int ads_sys_acl_set_file(HANDLE_PARAMETER connection_struct *conn, const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl)
++{
++      return SMB_VFS_NEXT_SYS_ACL_SET_FILE(handle, conn, name, acltype, theacl);
++}
++
++static int ads_sys_acl_delete_def_file(HANDLE_PARAMETER connection_struct *conn, const char *path)
++{
++      return SMB_VFS_NEXT_SYS_ACL_DELETE_DEF_FILE(handle, conn, path);
++}
++
++#ifdef        ADS_NEW_MODULE 
++static ssize_t ads_getxattr(vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name, void *value, size_t size)
++{
++        return SMB_VFS_NEXT_GETXATTR(handle, conn, path, name, value, size);
++}
++
++static ssize_t ads_lgetxattr(vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name, void *value, size_t
++size)
++{
++        return SMB_VFS_NEXT_LGETXATTR(handle, conn, path, name, value, size);
++}
++
++static ssize_t ads_fgetxattr(vfs_handle_struct *handle, struct files_struct *fsp,int fd, const char *name, void *value, size_t size)
++{
++        return SMB_VFS_NEXT_FGETXATTR(handle, fsp, fd, name, value, size);
++}
++
++static ssize_t ads_listxattr(vfs_handle_struct *handle, connection_struct *conn,const char *path, char *list, size_t size)
++{
++        return SMB_VFS_NEXT_LISTXATTR(handle, conn, path, list, size);
++}
++
++static ssize_t ads_llistxattr(vfs_handle_struct *handle,struct connection_struct *conn,const char *path, char *list, size_t size)
++{
++        return SMB_VFS_NEXT_LLISTXATTR(handle, conn, path, list, size);
++}
++
++static int ads_removexattr(vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name)
++{
++        return SMB_VFS_NEXT_REMOVEXATTR(handle, conn, path, name);
++}
++
++static int ads_lremovexattr(vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name)
++{
++        return SMB_VFS_NEXT_LREMOVEXATTR(handle, conn, path, name);
++}
++
++static int ads_fremovexattr(vfs_handle_struct *handle, struct files_struct *fsp,int fd, const char *name)
++{
++        return SMB_VFS_NEXT_FREMOVEXATTR(handle, fsp, fd, name);
++}
++
++static int ads_setxattr(vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name, const void *value, size_t size, int flags)
++{
++        return SMB_VFS_NEXT_SETXATTR(handle, conn, path, name, value, size, flags);
++}
++
++static int ads_lsetxattr(vfs_handle_struct *handle, struct connection_struct *conn,const char *path, const char *name, const void *value, size_t size, int flags)
++{
++        return SMB_VFS_NEXT_LSETXATTR(handle, conn, path, name, value, size, flags);
++}
++
++static int ads_fsetxattr(vfs_handle_struct *handle, struct files_struct *fsp,int fd, const char *name, const void *value, size_t size, int flags)
++{
++        return SMB_VFS_NEXT_FSETXATTR(handle, fsp, fd, name, value, size, flags);
++}
++
++#endif
++
++/* ---------------------------------- 
++ * enumerate 
++*/
++static ssize_t ads_listads(HANDLE_PARAMETER struct connection_struct *conn,const char *path, char *list, size_t size)
++{
++      char *ads_path = 0;
++      char *main_path = 0;
++      TALLOC_CTX *ctx;
++      size_t     len, total = 0;
++      SMB_STRUCT_STAT st;
++      SMB_STRUCT_STAT *main_info = &st;
++      
++
++      if (!list || !path) {
++              /* aka we have ads functionnality */
++              return 0;
++      }
++
++      DEBUG(3,("ads: ads_listads %s\n", path));
++
++      if (!(ctx = ADS_TALLOC_INIT("ads_listads")))
++              return -1;
++
++      if (ads_build_paths(ctx, conn->origpath, path, &ads_path, &main_path, &main_info, 0) < 0) {
++          talloc_destroy(ctx);
++          return -1;
++      }
++
++      /*
++         if data stream
++            for each stream
++      */
++      if (ads_path == main_path) {
++          char *ads_base  = ads_dir(ctx, conn->origpath, path, isDir(main_info));
++          struct dirent *dent = 0;
++          DIR *dir = opendir(ads_base);
++          
++          /* XXX need to be root ? */
++          if (dir) {
++              
++              while (NULL != (dent = readdir(dir))) {
++                  if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0)
++                      continue;
++                  len = strlen(dent->d_name) +8 ;
++                  total += len;
++                  if (total >= size) {
++                      talloc_destroy(ctx);
++                      errno = ERANGE;
++                      return -1;
++                  }
++                  snprintf (list, len, ":%s:$DATA", dent->d_name);
++                  list += len;
++              }
++              closedir(dir);
++          }
++      }
++
++      talloc_destroy(ctx);
++      return total;
++}
++
++/* ------------------------------------
++ * VFS operations structure */
++
++#ifndef SMB_VFS_OP
++#define SMB_VFS_OP(x) ((void *) x)  
++#endif
++
++static vfs_op_tuple ads_op_tuples[] = {
++
++      /* Disk operations */
++
++      {SMB_VFS_OP(ads_disk_free),                     SMB_VFS_OP_DISK_FREE,           SMB_VFS_LAYER_TRANSPARENT},
++      
++      /* Directory operations */
++
++      {SMB_VFS_OP(ads_opendir),                       SMB_VFS_OP_OPENDIR,             SMB_VFS_LAYER_TRANSPARENT},
++      {SMB_VFS_OP(ads_readdir),                       SMB_VFS_OP_READDIR,             SMB_VFS_LAYER_TRANSPARENT},
++      {SMB_VFS_OP(ads_mkdir),                         SMB_VFS_OP_MKDIR,               SMB_VFS_LAYER_TRANSPARENT},
++      {SMB_VFS_OP(ads_rmdir),                         SMB_VFS_OP_RMDIR,               SMB_VFS_LAYER_TRANSPARENT},
++
++      /* File operations */
++
++      {SMB_VFS_OP(ads_open),                          SMB_VFS_OP_OPEN,                SMB_VFS_LAYER_TRANSPARENT},
++      {SMB_VFS_OP(ads_rename),                        SMB_VFS_OP_RENAME,              SMB_VFS_LAYER_TRANSPARENT},
++      {SMB_VFS_OP(ads_stat),                          SMB_VFS_OP_STAT,                SMB_VFS_LAYER_TRANSPARENT},
++      {SMB_VFS_OP(ads_lstat),                         SMB_VFS_OP_LSTAT,               SMB_VFS_LAYER_TRANSPARENT},
++      {SMB_VFS_OP(ads_unlink),                        SMB_VFS_OP_UNLINK,              SMB_VFS_LAYER_TRANSPARENT},
++      {SMB_VFS_OP(ads_chmod),                         SMB_VFS_OP_CHMOD,               SMB_VFS_LAYER_TRANSPARENT},
++      {SMB_VFS_OP(ads_chown),                         SMB_VFS_OP_CHOWN,               SMB_VFS_LAYER_TRANSPARENT},
++      {SMB_VFS_OP(ads_chdir),                         SMB_VFS_OP_CHDIR,               SMB_VFS_LAYER_TRANSPARENT},
++      {SMB_VFS_OP(ads_utime),                         SMB_VFS_OP_UTIME,               SMB_VFS_LAYER_TRANSPARENT},
++      {SMB_VFS_OP(ads_symlink),                       SMB_VFS_OP_SYMLINK,             SMB_VFS_LAYER_TRANSPARENT},
++      {SMB_VFS_OP(ads_readlink),                      SMB_VFS_OP_READLINK,            SMB_VFS_LAYER_TRANSPARENT},
++      {SMB_VFS_OP(ads_link),                          SMB_VFS_OP_LINK,                SMB_VFS_LAYER_TRANSPARENT},
++      {SMB_VFS_OP(ads_mknod),                         SMB_VFS_OP_MKNOD,               SMB_VFS_LAYER_TRANSPARENT},
++      {SMB_VFS_OP(ads_realpath),                      SMB_VFS_OP_REALPATH,            SMB_VFS_LAYER_TRANSPARENT},
++
++      /* NT File ACL operations */
++
++      {SMB_VFS_OP(ads_set_nt_acl),                    SMB_VFS_OP_SET_NT_ACL,          SMB_VFS_LAYER_TRANSPARENT},
++
++      /* POSIX ACL operations */
++
++      {SMB_VFS_OP(ads_chmod_acl),                     SMB_VFS_OP_CHMOD_ACL,           SMB_VFS_LAYER_TRANSPARENT},
++
++      {SMB_VFS_OP(ads_sys_acl_get_file),              SMB_VFS_OP_SYS_ACL_GET_FILE,            SMB_VFS_LAYER_TRANSPARENT},
++      {SMB_VFS_OP(ads_sys_acl_set_file),              SMB_VFS_OP_SYS_ACL_SET_FILE,            SMB_VFS_LAYER_TRANSPARENT},
++      {SMB_VFS_OP(ads_sys_acl_delete_def_file),       SMB_VFS_OP_SYS_ACL_DELETE_DEF_FILE,     SMB_VFS_LAYER_TRANSPARENT},
++#ifdef        ADS_NEW_MODULE
++      /* EA operations. */
++      {SMB_VFS_OP(ads_getxattr),                      SMB_VFS_OP_GETXATTR,                    SMB_VFS_LAYER_TRANSPARENT},
++      {SMB_VFS_OP(ads_lgetxattr),                     SMB_VFS_OP_LGETXATTR,                   SMB_VFS_LAYER_TRANSPARENT},
++      {SMB_VFS_OP(ads_fgetxattr),                     SMB_VFS_OP_FGETXATTR,                   SMB_VFS_LAYER_TRANSPARENT},
++      {SMB_VFS_OP(ads_listxattr),                     SMB_VFS_OP_LISTXATTR,                   SMB_VFS_LAYER_TRANSPARENT},
++      {SMB_VFS_OP(ads_llistxattr),                    SMB_VFS_OP_LLISTXATTR,                  SMB_VFS_LAYER_TRANSPARENT},
++      {SMB_VFS_OP(ads_removexattr),                   SMB_VFS_OP_REMOVEXATTR,                 SMB_VFS_LAYER_TRANSPARENT},
++      {SMB_VFS_OP(ads_lremovexattr),                  SMB_VFS_OP_LREMOVEXATTR,                SMB_VFS_LAYER_TRANSPARENT},
++      {SMB_VFS_OP(ads_fremovexattr),                  SMB_VFS_OP_FREMOVEXATTR,                SMB_VFS_LAYER_TRANSPARENT},
++      {SMB_VFS_OP(ads_setxattr),                      SMB_VFS_OP_SETXATTR,                    SMB_VFS_LAYER_TRANSPARENT},
++      {SMB_VFS_OP(ads_lsetxattr),                     SMB_VFS_OP_LSETXATTR,                   SMB_VFS_LAYER_TRANSPARENT},
++      {SMB_VFS_OP(ads_fsetxattr),                     SMB_VFS_OP_FSETXATTR,                   SMB_VFS_LAYER_TRANSPARENT},
++#endif
++      /* ADS operations */
++      {SMB_VFS_OP(ads_listads),                       SMB_VFS_OP_LISTADS,                     SMB_VFS_LAYER_TRANSPARENT},
++
++      {NULL,                                          SMB_VFS_OP_NOOP,                        SMB_VFS_LAYER_NOOP}
++};
++
++#ifdef ADS_NEW_MODULE
++
++NTSTATUS vfs_ads_init(void)
++{
++        return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "ads", ads_op_tuples);
++}
++
++#else
++/* VFS initialisation function.  Return vfs_op_tuple array back to SAMBA. */
++vfs_op_tuple *vfs_init(int *vfs_version, struct vfs_ops *def_vfs_ops,struct smb_vfs_handle_struct *vfs_handle)
++{
++      *vfs_version = SMB_VFS_INTERFACE_VERSION;
++        memcpy(&default_vfs_ops, def_vfs_ops, sizeof(struct vfs_ops));
++                   
++        ads_handle = vfs_handle;
++        DEBUG(3, ("ADS: vfs module loaded\n"));
++        return ads_op_tuples;
++}
++                                             
++/* VFS finalization function. */
++void vfs_done(connection_struct *conn)
++{
++      DEBUG(3, ("ADS: vfs module unloaded\n"));
++}
++
++#endif