vfs-module-darwin-streams [plain text]
Index: samba/source/modules/vfs_darwin_streams.c
===================================================================
--- /dev/null
+++ samba/source/modules/vfs_darwin_streams.c
@@ -0,0 +1,2155 @@
+/*
+ * Darwin ACL VFS module
+ *
+ * Copyright (c) 2006-2007 Apple Inc. All rights reserved.
+ *
+ * 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,
+ BRLMInitErr = 30003,
+ /*Errors in the database*/
+ BRLMDBOpenErr = 30013,
+ BRLMDBInvalidErr = 30023,
+ BRLMDBStaleErr =30043,
+ BRLMDBFullErr = 30053,
+ BRLMDBBoundsError = 30063, /*attempting to delete a record not cantained in a region*/
+ NLMHashTableBoundaryErr = 30083,
+ BRLMDBInvalidRefErr = 31013, /*any time an operation witha NLMRef fails*/
+ 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: samba/source/Makefile.in
===================================================================
--- samba/source/Makefile.in.orig
+++ samba/source/Makefile.in
@@ -432,6 +432,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
@@ -1364,6 +1365,13 @@ bin/macosxfs.@SHLIBEXT@: proto_exists $(
# 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_MODULE) $(VFS_AUDIT_OBJ)
@@ -1524,7 +1532,6 @@ bin/timelimit@EXEEXT@: script/tests/time
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: samba/source/configure.in
===================================================================
--- samba/source/configure.in.orig
+++ samba/source/configure.in
@@ -816,8 +816,13 @@ main() {
# that needs to be removed. -- jpeach
SMBD_LIBS="$SMBD_LIBS -framework DirectoryService"
+ # 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*)
@@ -6205,6 +6210,7 @@ SMB_MODULE(vfs_prealloc, \$(VFS_PREALLOC
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: samba/source/smbd/open.c
===================================================================
--- samba/source/smbd/open.c.orig
+++ samba/source/smbd/open.c
@@ -1613,6 +1613,35 @@ NTSTATUS open_file_ntcreate(connection_s
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);
}
Index: samba/source/include/MacExtensions.h
===================================================================
--- samba/source/include/MacExtensions.h.orig
+++ samba/source/include/MacExtensions.h
@@ -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: samba/source/locking/posix.c
===================================================================
--- samba/source/locking/posix.c.orig
+++ samba/source/locking/posix.c
@@ -190,6 +190,10 @@ static BOOL posix_fcntl_lock(files_struc
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_st
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))) {
@@ -1058,13 +1066,17 @@ BOOL set_posix_lock_windows_flavour(file
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) {
@@ -1178,7 +1190,8 @@ BOOL release_posix_lock_windows_flavour(
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;
@@ -1189,6 +1202,7 @@ BOOL release_posix_lock_windows_flavour(
* Release the POSIX locks on the list of ranges returned.
*/
+ ret = True;
for(; ulist; ulist = ulist->next) {
offset = ulist->start;
count = ulist->size;
@@ -1197,7 +1211,9 @@ BOOL release_posix_lock_windows_flavour(
(double)offset, (double)count ));
if (!posix_fcntl_lock(fsp,SMB_F_SETLK,offset,count,F_UNLCK)) {
- ret = False;
+ if (errno != ENOTSUP) {
+ ret = False;
+ }
}
}
@@ -1242,6 +1258,10 @@ BOOL set_posix_lock_posix_flavour(files_
}
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) ));
@@ -1331,7 +1351,9 @@ BOOL release_posix_lock_posix_flavour(fi
(double)offset, (double)count ));
if (!posix_fcntl_lock(fsp,SMB_F_SETLK,offset,count,F_UNLCK)) {
- ret = False;
+ if (errno != ENOTSUP) {
+ ret = False;
+ }
}
}