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