Index: samba/source/modules/vfs_darwin_streams.c =================================================================== --- /dev/null +++ samba/source/modules/vfs_darwin_streams.c @@ -0,0 +1,2171 @@ +/* + * 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 + +#include + +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, + uint32_t sessionID, + uint32_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(uint32_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 +#include +#include + +#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); + } + } +} + +/* Return TRUE if we are only opening for attribute access. */ +static BOOL is_attribute_open(const struct files_struct * fsp) +{ + BOOL rwopen; + + rwopen = fsp->access_mask & (FILE_READ_DATA | FILE_WRITE_DATA | + GENERIC_READ_ACCESS | GENERIC_WRITE_ACCESS); + + return rwopen ? False : True; +} + +/* 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, + ("%u bytes of xattr names filtered down to %u bytes\n", + (unsigned)total, (unsigned)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) { + return 0; + } + + 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", + (unsigned)sbuf->st_size, (unsigned)sbuf->st_blocks, + (unsigned)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, (unsigned)sbuf->st_size, + (unsigned)sbuf->st_blocks, (unsigned)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, (unsigned)sbuf->st_size, + (unsigned)sbuf->st_blocks, (unsigned)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) && + !is_attribute_open(fsp)) { + 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) && + !is_attribute_open(fsp)) { + 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(uint32_t) + NAME_MAX + 1]; + struct attrreference * data = (struct attrreference *)attrbuf; + uint32_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(uint32_t)); + nmlen = (uint32_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 = %u\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, (unsigned)(*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 @@ -433,6 +433,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 @@ -1365,6 +1366,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) @@ -1525,7 +1533,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 @@ -831,8 +831,13 @@ main() { # that needs to be removed. -- jpeach SMBD_LIBS="$SMBD_LIBS $(MACOSX_LIBS)" + # 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*) @@ -6267,6 +6272,7 @@ SMB_MODULE(vfs_commit, \$(VFS_COMMIT_OBJ 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_notify_fam, \$(VFS_NOTIFY_FAM_OBJ), "bin/notify_fam.$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))) { @@ -1061,13 +1069,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) { @@ -1181,7 +1193,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; @@ -1192,6 +1205,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; @@ -1200,7 +1214,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; + } } } @@ -1245,6 +1261,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) )); @@ -1334,7 +1354,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; + } } }