New VFS module for implementing NTFS streams support on Mac OS X. 3-0-25c/ntfs-streams
authorJames Peach <jpeach@apple.com>
Mon, 15 Oct 2007 22:00:17 +0000 (15:00 -0700)
committerJames Peach <jpeach@apple.com>
Mon, 15 Oct 2007 22:00:17 +0000 (15:00 -0700)
This is a VFS module that implements NTFS streams support on HFS-like
filesystems, using the resource fork, the Finder info attribute and
Darwin extended attributes to store the stream data.

There are some  associated changes in core Samba code:

    1. fcntl cannot be supported for streams that are faked on top of extended
    attributes. In this case, we alter the locking layer to silently ignore
    ENOTSUP errors from the VFS.

    2. The VFS open call can fail if we detect a conflicting share mode (from
    either SMB or AFP). In this case we do the deferred open dance. This change
    needs to be resolved against other mechanisms for doing system-wide open
    modes.

source/Makefile.in
source/configure.in
source/include/MacExtensions.h
source/locking/posix.c
source/modules/vfs_darwin_streams.c [new file with mode: 0644]
source/smbd/open.c

index ee6d6ed559ddc6321a66f892f3e254f8cd965846..d6e679350c75a7c3ec4b363303f37f273a7e4a74 100644 (file)
@@ -431,6 +431,7 @@ VFS_COMMIT_OBJ = modules/vfs_commit.o
 VFS_GPFS_OBJ = modules/vfs_gpfs.o modules/gpfs.o modules/nfs4_acls.o
 VFS_NOTIFY_FAM_OBJ = modules/vfs_notify_fam.o
 VFS_READAHEAD_OBJ = modules/vfs_readahead.o
+VFS_DARWIN_STREAMS_OBJ = modules/vfs_darwin_streams.o
 
 PLAINTEXT_AUTH_OBJ = auth/pampass.o auth/pass_check.o
 
@@ -1382,6 +1383,13 @@ bin/macosxfs.@SHLIBEXT@: $(CHARSET_MACOSXFS_OBJ)
 # NOTE, there is no build rule for a dynamic default VFS module because
 # this one MUST MUST MUST be built statically.
 
+# NOTE: This module will never build statically due to its extra dependencies
+bin/darwin_streams.@SHLIBEXT@: $(VFS_DARWIN_STREAMS_OBJ)
+       @echo "Building plugin $@"
+       @$(SHLD_MODULE) -F/System/Library/PrivateFrameworks \
+               $(VFS_DARWIN_STREAMS_OBJ) \
+               -framework ByteRangeLocking
+
 bin/audit.@SHLIBEXT@: proto_exists $(VFS_AUDIT_OBJ)
        @echo "Building plugin $@"
        @$(SHLD) $(LDSHFLAGS) -o $@ $(VFS_AUDIT_OBJ) \
@@ -1568,7 +1576,6 @@ bin/timelimit@EXEEXT@: script/tests/timelimit.o
 
 install: installservers installbin @INSTALL_CIFSMOUNT@ installman installscripts installdat installswat installmodules @INSTALL_LIBSMBCLIENT@ @INSTALL_LIBMSRPC@ @INSTALL_PAM_MODULES@ @INSTALL_LIBSMBSHAREMODES@
 
-
 install-everything: install installmodules
 
 # DESTDIR is used here to prevent packagers wasting their time
index 1d659f47c0f1f3f9ccb406270ca2306b3a24a74c..e171667d7ba086545e34f3d970735c2343734fb5 100644 (file)
@@ -814,8 +814,13 @@ main() {
        # in struct stat.
        CPPFLAGS="$CPPFLAGS -D_DARWIN_USE_64_BIT_INODE=1"
 
+       # This is needed to build against the BRLM framework. It should
+       # go away. -- jpeach
+       CPPFLAGS="$CPPFLAGS -F/System/Library/PrivateFrameworks"
+
 # Add a system specific charset module.
        default_shared_modules="$default_shared_modules charset_macosxfs"
+       default_shared_modules="$default_shared_modules vfs_darwin_streams"
 
        ;;
     *hurd*)
@@ -6158,6 +6163,7 @@ SMB_MODULE(vfs_prealloc, \$(VFS_PREALLOC_OBJ), "bin/prealloc.$SHLIBEXT", VFS)
 SMB_MODULE(vfs_commit, \$(VFS_COMMIT_OBJ), "bin/commit.$SHLIBEXT", VFS)
 SMB_MODULE(vfs_gpfs, \$(VFS_GPFS_OBJ), "bin/gpfs.$SHLIBEXT", VFS)
 SMB_MODULE(vfs_readahead, \$(VFS_READAHEAD_OBJ), "bin/readahead.$SHLIBEXT", VFS)
+SMB_MODULE(vfs_darwin_streams, \$(VFS_DARWIN_STREAMS_OBJ), "bin/darwin_streams.$SHLIBEXT", VFS)
 
 SMB_SUBSYSTEM(VFS,smbd/vfs.o)
 
index d09370ed9f7cf58b3bab54345c1982e005a1eab2..87fd06d9a765d04e2b1942a8662299df3bce7c79 100644 (file)
@@ -40,7 +40,7 @@
 /*
 ** NT's AFP_AfpInfo stream structure
 */
-#define APF_INFO_SIZE          0x3c            
+#define AFP_INFO_SIZE          0x3c
 #define AFP_Signature          0x41465000 
 #define AFP_Version                    0x00000100
 #define AFP_BackupTime         0x00000080
index dc54a3419417ba424ca6a2eb9e2826f2e6129bfc..bb09232823f67fc49e94e6396337150ab2e45c09 100644 (file)
@@ -190,6 +190,10 @@ static BOOL posix_fcntl_lock(files_struct *fsp, int op, SMB_OFF_T offset, SMB_OF
        DEBUG(8,("posix_fcntl_lock %d %d %.0f %.0f %d\n",fsp->fh->fd,op,(double)offset,(double)count,type));
 
        ret = SMB_VFS_LOCK(fsp,fsp->fh->fd,op,offset,count,type);
+       if (ret == False && errno == ENOTSUP) {
+               /* No locking available, our database is authoritative. */
+               return False;
+       }
 
        if (!ret && ((errno == EFBIG) || (errno == ENOLCK) || (errno ==  EINVAL))) {
 
@@ -235,6 +239,10 @@ static BOOL posix_fcntl_getlock(files_struct *fsp, SMB_OFF_T *poffset, SMB_OFF_T
                fsp->fh->fd,(double)*poffset,(double)*pcount,*ptype));
 
        ret = SMB_VFS_GETLOCK(fsp,fsp->fh->fd,poffset,pcount,ptype,&pid);
+       if (ret == False && errno == ENOTSUP) {
+               /* No locking available, our database is authoritative. */
+               return False;
+       }
 
        if (!ret && ((errno == EFBIG) || (errno == ENOLCK) || (errno ==  EINVAL))) {
 
@@ -1061,13 +1069,17 @@ BOOL set_posix_lock_windows_flavour(files_struct *fsp,
                DEBUG(5,("set_posix_lock_windows_flavour: Real lock: Type = %s: offset = %.0f, count = %.0f\n",
                        posix_lock_type_name(posix_lock_type), (double)offset, (double)count ));
 
-               if (!posix_fcntl_lock(fsp,SMB_F_SETLK,offset,count,posix_lock_type)) {
+               ret = posix_fcntl_lock(fsp,SMB_F_SETLK,offset,
+                       count,posix_lock_type);
+               if (ret == False && errno != ENOTSUP) {
                        *errno_ret = errno;
                        DEBUG(5,("set_posix_lock_windows_flavour: Lock fail !: Type = %s: offset = %.0f, count = %.0f. Errno = %s\n",
                                posix_lock_type_name(posix_lock_type), (double)offset, (double)count, strerror(errno) ));
                        ret = False;
                        break;
                }
+
+               ret = True;
        }
 
        if (!ret) {
@@ -1181,7 +1193,8 @@ BOOL release_posix_lock_windows_flavour(files_struct *fsp,
                DEBUG(5,("release_posix_lock_windows_flavour: downgrading lock to READ: offset = %.0f, count = %.0f\n",
                        (double)offset, (double)count ));
 
-               if (!posix_fcntl_lock(fsp,SMB_F_SETLK,offset,count,F_RDLCK)) {
+               ret = posix_fcntl_lock(fsp,SMB_F_SETLK,offset,count,F_RDLCK);
+               if (ret == False && errno != ENOTSUP) {
                        DEBUG(0,("release_posix_lock_windows_flavour: downgrade of lock failed with error %s !\n", strerror(errno) ));
                        talloc_destroy(ul_ctx);
                        return False;
@@ -1192,6 +1205,7 @@ BOOL release_posix_lock_windows_flavour(files_struct *fsp,
         * Release the POSIX locks on the list of ranges returned.
         */
 
+       ret = True;
        for(; ulist; ulist = ulist->next) {
                offset = ulist->start;
                count = ulist->size;
@@ -1200,7 +1214,9 @@ BOOL release_posix_lock_windows_flavour(files_struct *fsp,
                        (double)offset, (double)count ));
 
                if (!posix_fcntl_lock(fsp,SMB_F_SETLK,offset,count,F_UNLCK)) {
-                       ret = False;
+                       if (errno != ENOTSUP) {
+                               ret = False;
+                       }
                }
        }
 
@@ -1245,6 +1261,10 @@ BOOL set_posix_lock_posix_flavour(files_struct *fsp,
        }
 
        if (!posix_fcntl_lock(fsp,SMB_F_SETLK,offset,count,posix_lock_type)) {
+               if (errno == ENOTSUP) {
+                   return True;
+               }
+
                *errno_ret = errno;
                DEBUG(5,("set_posix_lock_posix_flavour: Lock fail !: Type = %s: offset = %.0f, count = %.0f. Errno = %s\n",
                        posix_lock_type_name(posix_lock_type), (double)offset, (double)count, strerror(errno) ));
@@ -1334,7 +1354,9 @@ BOOL release_posix_lock_posix_flavour(files_struct *fsp,
                        (double)offset, (double)count ));
 
                if (!posix_fcntl_lock(fsp,SMB_F_SETLK,offset,count,F_UNLCK)) {
-                       ret = False;
+                       if (errno != ENOTSUP) {
+                               ret = False;
+                       }
                }
        }
 
diff --git a/source/modules/vfs_darwin_streams.c b/source/modules/vfs_darwin_streams.c
new file mode 100644 (file)
index 0000000..bcb622b
--- /dev/null
@@ -0,0 +1,2171 @@
+/*
+ * Darwin ACL VFS module
+ *
+ * Copyright (C) James Peach 2006-2007
+ *
+ * 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.
+ */
+
+/* This header has to be here due to preprocessor conflicts with Samba
+ * headers.
+ */
+#include <sys/types.h>
+
+typedef enum BRLMForkType
+{
+       kBRLMDataFork = 1,
+       kBRLMResFork= 2
+} BRLMForkType;
+
+typedef enum BRLMLockType
+{
+       kBRLMFree = 0x0000,
+       kBRLMRLock = 0x0001,
+       kBRLMWLock=0x0002
+} BRLMLockType;
+
+typedef enum BRLMStatus {
+       BRLMNoErr = 0,
+       /*Errors in the database*/
+       BRLMInitErr = 30003,
+       BRLMDBOpenErr = 30013,
+       BRLMDBInvalidErr = 30023,
+       BRLMDBStaleErr =30043,
+       BRLMDBFullErr = 30053,
+       /*attempting to delete a record not cantained in a region*/
+       BRLMDBBoundsError = 30063,
+       NLMHashTableBoundaryErr = 30083,
+       /*any time an operation witha NLMRef fails*/
+       BRLMDBInvalidRefErr = 31013,
+       BRLMFileRecNotFound = 31023,
+       BRLMProcRecNotFound = 31033,
+       BRLMBRLRecNotFound = 31043,
+       BRLMFileRecAlreadyExists = 31053,
+       BRLMOpenDenied = 32013,
+       BRLMLockConflict = 32023,
+       /*Sytem Errors*/
+       BRLMParamErr = 35000,
+       BRLMMemErr = 35013,
+       BRLMSysErr = 35023,
+       BRLMMiscErr = 39993
+} BRLMStatus;
+
+typedef struct NLMData NLMData;
+typedef NLMData* NLMDataPtr;
+typedef NLMDataPtr BRLMRef;
+
+/*these map 1-1 with afp access permissions*/
+enum {
+       kBRLMRead =             0x01,
+       kBRLMWrite =            0x02,
+       kBRLMDenyRead =         0x10,
+       kBRLMDenyWrite =        0x20,
+       kBRLMAccessMask =       0x33
+};
+
+/* these constants are for the options flag in BRLMPosixOpen   */
+enum {
+       kBRLMOpenTruncate = 0x0001
+ };
+
+BRLMStatus BRLMInit(void);
+BRLMStatus BRLMClose(void);
+
+BRLMStatus BRLMPosixOpen(const u_int8_t* path,
+                        BRLMForkType forkType,
+                       int8_t openPermissions,
+                        int32_t creatPermissions,
+                        mode_t mode,
+                        BRLMRef* ref,
+                        u_int32_t sessionID,
+                       u_int32_t options);
+BRLMStatus BRLMCloseRef(BRLMRef ref);
+BRLMStatus BRLMByteRangeLock(const BRLMRef ref, BRLMLockType type,
+                       u_int64_t start, u_int64_t count);
+BRLMStatus BRLMByteRangeUnlock(const BRLMRef ref, BRLMLockType type,
+                       u_int64_t start, u_int64_t count);
+BRLMStatus BRLMCanRead(BRLMRef ref, u_int64_t offset, u_int64_t count);
+BRLMStatus BRLMCanWrite(BRLMRef ref, u_int64_t offset, u_int64_t count);
+
+int32_t BRLMGetDescriptor(BRLMRef ref);
+int16_t BRLMGetForkRefNum(BRLMRef ref);
+
+BRLMStatus NLMDebug(u_int32_t i);
+BRLMStatus BRLMExchange(BRLMRef ref1, BRLMRef ref2);
+
+#undef DEBUGLEVEL
+#define BOOL_DEFINED
+
+#include "includes.h"
+#include "talloc.h"
+#include "md5.h"
+#include "MacExtensions.h"
+
+#include <sys/xattr.h>
+#include <sys/attr.h>
+#include <sys/vnode.h>
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_VFS
+
+#define MODULE_NAME "darwin_streams"
+
+#define AFPINFO_STREAM_CANON           ":AFP_AfpInfo:$DATA"
+#define AFPRESOURCE_STREAM_CANON       ":AFP_Resource:$DATA"
+
+static int module_trace_level = 100;
+static int module_trace_brlm_level = 100;
+static int module_trace_sys_entry_level= 100;
+
+#define CANONICAL_CASE CASE_UPPER
+
+static char afp_info_name[sizeof(AFPINFO_STREAM)];
+static char afp_resource_name[sizeof(AFPRESOURCE_STREAM)];
+
+static int darwin_open_brlm(vfs_handle_struct *handle,
+               const char * fname,
+               struct files_struct * fsp,
+               BRLMForkType ftype,
+               int flags,
+               mode_t mode);
+
+#define MODULE_TRACE           module_trace_level
+#define MODULE_TRACE_BRLM      module_trace_brlm_level
+#define MODULE_TRACE_SYS_ENTRY module_trace_sys_entry_level
+
+#define TRACE_SYS_ENTRY() DEBUG(MODULE_TRACE_SYS_ENTRY, \
+       ("%s: entered %s\n", MODULE_NAME, __FUNCTION__))
+
+#define BRLM_CALL(expr, status) do { \
+               int sav; \
+               errno = 0; \
+               status = (expr); \
+               sav = errno; \
+               DEBUG(MODULE_TRACE_BRLM, \
+                       ("%s: %s gave result result=%d errno=%d\n", \
+                        MODULE_NAME, #expr, (int)status, sav)); \
+               errno = sav; \
+       } while (0);
+
+struct darwin_stream_io;
+
+typedef ssize_t (*darwin_stream_pread)(struct darwin_stream_io *sio,
+                       int fd, void *data, size_t count, SMB_OFF_T offset);
+
+typedef ssize_t (*darwin_stream_pwrite)(struct darwin_stream_io *sio,
+                       int fd, const void *data, size_t count, SMB_OFF_T offset);
+
+typedef int (*darwin_stream_ftrunc)(struct darwin_stream_io *sio,
+                       int fd, SMB_OFF_T len);
+
+typedef int (*darwin_stream_fstat)(struct darwin_stream_io *sio,
+                       int fd, SMB_STRUCT_STAT *sbuf);
+
+typedef BOOL (*darwin_stream_lock)(struct darwin_stream_io *sio,
+                       int fd, int op, SMB_OFF_T offset, SMB_OFF_T count,
+                       int type);
+
+typedef BOOL (*darwin_stream_getlock)(struct darwin_stream_io *sio,
+                       int fd, SMB_OFF_T *poffset, SMB_OFF_T *pcount,
+                       int *ptype, pid_t *ppid);
+
+typedef int (*darwin_stream_close)(struct darwin_stream_io *sio, int fd);
+
+struct darwin_stream_io
+{
+
+       union {
+               /* Extra info for streams built on xattrs. */
+               struct {
+                       char * sname;  /* stream (or xattr) name. */
+               } xattr;
+
+               /* Extra info for the primary data stream. */
+               struct {
+                       BRLMRef * bref;
+               } dfork;
+
+       } xtra;
+
+       darwin_stream_pread     pread;
+       darwin_stream_pwrite    pwrite;
+       darwin_stream_ftrunc    ftruncate;
+       darwin_stream_fstat     fstat;
+       darwin_stream_lock      lock;
+       darwin_stream_getlock   getlock;
+       darwin_stream_close     close;
+};
+
+typedef BOOL (*name_filter)(const char * name);
+
+static ssize_t enosys_ssize_t(void * unused)
+{
+       errno = ENOSYS;
+       return -1;
+}
+
+static void debug_attribute_names(int level, const char * list, size_t total)
+{
+       const char * name;
+       const char * end;
+
+       if (DEBUGLVL(level)) {
+               end = list + total;
+
+               for (name = list; name < end; ) {
+                       DEBUGADD(level, ("        %s\n", name));
+                       name += (strlen(name) + 1);
+               }
+       }
+}
+
+/* Map from a stream name to one of the special xattr names reserved by Apple.
+ * This assumes the stream name has been normalised by the caller.
+ */
+static const char * map_special_xattr_names(const char * sname)
+{
+       if (strcmp(sname, afp_resource_name) == 0) {
+               return XATTR_RESOURCEFORK_NAME;
+       } else if (strcmp(sname, afp_info_name) == 0) {
+               return XATTR_FINDERINFO_NAME;
+       } else {
+               return sname;
+       }
+}
+
+/* Take a buffer containing a sequence on NULL-terminated strings and remove
+ * any that are not allowed according to the filter function. The return is the
+ * amount of valid data remaining. The passed-in buffer is modified, but not
+ * grown or shrunk.
+ */
+static ssize_t filter_name_list(char * list, ssize_t total, name_filter allowed)
+{
+       char * name;
+       ssize_t newtotal = 0;
+       size_t xlen;
+       char * end = list + total;
+
+       if (!list) {
+               return 0;
+       }
+
+       for (name = list; name < end; ) {
+           xlen = strlen(name) + 1;
+
+           if (!allowed(name)) {
+                   size_t remain;
+                   /* Move the remainder of the buffer up to the last
+                    * unfiltered entry.
+                    */
+                   remain = end - (name + xlen);
+                   memmove(name, name + xlen, remain);
+                   /* Pull the end marker in by the amount we removed from the
+                    * middle.
+                    */
+                   end -= xlen;
+           } else {
+                   newtotal += xlen;
+                   name += xlen;
+           }
+
+       }
+
+       return newtotal;
+}
+
+static ssize_t get_xattr_size(const char * path, int fd,
+                               const char * name, int flags)
+{
+       ssize_t ret;
+
+       if (path) {
+               ret = getxattr(path, name, NULL, 0, 0, flags);
+       } else {
+               ret =  fgetxattr(fd, name, NULL, 0, 0, flags);
+       }
+
+       if (ret == -1 && errno == ENOATTR) {
+               /* XNU equates emptiness with nonexistence for these
+                * attributes. Make sure we don't get confused.
+                */
+               if (strcmp(name, XATTR_FINDERINFO_NAME) == 0 ||
+                   strcmp(name, XATTR_RESOURCEFORK_NAME) == 0) {
+                       errno = 0;
+                       return 0;
+               }
+       }
+
+       return ret;
+}
+
+/* ========================================================================
+   getxattr(2), setxattr(2) and listxattr(2) emulation.
+
+   These map directly to the Darwin extended attributes API, but
+   carefully avoid presenting any streams that were stored in xattrs.
+   ======================================================================== */
+
+static BOOL exclude_stream_names(const char * const name)
+{
+       /* A user stream masquerading as an xattr. */
+       if (*name == ':') {
+               return False;
+       }
+
+       /* Clients are supposed to use the stream name, not the xattr name. */
+       if (strcmp(name, XATTR_FINDERINFO_NAME) == 0) {
+               return False;
+       }
+
+       /* Clients are supposed to use the stream name, not the xattr name. */
+       if (strcmp(name, XATTR_RESOURCEFORK_NAME) == 0) {
+               return False;
+       }
+
+       return True;
+}
+
+static ssize_t do_list_xattr(const char * path, int fd,
+                   void * list, size_t max, int options)
+{
+       ssize_t total; /* total length of returned name */
+       ssize_t valid; /* length of valid xattr names */
+
+       if (path) {
+               total = listxattr(path, list, max, options);
+       } else {
+               total = flistxattr(fd, list, max, options);
+       }
+
+       /* Bail on error or if the caller is testing for the required
+        * buffer size.
+        */
+       if (total <= 0 || list == NULL) {
+               return total;
+       }
+
+       /* Filter out xattrs that are masquerading as streams. */
+       valid = filter_name_list(list, total, exclude_stream_names);
+
+       DEBUG(MODULE_TRACE,
+               ("%d bytes of xattr names filtered down to %d bytes\n",
+                total, valid));
+       return valid;
+}
+
+static ssize_t do_get_xattr(const char * path, int fd,
+                   const char * name, void * value, size_t size, int options)
+{
+       ssize_t len;
+
+       /* Don't let xattr clients look at any streams that we have
+        * stored in xattrs.
+        */
+       if (!exclude_stream_names(name)) {
+               errno = ENOATTR;
+               return -1;
+       }
+
+       if (path) {
+               len = getxattr(path, name, value, size, 0, options);
+       } else {
+               len = fgetxattr(fd, name, value, size, 0, options);
+       }
+
+       return len;
+}
+
+static ssize_t do_setxattr(const char * path, int fd,
+                   const char * name, void * value, size_t size, int options)
+{
+       ssize_t len;
+
+       /* Don't let xattr clients look at any streams that we have
+        * stored in xattrs.
+        */
+       if (!exclude_stream_names(name)) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (path) {
+               len = setxattr(path, name, value, size, 0, options);
+       } else {
+               len = fsetxattr(fd, name, value, size, 0, options);
+       }
+
+       return len;
+}
+
+static ssize_t list_existing_xattr(const char * path, int fd, char ** names)
+{
+       ssize_t sz;
+       size_t  buflen = 0;
+
+       *names = NULL;
+
+       if (path) {
+               sz = listxattr(path, NULL, 0, 0);
+       } else {
+               sz = flistxattr(fd, NULL, 0, 0);
+       }
+
+       if (sz <= 0) {
+               return sz;
+       }
+
+       buflen = sz;
+       *names = SMB_MALLOC(buflen);
+       if (*names == NULL) {
+               return -1;
+       }
+
+       for (;;)
+       {
+               if (path) {
+                       sz = listxattr(path, *names, buflen, 0);
+               } else {
+                       sz = flistxattr(fd, *names, buflen, 0);
+               }
+
+               switch (sz) {
+               case 0:
+                       SAFE_FREE(*names);
+                       return 0;
+
+               case -1:
+                       /* Oops, need to grow the name buffer ... */
+                       if (errno == ERANGE) {
+                               buflen *= 2;
+                               *names = SMB_REALLOC(*names, buflen);
+                               if (*names == NULL) {
+                                       return -1;
+                               }
+
+                               continue;
+                       }
+
+                       SAFE_FREE(*names);
+                       return -1;
+
+               default:
+                       return sz;
+               }
+
+       }
+
+       return -1;
+}
+
+static ssize_t get_existing_xattr(const char * xname, int fd,
+               void ** buf, size_t *bufsz)
+{
+       ssize_t sz = -1;
+
+       *buf = NULL;
+       *bufsz = 0;
+
+       do
+       {
+               *bufsz += 1024;
+               *buf = SMB_REALLOC(*buf, *bufsz);
+               if (*buf == NULL) {
+                       /* SMB_REALLOC frees on failure. */
+                       *bufsz = 0;
+                       errno = ENOMEM;
+                       return -1;
+               }
+
+               sz = fgetxattr(fd, xname, *buf, *bufsz, 0, 0);
+       } while (sz < 0 && errno == ERANGE);
+
+       if (sz < 0) {
+               int sav = errno;
+               SAFE_FREE(*buf);
+               *bufsz = 0;
+               errno = sav;
+               return -1;
+       }
+
+       DEBUG(MODULE_TRACE,
+               ("%s: xattr '%s' is %ld bytes in a %lu byte buffer\n",
+               MODULE_NAME, xname, (long)sz, (unsigned long)*bufsz));
+
+       return sz;
+}
+
+static int do_removexattr(const char * path, int fd, const char * name,
+                       int options)
+{
+       /* Don't let xattr clients look at any streams that we have
+        * stored in xattrs.
+        */
+       if (!exclude_stream_names(name)) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       if (path) {
+               return removexattr(path, name, options);
+       } else {
+               return fremovexattr(fd, name, options);
+       }
+}
+
+static ssize_t darwin_sys_listxattr(vfs_handle_struct *handle,
+                   const char *path, char *list, size_t size)
+{
+       return do_list_xattr(path, -1, list, size, 0);
+}
+
+static ssize_t darwin_sys_llistxattr(vfs_handle_struct *handle,
+                   const char *path, char *list, size_t size)
+{
+       return do_list_xattr(path, -1, list, size, XATTR_NOFOLLOW);
+}
+
+static ssize_t darwin_sys_flistxattr(vfs_handle_struct *handle,
+                   struct files_struct *fsp, int fd, char *list,
+                   size_t size)
+{
+       return do_list_xattr(NULL, fd, list, size, XATTR_NOFOLLOW);
+}
+
+static ssize_t darwin_sys_getxattr(vfs_handle_struct *handle,
+                   const char *path, char *name, void* value, size_t size)
+{
+       return do_get_xattr(path, -1, name, value, size, 0);
+}
+
+static ssize_t darwin_sys_lgetxattr(vfs_handle_struct *handle,
+                   const char *path, char *name, void* value, size_t size)
+{
+       return do_get_xattr(path, -1, name, value, size, XATTR_NOFOLLOW);
+}
+
+static ssize_t darwin_sys_fgetxattr(vfs_handle_struct *handle,
+                   struct files_struct *fsp, int fd,
+                   char *name, void *value, size_t size)
+{
+       return do_get_xattr(NULL, fd, name, value, size, 0);
+}
+
+static ssize_t darwin_sys_setxattr(vfs_handle_struct *handle,
+                   const char *path, char *name, void* value,
+                   size_t size, int flags)
+{
+       return do_setxattr(path, -1, name, value, size, flags);
+}
+
+static ssize_t darwin_sys_lsetxattr(vfs_handle_struct *handle,
+                   const char *path, char *name, void* value, size_t size,
+                   int flags)
+{
+       return do_setxattr(path, -1, name, value, size, flags | XATTR_NOFOLLOW);
+}
+
+static ssize_t darwin_sys_fsetxattr(vfs_handle_struct *handle,
+                   struct files_struct *fsp, int fd,
+                   char *name, void *value, size_t size, int flags)
+{
+       return do_setxattr(NULL, fd, name, value, size, flags);
+}
+
+static ssize_t darwin_sys_removexattr(vfs_handle_struct *handle,
+                   const char *path, char *name)
+{
+       return do_removexattr(path, -1, name, 0);
+}
+
+static ssize_t darwin_sys_lremovexattr(vfs_handle_struct *handle,
+                   const char *path, char *name)
+{
+       return do_removexattr(path, -1, name, XATTR_NOFOLLOW);
+}
+
+static ssize_t darwin_sys_fremovexattr(vfs_handle_struct *handle,
+                   struct files_struct *fsp, int fd, char *name)
+{
+       return do_removexattr(NULL, fd, name, 0);
+}
+
+/* ========================================================================
+   locking wrappers for xattrs, finder info and the resource fork.
+   ======================================================================== */
+
+/* FIXME: this should store POSIX-style locks in a TDB since we can't store
+ * then lock info in  the kernel.
+ */
+
+static BOOL darwin_getlock_ignore(struct darwin_stream_io *sio,
+                   int fd, SMB_OFF_T *poffset, SMB_OFF_T *pcount,
+                   int *ptype, pid_t *ppid)
+{
+       errno = ENOTSUP;
+       return False;
+}
+
+static BOOL darwin_lock_ignore(struct darwin_stream_io *sio,
+                   int fd, int op, SMB_OFF_T offset, SMB_OFF_T count,
+                   int type)
+{
+       errno = ENOTSUP;
+       return False;
+}
+
+static BOOL darwin_getlock_brlm(struct darwin_stream_io *sio,
+                   int fd, SMB_OFF_T *poffset, SMB_OFF_T *pcount,
+                   int *ptype, pid_t *ppid)
+{
+       BRLMRef bref = *sio->xtra.dfork.bref;
+       BRLMStatus bstatus;
+
+       if (bref == 0) {
+               errno = EINVAL;
+               return False;
+       }
+
+       BRLM_CALL(BRLMCanRead(bref, *poffset, *pcount), bstatus);
+       if (bstatus == BRLMLockConflict) {
+               /* If we can't read a range, someone must have a write
+                * lock on it.
+                */
+               *ptype = F_WRLCK;
+               return True;
+       }
+
+       BRLM_CALL(BRLMCanWrite(bref, *poffset, *pcount), bstatus);
+       if (bstatus == BRLMLockConflict) {
+               /* If we can read a range, but can't write it, someone must
+                * have a read lock in it.
+                */
+               *ptype = F_RDLCK;
+               return True;
+       }
+
+       if (bstatus == BRLMNoErr) {
+               *ptype = F_UNLCK;
+               return True;
+       }
+
+       /* BRLM sets errno for us. */
+       return False;
+}
+
+static BOOL darwin_lock_brlm(struct darwin_stream_io *sio,
+                   int fd, int op,
+                   SMB_OFF_T offset, SMB_OFF_T count,
+                   int type)
+{
+       BRLMRef bref = *sio->xtra.dfork.bref;
+       BRLMStatus bstatus;
+
+       if (bref == 0) {
+               errno = EINVAL;
+               return False;
+       }
+
+       if (type != F_RDLCK && type != F_WRLCK && type != F_UNLCK) {
+               DEBUG(0, ("%s: invalid lock type %d\n",
+                           MODULE_NAME, type));
+               errno = EINVAL;
+               return False;
+       }
+
+       SMB_ASSERT(op != SMB_F_GETLK);
+
+       if (op != SMB_F_SETLK && op != SMB_F_SETLKW) {
+               DEBUG(0, ("%s: invalid lock operation %d\n",
+                           MODULE_NAME, op));
+               errno = EINVAL;
+               return False;
+       }
+
+       switch (type) {
+       case F_RDLCK:
+               BRLM_CALL(BRLMByteRangeLock(bref, kBRLMRLock,
+                                       offset, count), bstatus);
+               break;
+       case F_WRLCK:
+               BRLM_CALL(BRLMByteRangeLock(bref, kBRLMWLock,
+                                       offset, count), bstatus);
+               break;
+       case F_UNLCK:
+              BRLM_CALL(BRLMByteRangeUnlock(bref, kBRLMFree,
+                                       offset, count), bstatus);
+              break;
+
+       default:
+               errno = EINVAL;
+               return False;
+       }
+
+       return (bstatus == BRLMNoErr) ? True : False;
+}
+
+/* ========================================================================
+   pwrite(2) wrappers for xattrs, finder info and the resource fork.
+   ======================================================================== */
+
+/* Write to an extended attribute as if it were a file stream. Unfortunately
+ * the position argument to the xattr APIs is ignored (except for the resouce
+ * fork). This means we have to read, update and copy.
+ */
+static ssize_t darwin_pwrite_xattr(struct darwin_stream_io *sio,
+                       int fd, const void *data,
+                       size_t count, SMB_OFF_T offset)
+{
+       ssize_t sz;
+       size_t  bufsz;
+       void *  buf;
+
+       sz = get_existing_xattr(sio->xtra.xattr.sname, fd, &buf, &bufsz);
+       if (sz < 0) {
+               return -1;
+       }
+
+       if ((offset + count) > bufsz) {
+               buf = SMB_REALLOC(buf, offset + count);
+               if (buf == NULL) {
+                       errno = ENOMEM;
+                       return -1;
+               }
+       }
+
+       memcpy((uint8_t *)buf + offset, data, count);
+       sz = fsetxattr(fd, sio->xtra.xattr.sname, buf, offset + count, 0, 0);
+       if (sz < 0) {
+               int errsav = errno;
+               SAFE_FREE(buf);
+               errno = errsav;
+               return -1;
+       }
+
+       SAFE_FREE(buf);
+       return count;
+}
+
+static ssize_t darwin_pwrite_finfo(struct darwin_stream_io *sio,
+                       int fd, const void *data,
+                       size_t count, SMB_OFF_T offset)
+{
+       AfpInfo afpi;
+       int ret;
+
+       /* Additional restrictions on writing the finder info:
+        *      1. you have to write from the beginning
+        *      2. you have to write all of it
+        */
+       if (offset != 0) {
+               errno = ENOTSUP;
+               return -1;
+       }
+
+       if (count < AFP_INFO_SIZE) {
+               errno = ENOTSUP;
+               return -1;
+       }
+
+       /*
+        uint32         afpi_Signature;
+        uint32         afpi_Version;
+        uint32         afpi_Reserved1;
+        uint32         afpi_BackupTime;
+        unsigned char  afpi_FinderInfo[AFP_FinderSize];
+        unsigned char  afpi_ProDosInfo[6];
+        unsigned char  afpi_Reserved2[6];
+        */
+       afpi.afpi_Signature = RIVAL(data, offset);
+       offset += 4; /* uint32  afpi_Signature */
+       afpi.afpi_Version = RIVAL(data, offset);
+       offset += 4; /* uint32  afpi_Version */
+       afpi.afpi_Reserved1 = RIVAL(data, offset);
+       offset += 4; /* uint32  afpi_Reserved1 */
+       afpi.afpi_BackupTime = RIVAL(data, offset);
+       offset += 4; /* uint32  afpi_BackupTime */
+       memcpy(afpi.afpi_FinderInfo, (uint8_t *)data + offset, AFP_FinderSize);
+       offset += AFP_FinderSize; /* 32 bytes */
+       memcpy(afpi.afpi_ProDosInfo, (uint8_t *)data + offset, 6);
+       offset += 6;
+       memcpy(afpi.afpi_Reserved2, (uint8_t *)data + offset, 6);
+       offset += 6;
+       /*
+        * On success, 0 is returned.  On failure, -1 is returned and the global
+     * variable errno is set as follows.
+        */
+       ret = fsetxattr(fd, XATTR_FINDERINFO_NAME,
+               afpi.afpi_FinderInfo, sizeof(afpi.afpi_FinderInfo), 0, 0);
+       if (ret < 0) {
+               return -1;
+       }
+
+       /*
+        * Pretend we wrote everything they request. We are only writing
+        * the finder info anyways. VISTA will write 512 and then set the
+        * eof to AFP_INFO_SIZE (60 bytes).
+        */
+       return count;
+}
+
+/* ========================================================================
+   pread(2) wrappers for xattrs, finder info and the resource fork.
+   ======================================================================== */
+
+static ssize_t darwin_pread_xattr(struct darwin_stream_io *sio,
+                       int fd, void *data,
+                       size_t count, SMB_OFF_T offset)
+{
+       ssize_t sz;
+       size_t  bufsz;
+       size_t  overlap;
+       void *  buf;
+
+       sz = get_existing_xattr(sio->xtra.xattr.sname, fd, &buf, &bufsz);
+       if (sz < 0) {
+               return -1;
+       }
+
+       /* Attempt to read past EOF. */
+       if (sz <= offset) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       overlap = (offset + count) > sz ? (sz - offset) : count;
+       memcpy(data, (uint8 *)buf + offset, overlap);
+
+       SAFE_FREE(buf);
+       return overlap;
+}
+
+static ssize_t darwin_pread_finfo(struct darwin_stream_io *sio,
+                       int fd, void *data,
+                       size_t count, SMB_OFF_T offset)
+{
+       AfpInfo afpi;
+       size_t  bufsz;
+       void *  buf;
+       int     ret;
+
+       /* Additional restrictions on reading the finder info:
+        *      1. We only allow complete reads of the data.
+        *      2. You can read pass the eof, but you cannot do a partial read of the data.
+        *      3. If you are reading from the begining then you must read all the data.
+        *
+        * VISTA will attempt a 512 read. I traced this between Vista and Windows 2000. We
+        * now do the samething as the Windows 2000 server. We treat it like any other attempt
+        * to read pass the eof.
+        */
+       if (offset != 0) {
+               if (offset >= AFP_INFO_SIZE)
+                   return 0;
+               /* Trying to do a partial read not allowed */
+               errno = ENOTSUP;
+               return -1;
+       }
+
+       if (count <  AFP_INFO_SIZE) {
+               errno = ENOTSUP;
+               return -1;
+       }
+
+       ret = get_existing_xattr(XATTR_FINDERINFO_NAME, fd, &buf, &bufsz);
+       if (ret < 0) {
+               ret = 0;
+       }
+
+       if (ret != 0 && ret != AFP_FinderSize) {
+               DEBUG(0, ("%s: expected %s to be %d bytes, "
+                           "but found %d bytes\n",
+                       MODULE_NAME, XATTR_FINDERINFO_NAME,
+                       (int)AFP_FinderSize, (int)ret));
+               SAFE_FREE(buf);
+               errno = EINVAL;
+               return -1;
+       }
+
+       /* FinderInfo is not the same structure as AFPInfo. Need to
+        * translate it. If the fgetxattr failed or there was no Finder info,
+        * we return an empty Finder info. According to comments in XNU, this
+        * is conventionally equivalent to not Finder info.
+        */
+       ZERO_STRUCT(afpi);
+       /*
+        uint32         afpi_Signature;
+        uint32         afpi_Version;
+        uint32         afpi_Reserved1;
+        uint32         afpi_BackupTime;
+        unsigned char  afpi_FinderInfo[AFP_FinderSize];
+        unsigned char  afpi_ProDosInfo[6];
+        unsigned char  afpi_Reserved2[6];
+        */
+       afpi.afpi_Signature = AFP_Signature; /* "AFP\0" */
+       afpi.afpi_Version = AFP_Version;
+
+       if (ret == AFP_FinderSize) {
+               memcpy(afpi.afpi_FinderInfo, buf,
+                       sizeof(afpi.afpi_FinderInfo));
+       }
+
+       /*
+        * The above code makes sure that offset starts at zero. I know its
+        * crazy, but the afpinfo header is in big endian alway.
+        */
+       RSIVAL(data, offset, afpi.afpi_Signature);
+       offset += 4; /* uint32  afpi_Signature */
+       RSIVAL(data, offset, afpi.afpi_Version);
+       offset += 4; /* uint32  afpi_Version */
+       RSIVAL(data, offset, afpi.afpi_Reserved1);
+       offset += 4; /* uint32  afpi_Reserved1 */
+       RSIVAL(data, offset, afpi.afpi_BackupTime);
+       offset += 4; /* uint32  afpi_BackupTime */
+       memcpy((uint8_t *)data + offset, afpi.afpi_FinderInfo, AFP_FinderSize);
+       offset += AFP_FinderSize; /* 32 bytes */
+       memcpy((uint8_t *)data + offset, afpi.afpi_ProDosInfo, 6);
+       offset += 6;
+       memcpy((uint8_t *)data + offset, afpi.afpi_Reserved2, 6);
+       offset += 6;
+
+       SAFE_FREE(buf);
+       return AFP_INFO_SIZE;
+}
+
+/* ========================================================================
+   ftruncate(2) wrappers for xattrs, finder info and the resource fork.
+   ======================================================================== */
+
+static int darwin_ftruncate_xattr(struct darwin_stream_io *sio,
+                               int fd, SMB_OFF_T len)
+{
+       char null = '\0';
+
+       /* This is not exactly truncating, but it's as close as we can get
+        * without being able to pass a zero length to setxattr.
+        */
+       return fsetxattr(fd, sio->xtra.xattr.sname, &null, 1, 0, 0);
+}
+
+static int darwin_ftruncate_finfo(struct darwin_stream_io *sio,
+                               int fd, SMB_OFF_T len)
+{
+       char null[AFP_FinderSize] = {0};
+
+
+       /* This is not exactly truncating, but it's as close as we can get
+        * without being able to pass a zero length to setxattr.
+        */
+       return fsetxattr(fd, sio->xtra.xattr.sname, &null, sizeof(null), 0, 0);
+}
+
+/* ========================================================================
+   fstat(2) wrappers for xattrs, finder info and the resource fork.
+   ======================================================================== */
+
+static ino_t stream_inode(const SMB_STRUCT_STAT * sbuf, const char * sname)
+{
+       struct MD5Context ctx;
+       unsigned char hash[16];
+
+       MD5Init(&ctx);
+       MD5Update(&ctx, (unsigned char *)&(sbuf->st_dev), sizeof(sbuf->st_dev));
+       MD5Update(&ctx, (unsigned char *)&(sbuf->st_ino), sizeof(sbuf->st_ino));
+       MD5Update(&ctx, (unsigned char *)sname, strlen(sname));
+       MD5Final(hash, &ctx);
+
+       DEBUG(MODULE_TRACE,
+               ("mapped st_ino=%u, st_dev=%u, sname='%s' to inode %u\n",
+               (unsigned)sbuf->st_ino, (unsigned)sbuf->st_dev,
+               sname, (unsigned)(*(ino_t *)hash) ));
+
+       /* Hopefully all the variation is in the lower 4 (or 8) bytes! */
+       return *(ino_t *)hash;
+}
+
+static int darwin_fstat_rsrc(struct darwin_stream_io *sio,
+                       int fd, SMB_STRUCT_STAT *sbuf)
+{
+       if (sys_fstat(fd, sbuf) == -1) {
+               return -1;
+       }
+
+       /* The resource fork shares an inode number with the data fork. We
+        * need to fake up a distinct inode so that the locking layer can
+        * lock the both forks separately.
+        */
+       sbuf->st_ino = stream_inode(sbuf, "/..namedfork/rsrc");
+
+       DEBUG(MODULE_TRACE,
+               ("rsrc st_size=%u, st_blocks=%u, st_ino=%u\n",
+               sbuf->st_size, sbuf->st_blocks, sbuf->st_ino));
+       return 0;
+}
+
+static int darwin_fstat_xattr(struct darwin_stream_io *sio,
+                       int fd, SMB_STRUCT_STAT *sbuf)
+{
+       if (sys_fstat(fd, sbuf) == -1) {
+               return -1;
+       }
+
+       DEBUG(MODULE_TRACE,
+               ("stream '%s' st_size=%u, st_blocks=%u, st_ino=%u\n",
+               sio->xtra.xattr.sname, sbuf->st_size,
+               sbuf->st_blocks, sbuf->st_ino));
+
+       sbuf->st_size = get_xattr_size(NULL, fd, sio->xtra.xattr.sname, 0);
+       if (sbuf->st_size == -1) {
+               return -1;
+       }
+
+       /* We are pretending that this xattr contains the AFP_AfpInfo stream,
+        * but it really only contains the finder info field. Touch up the size
+        * to preserve the illusion.
+        */
+       if (strcmp(sio->xtra.xattr.sname, XATTR_FINDERINFO_NAME) == 0 &&
+           sbuf->st_size == AFP_FinderSize) {
+               sbuf->st_size = AFP_INFO_SIZE;
+       }
+
+       /* Touch up st_blocks based on st_size. This overcounts when st_size is
+        * an even number of blocks. It doesn't matter.
+        */
+       sbuf->st_blocks = sbuf->st_size % STAT_ST_BLOCKSIZE + 1;
+
+       /* Try to generate a unique inode number for each stream. Relies on
+        * stream name normalisation and a bit of luck.
+        */
+       sbuf->st_ino = stream_inode(sbuf, sio->xtra.xattr.sname);
+
+       /* Make sure that the stream appears to be a regular file,
+        * irrespective of its underlying type.
+        */
+       sbuf->st_mode &= ~S_IFMT;
+       sbuf->st_mode |= S_IFREG;
+
+       DEBUG(MODULE_TRACE,
+               ("stream '%s' st_size=%u, st_blocks=%u, st_ino=%u\n",
+               sio->xtra.xattr.sname, sbuf->st_size,
+               sbuf->st_blocks, sbuf->st_ino));
+       return 0;
+}
+
+/* ========================================================================
+   close(2) wrappers.
+   ======================================================================== */
+
+static int darwin_close_brlm(struct darwin_stream_io *sio, int fd)
+{
+       BRLMStatus bstatus;
+       BRLMRef * bref = sio->xtra.dfork.bref;
+
+       /* Note that the fd that is passed here is not guaranteed to be the
+        * one associated with this file descriptor. It is guaranteed to
+        * be for the same file (which means that it was opened by the BRLM
+        * framework at some point).
+
+        */
+
+       if (*bref != 0) {
+               BRLM_CALL(BRLMCloseRef(*bref), bstatus);
+               *bref = 0;
+       }
+
+       return 0;
+}
+
+static int brlm_handle_destructor(void * mem_ctx)
+{
+       BRLMStatus bstatus;
+       BRLMRef * bref = (BRLMRef *)mem_ctx;
+
+       if (*bref != 0) {
+               BRLM_CALL(BRLMCloseRef(*bref), bstatus);
+               *bref = 0;
+       }
+
+       return 0;
+}
+
+/* ========================================================================
+   open(2) wrappers for BRLM, xattrs, finder info and the resource fork.
+   ======================================================================== */
+
+static int darwin_open_xattr(vfs_handle_struct *handle,
+               const char * fname,
+               const char * sname,
+               struct files_struct * fsp,
+               int flags,
+               mode_t mode)
+{
+       ssize_t ret;
+       int     baseflags;
+       struct darwin_stream_io *sio = NULL;
+       BOOL    xattr_is_present = True;
+       int     hostfd;
+
+       DEBUG(MODULE_TRACE, ("xattr method: fname='%s' sname='%s'\n",
+                   fname, sname ? sname : ""));
+
+       /* We use baseflags to turn off nasty side-effects when opening the
+        * underlying file.
+        */
+       baseflags = flags;
+       baseflags &= ~O_TRUNC;
+       baseflags &= ~O_EXCL;
+       /* Leave O_CREAT on so the underlying file can be created. */
+
+       hostfd = sys_open(fname, baseflags, mode);
+
+       /* It is legit to open a stream on a directory, but the base
+        * fd has to be read-only.
+        */
+       if ((hostfd == -1) && (errno == EISDIR)) {
+               baseflags &= ~O_ACCMODE;
+               baseflags |= O_RDONLY;
+               hostfd = sys_open(fname, baseflags, mode);
+       }
+
+       if (hostfd == -1) {
+               return -1;
+       }
+
+       ret = get_xattr_size(NULL, hostfd, sname, 0);
+       if (ret == -1) {
+               if (errno != ENOATTR) {
+                       goto fail;
+               }
+
+               xattr_is_present = False;
+       }
+
+       if (!xattr_is_present) {
+               /* Lookout! This is racey since someone else might have set a
+                * real value since we determined that the xattr wasn't
+                * present. There's not much we can do about this with the
+                * current API.
+                */
+               if (flags & O_CREAT) {
+                       char null = '\0';
+                       ret = fsetxattr(hostfd, sname, &null, 1, 0,
+                               flags & O_EXCL ? XATTR_CREATE : 0);
+                       /* This is a "best effort" create. HFS can fail with
+                        * ERANGE for com.apple.FinderInfo, and we know that
+                        * if the client writes data, it will create the
+                        * attribute as a side-effect.
+                        */
+               } else {
+                       errno = ENOENT;
+                       goto fail;
+               }
+       }
+
+       /* This might be redundant if we just created the xattr above, but
+        * might not be if we are racing with another writer.
+        */
+       if (flags & O_TRUNC) {
+               char null = '\0';
+               fsetxattr(hostfd, sname, &null, 1, 0, XATTR_REPLACE);
+       }
+
+       sio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct darwin_stream_io);
+       if (!sio) {
+               errno = ENOMEM;
+               goto fail;
+       }
+
+       sio->xtra.xattr.sname =
+           talloc_strdup(VFS_MEMCTX_FSP_EXTENSION(handle, fsp), sname);
+
+       /* We don't need a special close operation. xtra.xattr.sname will be
+        * release when the extension talloc context is destroyed.
+        */
+
+       sio->pread = darwin_pread_xattr;
+       sio->pwrite = darwin_pwrite_xattr;
+       sio->ftruncate = darwin_ftruncate_xattr;
+       sio->fstat = darwin_fstat_xattr;
+       sio->getlock = darwin_getlock_ignore;
+       sio->lock = darwin_lock_ignore;
+
+       return hostfd;
+
+fail:
+       {
+               int sav = errno;
+               if (sio) {
+                       VFS_REMOVE_FSP_EXTENSION(handle, fsp);
+               }
+               close(hostfd);
+               errno = sav;
+               return -1;
+       }
+}
+
+static int darwin_open_finfo(vfs_handle_struct *handle,
+               const char * fname,
+               struct files_struct * fsp,
+               int flags,
+               mode_t mode)
+{
+       int ret;
+       struct darwin_stream_io *sio;
+
+       DEBUG(MODULE_TRACE, ("finfo method: fname='%s'\n", fname));
+
+       /* Map the CIFS finder info stream to the MacOSX finder info
+        * extended attribute.
+        */
+       ret = darwin_open_xattr(handle, fname, XATTR_FINDERINFO_NAME,
+                               fsp, flags|O_CREAT, mode);
+       if (ret < 0) {
+               return ret;
+       }
+
+       sio = VFS_FETCH_FSP_EXTENSION(handle, fsp);
+       SMB_ASSERT(sio != NULL);
+
+       /* Override the extended attribute ops, because we will need to to a
+        * bit of extra checking.
+        */
+       sio->pread = darwin_pread_finfo;
+       sio->pwrite = darwin_pwrite_finfo;
+       sio->ftruncate = darwin_ftruncate_finfo;
+
+       return ret;
+}
+
+static int darwin_open_rsrc(vfs_handle_struct *handle,
+               const char * fname,
+               struct files_struct * fsp,
+               int flags,
+               mode_t mode)
+{
+       int fd;
+       int basefd;
+       struct darwin_stream_io *sio;
+       int errsav;
+       char *fullname = NULL;
+
+       DEBUG(MODULE_TRACE, ("rsrc method: fname='%s'\n", fname));
+
+       fullname = talloc_asprintf(NULL, "%s%s", fname, "/..namedfork/rsrc");
+       if (!fullname) {
+               errno = ENOMEM;
+               return -1;
+       }
+
+       sio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct darwin_stream_io);
+       if (!sio) {
+               talloc_free(fullname);
+               errno = ENOMEM;
+               return -1;
+       }
+
+       sio->fstat = darwin_fstat_rsrc;
+
+       /* If the base file does not exist, opening the resource fork will
+        * fail, even if we get O_CREAT. This makes sure that the base file
+        * is always around, but doesn't prevent an unlink racing with the
+        * second open.
+        */
+       basefd = sys_open(fname, flags, mode);
+       errsav = errno;
+       if (basefd == -1) {
+               talloc_free(fullname);
+               VFS_REMOVE_FSP_EXTENSION(handle, fsp);
+               errno = errsav;
+               return -1;
+       }
+
+       if (lp_parm_bool(SNUM(handle->conn), MODULE_NAME, "brlm", False)) {
+               fd = darwin_open_brlm(handle, fullname, fsp,
+                           kBRLMResFork, flags, mode);
+       } else {
+               fd = sys_open(fullname, flags, mode);
+       }
+
+       errsav = errno;
+       if (fd == -1) {
+               VFS_REMOVE_FSP_EXTENSION(handle, fsp);
+       }
+
+       close(basefd);
+       talloc_free(fullname);
+
+       errno = errsav;
+       return fd;
+}
+
+static int brlm_map_mode(const struct files_struct * fsp, int flags)
+{
+       uint32 dmode;
+       int bmode;
+
+       dmode = map_share_mode_to_deny_mode(fsp->share_access, 0);
+       switch (dmode) {
+               case DENY_ALL:
+                       bmode = kBRLMDenyRead | kBRLMDenyWrite;
+                       break;
+               case DENY_WRITE:
+                       bmode = kBRLMDenyWrite;
+                       break;
+               case DENY_READ:
+                       bmode = kBRLMDenyRead;
+                       break;
+               default:
+                       bmode = 0;
+       }
+
+       switch (flags & O_ACCMODE) {
+               case O_RDONLY:
+                       bmode |= kBRLMRead;
+                       break;
+               case O_WRONLY:
+                       bmode |= kBRLMWrite;
+                       break;
+               case O_RDWR:
+                       bmode |= (kBRLMRead | kBRLMWrite);
+                       break;
+       }
+
+       DEBUG(MODULE_TRACE,
+           ("mapped share_access=%#x to deny mode=%#x to BRLM mode=%#x\n",
+                   fsp->share_access, dmode, bmode));
+       return bmode;
+}
+
+static int darwin_open_brlm(vfs_handle_struct *handle,
+               const char * fname,
+               struct files_struct * fsp,
+               BRLMForkType ftype,
+               int flags,
+               mode_t mode)
+{
+       BRLMRef bref;
+       BRLMStatus bstatus;
+       int bmode;
+       struct darwin_stream_io *sio;
+
+       DEBUG(MODULE_TRACE, ("brlm method: fname='%s'\n", fname));
+
+       sio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct darwin_stream_io);
+       if (!sio) {
+               errno = ENOMEM;
+               return -1;
+       }
+
+       bmode = brlm_map_mode(fsp, flags);
+
+       BRLM_CALL(BRLMPosixOpen((unsigned char *)fname, ftype,
+                       bmode, flags & (~O_ACCMODE), mode, &bref, 0, 0),
+                 bstatus);
+
+       switch (bstatus) {
+               case BRLMNoErr:
+                   sio->xtra.dfork.bref =
+                           talloc_zero(VFS_MEMCTX_FSP_EXTENSION(handle, fsp),
+                                   BRLMRef);
+
+                   /* Set a destructor to close this BRLM reference whenthe
+                    * the fsp is destroyed.
+                    */
+                   talloc_set_destructor((void *)(sio->xtra.dfork.bref),
+                                           brlm_handle_destructor);
+                   *sio->xtra.dfork.bref = bref;
+
+                   SMB_ASSERT(sio->lock == NULL);
+                   SMB_ASSERT(sio->getlock == NULL);
+                   SMB_ASSERT(sio->close == NULL);
+
+                   sio->lock = darwin_lock_brlm;
+                   sio->getlock = darwin_getlock_brlm;
+                   sio->close = darwin_close_brlm;
+
+                   /* FIXME: BRLMPosixOpen always sets O_NONBLOCK. We should
+                    * turn this off if it's not in the original mode.
+                    */
+                   DEBUG(MODULE_TRACE, ("%s: opening %s gave fd=%d\n",
+                               MODULE_NAME, fname, BRLMGetDescriptor(bref)));
+
+                   return BRLMGetDescriptor(bref);
+
+               case BRLMOpenDenied:
+               case BRLMLockConflict:
+                       errno = EDEADLK;
+                       /* FALLTHRU */
+
+               default:
+                       VFS_REMOVE_FSP_EXTENSION(handle, fsp);
+                       return(-1);
+       }
+}
+
+/* ========================================================================
+   Stream emulation entry points.
+   ======================================================================== */
+
+static int darwin_sys_connect(vfs_handle_struct *handle,
+               const char *service, const char *user)
+{
+    /* Set up message levels depending on the config, eg.
+     *         darwin_streams:msgtrace = 4
+     *         darwin_streams:msgbrlm = 0
+     *         darwin_streams:msgentry = 100
+     */
+
+    MODULE_TRACE = lp_parm_int(SNUM(handle->conn), MODULE_NAME,
+           "msgtrace", MODULE_TRACE);
+    MODULE_TRACE_BRLM = lp_parm_int(SNUM(handle->conn), MODULE_NAME,
+           "msgbrlm", MODULE_TRACE_BRLM);
+    MODULE_TRACE_SYS_ENTRY = lp_parm_int(SNUM(handle->conn), MODULE_NAME,
+           "msgentry", MODULE_TRACE_SYS_ENTRY);
+
+    return SMB_VFS_NEXT_CONNECT(handle, service, user);
+}
+
+static int darwin_sys_open(vfs_handle_struct * handle,
+               const char * path,
+               struct files_struct * fsp,
+               int flags,
+               mode_t mode)
+{
+       pstring fname;
+       pstring sname;
+
+       TRACE_SYS_ENTRY();
+       pstrcpy(fname, path);
+       if (!NT_STATUS_IS_OK(split_ntfs_stream_name(fname, sname))) {
+               /* Not necessarily the right error code. */
+               errno = ENOENT;
+               return -1;
+       }
+
+       DEBUG(MODULE_TRACE, ("split fname='%s' sname='%s'\n", fname, sname));
+
+       /* If it's not a stream, punt it. */
+       if (!(*sname)) {
+               int ret;
+               if (lp_parm_bool(SNUM(handle->conn),
+                           MODULE_NAME, "brlm", False)) {
+                       ret = darwin_open_brlm(handle, fname, fsp,
+                                           kBRLMDataFork, flags, mode);
+               } else {
+                       ret = sys_open(path, flags, mode);
+               }
+
+               if (ret != -1) {
+                       fsp->is_sendfile_capable = lp_use_sendfile(SNUM(handle->conn));
+               }
+
+               return ret;
+       }
+
+       /* Only allow streams named :foo, etc. Higher layers abide by this
+        * rule, and it guarantees protection of the com.apple xattr namespace
+        * from callers' sticky fingers.
+        */
+       if (*sname != ':') {
+               DEBUG(MODULE_TRACE,
+                       ("fname='%s' sname='%s'\n", fname, sname));
+               DEBUGADD(MODULE_TRACE,
+                       ("\tstream name has no leading ':'\n"));
+               errno = EINVAL;
+               return -1;
+       }
+
+       /* Normalise to emulate case-insensitive stream name lookups. */
+       strnorm(sname, CANONICAL_CASE);
+
+       if (strcmp(sname, afp_resource_name) == 0) {
+               return darwin_open_rsrc(handle, fname, fsp, flags, mode);
+       } else if (strcmp(sname, afp_info_name) == 0) {
+               return darwin_open_finfo(handle, fname, fsp, flags, mode);
+       } else {
+               return darwin_open_xattr(handle, fname, sname, fsp, flags, mode);
+       }
+}
+
+static int darwin_sys_unlink(vfs_handle_struct *handle, const char *path)
+{
+       pstring fname;
+       pstring sname;
+       const char * mapped;
+       int ret;
+
+       TRACE_SYS_ENTRY();
+
+       pstrcpy(fname, path);
+       if (!NT_STATUS_IS_OK(split_ntfs_stream_name(fname, sname))) {
+               /* Not necessarily the right error code. */
+               errno = ENOENT;
+               return -1;
+       }
+
+       if (!(*sname)) {
+               return unlink(path);
+       }
+
+       if (*sname != ':') {
+               errno = ENOENT;
+               return -1;
+       }
+
+       DEBUG(MODULE_TRACE, ("fname='%s' sname='%s'\n", fname, sname));
+
+       /* Normalise to upper case to emulate case-insensitive stream name
+        * lookups.
+        */
+       strnorm(sname, CANONICAL_CASE);
+
+       mapped = map_special_xattr_names(sname);
+       ret = removexattr(fname, mapped, XATTR_NOFOLLOW);
+
+       /* Special xattrs may go AWOL as a side-effect of being zeroed. Since
+        * we can't tell when this happens, we have to just swallow the error.
+        */
+       if (mapped != sname) {
+               errno = (errno == ENOATTR) ? 0 : errno;
+       } else {
+               errno = (errno == ENOATTR) ? ENOENT : errno;
+       }
+
+       return ret;
+}
+
+static int darwin_sys_close(vfs_handle_struct *handle,
+                   struct files_struct * fsp, int fd)
+{
+       struct darwin_stream_io *sio = NULL;
+       int ret;
+
+       TRACE_SYS_ENTRY();
+
+       /* If this is a resource fork or the primary data fork, the fd here is
+        * that taken directly from open. For the xattr-based streams, the fd
+        * is that of the file hosting the xattr. In any case, the right thing
+        * to do is to just close it.
+        */
+
+       sio = VFS_FETCH_FSP_EXTENSION(handle, fsp);
+       if (sio && sio->close) {
+               ret = sio->close(sio, fd);
+       } else {
+               ret = close(fd);
+       }
+
+       return ret;
+}
+
+static int darwin_sys_pread(vfs_handle_struct *handle,
+                       files_struct *fsp,
+                       int fd, void *data,
+                       size_t count, SMB_OFF_T offset)
+{
+       struct darwin_stream_io * sio;
+
+       TRACE_SYS_ENTRY();
+
+       sio = VFS_FETCH_FSP_EXTENSION(handle, fsp);
+       if (!sio || !sio->pread) {
+               return sys_pread(fd, data, count, offset);
+       }
+
+       return sio->pread(sio, fd, data, count, offset);
+
+}
+
+static ssize_t darwin_sys_pwrite(vfs_handle_struct *handle,
+                       files_struct *fsp,
+                       int fd, const void *data,
+                       size_t count, SMB_OFF_T offset)
+{
+       struct darwin_stream_io * sio;
+
+       TRACE_SYS_ENTRY();
+
+       sio = VFS_FETCH_FSP_EXTENSION(handle, fsp);
+       if (!sio || !sio->pwrite) {
+               return sys_pwrite(fd, data, count, offset);
+       }
+       return sio->pwrite(sio, fd, data, count, offset);
+}
+
+static int darwin_sys_set_create_time(vfs_handle_struct *handle,
+                       const char *path,
+                       time_t createtime)
+{
+       struct attrlist alist = {0};
+       struct timespec ts = {0};
+       pstring fname;
+       pstring sname;
+
+       pstrcpy(fname, path);
+       if (!NT_STATUS_IS_OK(split_ntfs_stream_name(fname, sname))) {
+               /* Not necessarily the right error code. */
+               errno = ENOENT;
+               return -1;
+       }
+
+       /*
+        * If we decided to add setting all times here we need to
+        * allocate a buffer big enough to handle all the times.
+        * Since we are only doing create time just use the timespec
+        * structure.
+        */
+       ts.tv_sec = createtime;
+        alist.bitmapcount = ATTR_BIT_MAP_COUNT;
+       alist.commonattr = ATTR_CMN_CRTIME;
+       return  setattrlist (fname, &alist, (void*)&ts, sizeof(struct timespec), 0);
+}
+
+/*
+ * Give a path return the correct case of the end component.
+ */
+static BOOL darwin_sys_get_preserved_name(vfs_handle_struct *handle,
+                       const char *path, pstring name)
+{
+       struct attrlist attrlist;
+       char attrbuf[sizeof(struct attrreference) + sizeof(u_int32_t) + NAME_MAX + 1];
+       struct attrreference * data = (struct attrreference *)attrbuf;
+       u_int32_t *nmlen;
+       char *preserved_name = NULL;
+       int len, maxlen;
+
+       ZERO_STRUCT(attrlist);
+       ZERO_STRUCT(attrbuf);
+       attrlist.bitmapcount = ATTR_BIT_MAP_COUNT;
+       attrlist.commonattr = ATTR_CMN_NAME;
+       /* Call getattrlist to get the real volume name */
+       if (getattrlist(path, &attrlist, attrbuf, sizeof(attrbuf), FSOPT_NOFOLLOW) != 0) {
+               DEBUG(5, ("getattrlist for %s failed: %s\n", path, strerror(errno)));
+               return False;
+       }
+       /* Make sure we didn't get something bad */
+       maxlen = data->attr_dataoffset - (sizeof(struct attrreference) +  sizeof(u_int32_t));
+       nmlen = (u_int32_t *)(attrbuf+sizeof(struct attrreference));
+       /* Should never happen, but just to be safe */
+       if (*nmlen > maxlen) {
+               DEBUG(5, ("name length to large for buffer nmlen = %d  maxlen = %d\n", nmlen, maxlen));
+               return False;
+       }
+       len = *nmlen++;
+       preserved_name = (char *)nmlen;
+       preserved_name[len] = 0;
+       pstrcpy(name, preserved_name);
+       return True;
+}
+
+static int darwin_sys_ftruncate(vfs_handle_struct *handle,
+                       files_struct *fsp,
+                       int fd, SMB_OFF_T len)
+{
+       struct darwin_stream_io * sio;
+
+       TRACE_SYS_ENTRY();
+
+       sio = VFS_FETCH_FSP_EXTENSION(handle, fsp);
+       if (!sio || !sio->ftruncate) {
+               int ret = sys_ftruncate(fd, len);
+               DEBUG(MODULE_TRACE,
+                       ("%s: ftruncate fd=%d len=%u gave ret=%d errno=%d\n",
+                       MODULE_NAME, fd, (unsigned)len, ret, errno));
+               return ret;
+       }
+
+       return sio->ftruncate(sio, fd, len);
+
+}
+
+static int darwin_sys_fstat(vfs_handle_struct *handle,
+                       files_struct *fsp,
+                       int fd, SMB_STRUCT_STAT *sbuf)
+{
+       struct darwin_stream_io * sio;
+
+       TRACE_SYS_ENTRY();
+
+       sio = VFS_FETCH_FSP_EXTENSION(handle, fsp);
+       if (!sio || !sio->fstat) {
+               return sys_fstat(fd, sbuf);
+       }
+
+       return sio->fstat(sio, fd, sbuf);
+}
+
+static int darwin_sys_stat_common(vfs_handle_struct *handle,
+                       const char * path,
+                       SMB_STRUCT_STAT * sbuf,
+                       int flags)
+{
+       int ret, sav;
+       const char *mapped;
+       pstring fname;
+       pstring sname;
+
+       int (*stat_func)(const char *, SMB_STRUCT_STAT *);
+
+       pstrcpy(fname, path);
+       if (!NT_STATUS_IS_OK(split_ntfs_stream_name(fname, sname))) {
+               /* Not necessarily the right error code. */
+               errno = ENOENT;
+               return -1;
+       }
+
+       stat_func = (flags == XATTR_NOFOLLOW) ? sys_lstat : sys_stat;
+
+       if (!(*sname)) {
+               /* No streams involved ... */
+               return stat_func(fname, sbuf);
+       }
+
+       strnorm(sname, CANONICAL_CASE);
+
+       /* Take a different stat path for the resource fork to avoid
+        * cases where the xattr layer confuses an empty xattr for a
+        * missing xattr.
+        */
+       if (strcmp(sname, afp_resource_name) == 0) {
+               char * rsrc;
+
+               rsrc = talloc_asprintf(NULL, "%s%s",
+                           fname, "/..namedfork/rsrc");
+               if (!rsrc) {
+                       errno = ENOMEM;
+                       return -1;
+               }
+
+               ret = stat_func(rsrc, sbuf);
+               if (ret == -1) {
+                       sav = errno;
+                       talloc_free(rsrc);
+                       errno = sav;
+                       return -1;
+               }
+
+               sbuf->st_ino = stream_inode(sbuf, "/..namedfork/rsrc");
+               talloc_free(rsrc);
+               return 0;
+       }
+
+       /* For other streams-on-xattrs, we take the attributes of the
+        * underlying file and touch them up as necessary.
+        */
+       if (stat_func(fname, sbuf) == -1) {
+               return -1;
+       }
+
+       mapped = map_special_xattr_names(sname);
+       sbuf->st_size = get_xattr_size(fname, -1, mapped, flags);
+       sav = errno;
+
+       if (sbuf->st_size == -1) {
+               return -1;
+       }
+
+       /* We are pretending that this xattr contains the AFP_AfpInfo stream,
+        * but it really only contains the finder info field. Touch up the size
+        * to preserve the illusion.
+        */
+       if (strcmp(sname, afp_info_name) == 0 &&
+           sbuf->st_size == AFP_FinderSize) {
+               sbuf->st_size = AFP_INFO_SIZE;
+       }
+
+       sbuf->st_ino = stream_inode(sbuf, sname);
+       sbuf->st_mode &= ~S_IFMT;
+       sbuf->st_mode |= S_IFREG;
+       sbuf->st_blocks = sbuf->st_size % STAT_ST_BLOCKSIZE + 1;
+
+       errno = sav;
+       return 0;
+}
+
+static int darwin_sys_stat(vfs_handle_struct *handle,
+                       const char * path,
+                       SMB_STRUCT_STAT * sbuf)
+{
+       TRACE_SYS_ENTRY();
+       return darwin_sys_stat_common(handle, path, sbuf, 0);
+}
+
+static int darwin_sys_lstat(vfs_handle_struct *handle,
+                       const char * path,
+                       SMB_STRUCT_STAT * sbuf)
+{
+       TRACE_SYS_ENTRY();
+       return darwin_sys_stat_common(handle, path, sbuf, XATTR_NOFOLLOW);
+}
+
+static SMB_STRUCT_DIR * darwin_sys_opendir(vfs_handle_struct *handle,
+                       const char * path,
+                       const char * mask,
+                       uint32 attr)
+{
+       TRACE_SYS_ENTRY();
+
+       if (is_ntfs_stream_name(path)) {
+               errno = ENOTDIR;
+               return NULL;
+       }
+
+       return sys_opendir(path);
+}
+
+static ssize_t darwin_sys_lock(vfs_handle_struct *handle,
+                       files_struct *fsp, int fd, int op,
+                       SMB_OFF_T offset, SMB_OFF_T count, int type)
+{
+       struct darwin_stream_io * sio;
+
+       TRACE_SYS_ENTRY();
+
+       sio = VFS_FETCH_FSP_EXTENSION(handle, fsp);
+       if (!sio || !sio->lock) {
+               return fcntl_lock(fd, op, offset, count, type);
+       }
+       return sio->lock(sio, fd, op, offset, count, type);
+}
+
+static BOOL darwin_sys_getlock(vfs_handle_struct *handle,
+                       files_struct *fsp, int fd,
+                       SMB_OFF_T *poffset, SMB_OFF_T *pcount,
+                       int *ptype, pid_t *ppid)
+{
+       struct darwin_stream_io * sio;
+
+       TRACE_SYS_ENTRY();
+
+       sio = VFS_FETCH_FSP_EXTENSION(handle, fsp);
+       if (!sio || !sio->getlock) {
+               return fcntl_getlock(fd, poffset, pcount, ptype, ppid);
+       }
+
+       return sio->getlock(sio, fd, poffset, pcount, ptype, ppid);
+}
+
+static int darwin_sys_ntimes(struct vfs_handle_struct *handle,
+               const char *path, const struct timespec ts[2])
+{
+       pstring fname;
+       pstring sname;
+
+       TRACE_SYS_ENTRY();
+       pstrcpy(fname, path);
+       if (!NT_STATUS_IS_OK(split_ntfs_stream_name(fname, sname))) {
+               /* Not necessarily the right error code. */
+               errno = ENOENT;
+               return -1;
+       }
+
+       /* Updating the timestamps on a stream is the same as updating the
+        * timestamps on the host file.
+        */
+
+       return SMB_VFS_NEXT_NTIMES(handle, fname, ts);
+}
+
+static int darwin_sys_chflags(struct vfs_handle_struct *handle,
+               const char *path, int flags)
+{
+       pstring fname;
+       pstring sname;
+
+       TRACE_SYS_ENTRY();
+       pstrcpy(fname, path);
+       if (!NT_STATUS_IS_OK(split_ntfs_stream_name(fname, sname))) {
+               /* Not necessarily the right error code. */
+               errno = ENOENT;
+               return -1;
+       }
+
+       /* Updating the flags on a stream is the same as updating the
+        * timestamps on the host file.
+        */
+
+       return chflags(fname, flags);
+}
+
+/* ========================================================================
+   Streaminfo implementation.
+   ======================================================================== */
+
+#define DEFAULT_STREAM_COUNT 2
+#define DEFAULT_STREAM_LEN \
+       (sizeof(afp_info_name) + sizeof(afp_resource_name))
+
+static BOOL include_stream_names(const char * name)
+{
+       if (*name == ':') {
+               return True;
+       }
+
+       /* This catches all "normal" xattrs as well as the resource fork and
+        * finder info xatter mappings.
+        */
+       return False;
+}
+
+static int darwin_sys_streaminfo(vfs_handle_struct *handle,
+       struct files_struct *fsp,
+       const char *fname,
+       char **names, size_t **sizes)
+{
+       int smax = DEFAULT_STREAM_COUNT; /* max stream names we allocated */
+       int scount = 0; /* current number of stream names */
+       char * end;
+       char * name;
+       ssize_t total;
+       ssize_t valid;
+       BOOL isDir = False;
+
+       TRACE_SYS_ENTRY();
+
+       /* Make sure that the caller is asking for stream info for an
+        * ordinary file, not some sort of stream.
+        */
+       if (fsp && fsp->fh->fd != -1) {
+               if (is_ntfs_stream_name(fsp->fsp_name)) {
+                       errno = EINVAL;
+                       return -1;
+               }
+       } else if (is_ntfs_stream_name(fname)) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       /* Fetch all the xattr names. */
+       if (fsp && fsp->fh->fd != -1) {
+               isDir = fsp->is_directory;
+               total = list_existing_xattr(NULL, fsp->fh->fd, names);
+       } else {
+               SMB_STRUCT_STAT sbuf;
+               if (sys_stat(fname, &sbuf) != -1 )
+                       isDir = S_ISDIR(sbuf.st_mode);
+               total = list_existing_xattr(fname, -1, names);
+       }
+
+       if (total < 0) {
+               goto fail;
+       }
+
+       /* Filter out the xattr names that are not actually named streams. */
+       valid = filter_name_list(*names, total, include_stream_names);
+
+       if ((total - valid) < DEFAULT_STREAM_LEN) {
+               /* We have don't have enough room stashed for the
+                * default streams.
+                */
+               *names = SMB_REALLOC(*names, total + DEFAULT_STREAM_LEN);
+               if (*names == NULL) {
+                       goto fail;
+               }
+
+               total += DEFAULT_STREAM_LEN;
+       }
+
+       /* Preallocate any default entries. */
+       if (smax) {
+               *sizes = SMB_CALLOC_ARRAY(size_t, smax);
+               if (*sizes == NULL) {
+                       goto fail;
+               }
+       }
+
+       /* Walk the sequence of valid names and figure out what the length of
+        * the corresponding xattr is.
+        */
+       end = (*names) + valid;
+       for (name = *names; name < end; name += (strlen(name) + 1)) {
+               if (scount >= (smax - DEFAULT_STREAM_COUNT)) {
+                       smax += 20;
+                       *sizes = SMB_REALLOC(*sizes, sizeof(size_t) * smax);
+                       if (*sizes == NULL) {
+                               goto fail;
+                       }
+               }
+
+               if (fsp && fsp->fh->fd != -1) {
+                       (*sizes)[scount] = get_xattr_size(NULL, fsp->fh->fd, name, 0);
+               } else {
+                       (*sizes)[scount] = get_xattr_size(fname, -1, name, 0);
+               }
+
+               DEBUG(MODULE_TRACE, ("stream '%s' is %u bytes\n",
+                           name, (*sizes)[scount]));
+
+               ++scount;
+       }
+
+       /* NOTE: We are careful to always append :$DATA to all stream names.
+        * This matches the canonicalisation that Windows does, except that we
+        * have to clobber the case to normal form.
+        */
+       memcpy((*names) + valid, afp_info_name, sizeof(afp_info_name));
+       valid += sizeof(afp_info_name);
+       if (fsp && fsp->fh->fd != -1) {
+               smax = get_xattr_size(NULL, fsp->fh->fd, XATTR_FINDERINFO_NAME, 0);
+       } else {
+               smax = get_xattr_size(fname, -1, XATTR_FINDERINFO_NAME, 0);
+       }
+       (*sizes)[scount++] = smax > 0 ? AFP_INFO_SIZE : 0;
+
+       if (! isDir ) {
+               memcpy((*names) + valid, afp_resource_name, sizeof(afp_resource_name));
+               valid += sizeof(afp_resource_name);
+
+               if (fsp && fsp->fh->fd != -1) {
+                       smax = get_xattr_size(NULL, fsp->fh->fd, XATTR_RESOURCEFORK_NAME, 0);
+               } else {
+                       smax = get_xattr_size(fname, -1, XATTR_RESOURCEFORK_NAME, 0);
+               }
+               (*sizes)[scount++] = smax > 0 ? smax : 0;
+       }
+
+       if (fsp && fsp->fh->fd != -1) {
+               DEBUG(MODULE_TRACE, ("%s: %d streams for file fname=%s fd=%d:\n",
+                       MODULE_NAME, scount, fsp->fsp_name, fsp->fh->fd));
+       } else {
+               DEBUG(MODULE_TRACE, ("%s: %d streams for file fname=%s:\n",
+                           MODULE_NAME, scount, fname));
+       }
+       debug_attribute_names(MODULE_TRACE, *names, total);
+
+       return scount;
+
+fail:
+       {
+               int sav = errno;
+               sav = errno;
+               SAFE_FREE(*names);
+               SAFE_FREE(*sizes);
+               errno = sav;
+               return -1;
+       }
+}
+
+#undef DEFAULT_STREAM_LEN
+#undef DEFAULT_STREAM_COUNT
+
+/* ========================================================================
+   VFS operations structure
+   ======================================================================== */
+
+static vfs_op_tuple darwin_streams_ops[] = {
+
+       {SMB_VFS_OP(darwin_sys_connect), SMB_VFS_OP_CONNECT,
+           SMB_VFS_LAYER_TRANSPARENT},
+
+       {SMB_VFS_OP(darwin_sys_open), SMB_VFS_OP_OPEN,
+           SMB_VFS_LAYER_OPAQUE},
+       {SMB_VFS_OP(darwin_sys_close), SMB_VFS_OP_CLOSE,
+           SMB_VFS_LAYER_OPAQUE},
+       {SMB_VFS_OP(darwin_sys_unlink), SMB_VFS_OP_UNLINK,
+           SMB_VFS_LAYER_OPAQUE},
+
+       /* Don't support read and write calls. Samba will never call
+        * these if pread and pwrite are available.
+        */
+       {SMB_VFS_OP(enosys_ssize_t), SMB_VFS_OP_READ,
+           SMB_VFS_LAYER_OPAQUE},
+       {SMB_VFS_OP(enosys_ssize_t), SMB_VFS_OP_WRITE,
+           SMB_VFS_LAYER_OPAQUE},
+
+       {SMB_VFS_OP(darwin_sys_pread), SMB_VFS_OP_PREAD,
+           SMB_VFS_LAYER_OPAQUE},
+       {SMB_VFS_OP(darwin_sys_pwrite), SMB_VFS_OP_PWRITE,
+           SMB_VFS_LAYER_OPAQUE},
+
+       {SMB_VFS_OP(darwin_sys_stat), SMB_VFS_OP_STAT,
+           SMB_VFS_LAYER_OPAQUE},
+       {SMB_VFS_OP(darwin_sys_lstat), SMB_VFS_OP_LSTAT,
+           SMB_VFS_LAYER_OPAQUE},
+       {SMB_VFS_OP(darwin_sys_fstat), SMB_VFS_OP_FSTAT,
+           SMB_VFS_LAYER_OPAQUE},
+
+       {SMB_VFS_OP(darwin_sys_lock), SMB_VFS_OP_LOCK,
+           SMB_VFS_LAYER_OPAQUE},
+       {SMB_VFS_OP(darwin_sys_getlock), SMB_VFS_OP_GETLOCK,
+           SMB_VFS_LAYER_OPAQUE},
+
+       {SMB_VFS_OP(darwin_sys_set_create_time), SMB_VFS_OP_SET_CREATE_TIME,
+           SMB_VFS_LAYER_OPAQUE},
+
+       {SMB_VFS_OP(darwin_sys_get_preserved_name), SMB_VFS_OP_GET_PRESERVED_NAME,
+           SMB_VFS_LAYER_OPAQUE},
+
+       {SMB_VFS_OP(darwin_sys_ftruncate), SMB_VFS_OP_FTRUNCATE,
+           SMB_VFS_LAYER_OPAQUE},
+
+       {SMB_VFS_OP(darwin_sys_opendir), SMB_VFS_OP_OPENDIR,
+           SMB_VFS_LAYER_OPAQUE},
+
+       {SMB_VFS_OP(darwin_sys_streaminfo), SMB_VFS_OP_STREAMINFO,
+           SMB_VFS_LAYER_OPAQUE},
+
+       {SMB_VFS_OP(darwin_sys_chflags), SMB_VFS_OP_CHFLAGS,
+           SMB_VFS_LAYER_OPAQUE},
+
+       {SMB_VFS_OP(darwin_sys_getxattr), SMB_VFS_OP_GETXATTR,
+           SMB_VFS_LAYER_OPAQUE},
+       {SMB_VFS_OP(darwin_sys_setxattr), SMB_VFS_OP_SETXATTR,
+           SMB_VFS_LAYER_OPAQUE},
+       {SMB_VFS_OP(darwin_sys_listxattr), SMB_VFS_OP_LISTXATTR,
+           SMB_VFS_LAYER_OPAQUE},
+       {SMB_VFS_OP(darwin_sys_removexattr), SMB_VFS_OP_REMOVEXATTR,
+           SMB_VFS_LAYER_OPAQUE},
+
+       {SMB_VFS_OP(darwin_sys_fgetxattr), SMB_VFS_OP_FGETXATTR,
+           SMB_VFS_LAYER_OPAQUE},
+       {SMB_VFS_OP(darwin_sys_fsetxattr), SMB_VFS_OP_FSETXATTR,
+           SMB_VFS_LAYER_OPAQUE},
+       {SMB_VFS_OP(darwin_sys_flistxattr), SMB_VFS_OP_FLISTXATTR,
+           SMB_VFS_LAYER_OPAQUE},
+       {SMB_VFS_OP(darwin_sys_fremovexattr), SMB_VFS_OP_FREMOVEXATTR,
+           SMB_VFS_LAYER_OPAQUE},
+
+       {SMB_VFS_OP(darwin_sys_lgetxattr), SMB_VFS_OP_LGETXATTR,
+           SMB_VFS_LAYER_OPAQUE},
+       {SMB_VFS_OP(darwin_sys_lsetxattr), SMB_VFS_OP_LSETXATTR,
+           SMB_VFS_LAYER_OPAQUE},
+       {SMB_VFS_OP(darwin_sys_llistxattr), SMB_VFS_OP_LLISTXATTR,
+           SMB_VFS_LAYER_OPAQUE},
+       {SMB_VFS_OP(darwin_sys_lremovexattr), SMB_VFS_OP_LREMOVEXATTR,
+           SMB_VFS_LAYER_OPAQUE},
+
+       {SMB_VFS_OP(darwin_sys_ntimes), SMB_VFS_OP_NTIMES,
+           SMB_VFS_LAYER_TRANSPARENT},
+
+       {SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP}
+};
+
+NTSTATUS vfs_darwin_streams_init(void)
+{
+       BRLMStatus bstatus;
+
+       /* Speculatively init BRLM, even if we aren't going to use it. */
+       BRLM_CALL(BRLMInit(), bstatus);
+       setenv("MallocBadFreeAbort", "1", 1 /* overwrite */);
+
+       memcpy(afp_info_name, AFPINFO_STREAM,
+               sizeof(AFPINFO_STREAM));
+       memcpy(afp_resource_name, AFPRESOURCE_STREAM,
+               sizeof(AFPRESOURCE_STREAM));
+
+       strnorm(afp_info_name, CANONICAL_CASE);
+       strnorm(afp_resource_name, CANONICAL_CASE);
+
+       return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
+                       MODULE_NAME, darwin_streams_ops);
+}
index 0a6be4c79fd10d6127e6a0d8308085428805532b..2789f7dd6b57d5fc1a19bbd095e728140a04b27a 100644 (file)
@@ -1613,6 +1613,35 @@ NTSTATUS open_file_ntcreate(connection_struct *conn,
                             open_access_mask);
 
        if (!NT_STATUS_IS_OK(fsp_open)) {
+
+               /* Darwin BRLM will return EDEADLK if some other process
+                * already has a conflicting share mode. In this case, we
+                * do the deferred open dance again.
+                */
+               if (file_existed && errno == EDEADLK &&
+                   !(oplock_request & INTERNAL_OPEN_ONLY) &&
+                   lp_defer_sharing_violations()) {
+                       struct timeval timeout;
+                       struct deferred_open_record state;
+                       int timeout_usecs;
+
+                       state.delayed_for_oplocks = False;
+                       state.dev = dev;
+                       state.inode = inode;
+
+                       timeout_usecs = lp_parm_int(SNUM(conn),
+                                               "smbd","sharedelay",
+                                               SHARING_VIOLATION_USEC_WAIT);
+                       timeout = timeval_set(0, timeout_usecs);
+
+                       if (!request_timed_out(request_time, timeout)) {
+                               defer_open(lck, request_time, timeout,
+                                          &state);
+                       }
+
+                       fsp_open = NT_STATUS_SHARING_VIOLATION;
+               }
+
                if (lck != NULL) {
                        TALLOC_FREE(lck);
                }