diff options
Diffstat (limited to 'source3/modules/vfs_gpfs.c')
-rw-r--r-- | source3/modules/vfs_gpfs.c | 281 |
1 files changed, 227 insertions, 54 deletions
diff --git a/source3/modules/vfs_gpfs.c b/source3/modules/vfs_gpfs.c index bcf61f3bc7..d10906dfb1 100644 --- a/source3/modules/vfs_gpfs.c +++ b/source3/modules/vfs_gpfs.c @@ -30,7 +30,7 @@ #include <gpfs_gpl.h> #include "nfs4_acls.h" - +#include "vfs_gpfs.h" static int vfs_gpfs_kernel_flock(vfs_handle_struct *handle, files_struct *fsp, uint32 share_mode) @@ -153,7 +153,7 @@ static int gpfs_get_nfs4_acl(const char *fname, SMB4ACL_T **ppacl) DEBUG(10, ("gpfs_get_nfs4_acl invoked for %s\n", fname)); /* First get the real acl length */ - gacl = gpfs_getacl_alloc(fname, GPFS_ACL_TYPE_NFS4); + gacl = gpfs_getacl_alloc(fname, 0); if (gacl == NULL) { DEBUG(9, ("gpfs_getacl failed for %s with %s\n", fname, strerror(errno))); @@ -208,10 +208,10 @@ static int gpfs_get_nfs4_acl(const char *fname, SMB4ACL_T **ppacl) if (i > 0 && gace->aceType == SMB_ACE4_ACCESS_DENIED_ACE_TYPE) { struct gpfs_ace_v4 *prev = &gacl->ace_v4[i-1]; if (prev->aceType == SMB_ACE4_ACCESS_ALLOWED_ACE_TYPE && - prev->aceFlags == gace->aceFlags && - prev->aceIFlags == gace->aceIFlags && - (gace->aceMask & prev->aceMask) == 0 && - gace->aceWho == prev->aceWho) { + prev->aceFlags == gace->aceFlags && + prev->aceIFlags == gace->aceIFlags && + (gace->aceMask & prev->aceMask) == 0 && + gace->aceWho == prev->aceWho) { /* its redundent - skip it */ continue; } @@ -256,7 +256,7 @@ static NTSTATUS gpfsacl_get_nt_acl(vfs_handle_struct *handle, int result; *ppdesc = NULL; - result = gpfs_get_nfs4_acl(fsp->fsp_name, &pacl); + result = gpfs_get_nfs4_acl(name, &pacl); if (result == 0) return smb_get_nt_acl_nfs4(handle->conn, name, security_info, ppdesc, pacl); @@ -301,8 +301,31 @@ static bool gpfsacl_process_smbacl(files_struct *fsp, SMB4ACL_T *smbacl) gace->aceType = aceprop->aceType; gace->aceFlags = aceprop->aceFlags; gace->aceMask = aceprop->aceMask; + + /* + * GPFS can't distinguish between WRITE and APPEND on + * files, so one being set without the other is an + * error. Sorry for the many ()'s :-) + */ + + if (!fsp->is_directory + && + ((((gace->aceMask & ACE4_MASK_WRITE) == 0) + && ((gace->aceMask & ACE4_MASK_APPEND) != 0)) + || + (((gace->aceMask & ACE4_MASK_WRITE) != 0) + && ((gace->aceMask & ACE4_MASK_APPEND) == 0))) + && + lp_parm_bool(fsp->conn->params->service, "gpfs", + "merge_writeappend", True)) { + DEBUG(2, ("vfs_gpfs.c: file [%s]: ACE contains " + "WRITE^APPEND, setting WRITE|APPEND\n", + fsp->fsp_name)); + gace->aceMask |= ACE4_MASK_WRITE|ACE4_MASK_APPEND; + } + gace->aceIFlags = (aceprop->flags&SMB_ACE4_ID_SPECIAL) ? ACE4_IFLAG_SPECIAL_ID : 0; - + if (aceprop->flags&SMB_ACE4_ID_SPECIAL) { switch(aceprop->who.special_id) @@ -347,7 +370,7 @@ static NTSTATUS gpfsacl_set_nt_acl_internal(files_struct *fsp, uint32 security_i struct gpfs_acl *acl; NTSTATUS result = NT_STATUS_ACCESS_DENIED; - acl = gpfs_getacl_alloc(fsp->fsp_name, GPFS_ACL_TYPE_ACCESS); + acl = gpfs_getacl_alloc(fsp->fsp_name, 0); if (acl == NULL) return result; @@ -628,75 +651,225 @@ int gpfsacl_sys_acl_delete_def_file(vfs_handle_struct *handle, return -1; } +/* + * Assumed: mode bits are shiftable and standard + * Output: the new aceMask field for an smb nfs4 ace + */ +static uint32 gpfsacl_mask_filter(uint32 aceType, uint32 aceMask, uint32 rwx) +{ + const uint32 posix_nfs4map[3] = { + SMB_ACE4_EXECUTE, /* execute */ + SMB_ACE4_WRITE_DATA | SMB_ACE4_APPEND_DATA, /* write; GPFS specific */ + SMB_ACE4_READ_DATA /* read */ + }; + int i; + uint32_t posix_mask = 0x01; + uint32_t posix_bit; + uint32_t nfs4_bits; + + for(i=0; i<3; i++) { + nfs4_bits = posix_nfs4map[i]; + posix_bit = rwx & posix_mask; + + if (aceType==SMB_ACE4_ACCESS_ALLOWED_ACE_TYPE) { + if (posix_bit) + aceMask |= nfs4_bits; + else + aceMask &= ~nfs4_bits; + } else { + /* add deny bits when suitable */ + if (!posix_bit) + aceMask |= nfs4_bits; + else + aceMask &= ~nfs4_bits; + } /* other ace types are unexpected */ + + posix_mask <<= 1; + } + + return aceMask; +} + +static int gpfsacl_emu_chmod(const char *path, mode_t mode) +{ + SMB4ACL_T *pacl = NULL; + int result; + bool haveAllowEntry[SMB_ACE4_WHO_EVERYONE + 1] = {False, False, False, False}; + int i; + files_struct fake_fsp; /* TODO: rationalize parametrization */ + SMB4ACE_T *smbace; + + DEBUG(10, ("gpfsacl_emu_chmod invoked for %s mode %o\n", path, mode)); + + result = gpfs_get_nfs4_acl(path, &pacl); + if (result) + return result; + + if (mode & ~(S_IRWXU | S_IRWXG | S_IRWXO)) { + DEBUG(2, ("WARNING: cutting extra mode bits %o on %s\n", mode, path)); + } + + for (smbace=smb_first_ace4(pacl); smbace!=NULL; smbace = smb_next_ace4(smbace)) { + SMB_ACE4PROP_T *ace = smb_get_ace4(smbace); + uint32_t specid = ace->who.special_id; + + if (ace->flags&SMB_ACE4_ID_SPECIAL && + ace->aceType<=SMB_ACE4_ACCESS_DENIED_ACE_TYPE && + specid <= SMB_ACE4_WHO_EVERYONE) { + + uint32_t newMask; + + if (ace->aceType==SMB_ACE4_ACCESS_ALLOWED_ACE_TYPE) + haveAllowEntry[specid] = True; + + /* mode >> 6 for @owner, mode >> 3 for @group, + * mode >> 0 for @everyone */ + newMask = gpfsacl_mask_filter(ace->aceType, ace->aceMask, + mode >> ((SMB_ACE4_WHO_EVERYONE - specid) * 3)); + if (ace->aceMask!=newMask) { + DEBUG(10, ("ace changed for %s (%o -> %o) id=%d\n", + path, ace->aceMask, newMask, specid)); + } + ace->aceMask = newMask; + } + } + + /* make sure we have at least ALLOW entries + * for all the 3 special ids (@EVERYONE, @OWNER, @GROUP) + * - if necessary + */ + for(i = SMB_ACE4_WHO_OWNER; i<=SMB_ACE4_WHO_EVERYONE; i++) { + SMB_ACE4PROP_T ace; + + if (haveAllowEntry[i]==True) + continue; + + memset(&ace, 0, sizeof(SMB_ACE4PROP_T)); + ace.aceType = SMB_ACE4_ACCESS_ALLOWED_ACE_TYPE; + ace.flags |= SMB_ACE4_ID_SPECIAL; + ace.who.special_id = i; + + if (i==SMB_ACE4_WHO_GROUP) /* not sure it's necessary... */ + ace.aceFlags |= SMB_ACE4_IDENTIFIER_GROUP; + + ace.aceMask = gpfsacl_mask_filter(ace.aceType, ace.aceMask, + mode >> ((SMB_ACE4_WHO_EVERYONE - i) * 3)); + + /* don't add unnecessary aces */ + if (!ace.aceMask) + continue; + + /* we add it to the END - as windows expects allow aces */ + smb_add_ace4(pacl, &ace); + DEBUG(10, ("Added ALLOW ace for %s, mode=%o, id=%d, aceMask=%x\n", + path, mode, i, ace.aceMask)); + } + + /* don't add complementary DENY ACEs here */ + memset(&fake_fsp, 0, sizeof(struct files_struct)); + fake_fsp.fsp_name = (char *)path; /* no file_new is needed here */ + + /* put the acl */ + if (gpfsacl_process_smbacl(&fake_fsp, pacl) == False) + return -1; + return 0; /* ok for [f]chmod */ +} + static int vfs_gpfs_chmod(vfs_handle_struct *handle, const char *path, mode_t mode) { SMB_STRUCT_STAT st; + int rc; + if (SMB_VFS_NEXT_STAT(handle, path, &st) != 0) { - return -1; + return -1; } + /* avoid chmod() if possible, to preserve acls */ if ((st.st_mode & ~S_IFMT) == mode) { - return 0; + return 0; } - return SMB_VFS_NEXT_CHMOD(handle, path, mode); + + rc = gpfsacl_emu_chmod(path, mode); + if (rc == 1) + return SMB_VFS_NEXT_CHMOD(handle, path, mode); + return rc; } static int vfs_gpfs_fchmod(vfs_handle_struct *handle, files_struct *fsp, mode_t mode) { SMB_STRUCT_STAT st; + int rc; + if (SMB_VFS_NEXT_FSTAT(handle, fsp, &st) != 0) { - return -1; + return -1; } + /* avoid chmod() if possible, to preserve acls */ if ((st.st_mode & ~S_IFMT) == mode) { - return 0; + return 0; } - return SMB_VFS_NEXT_FCHMOD(handle, fsp, mode); + + rc = gpfsacl_emu_chmod(fsp->fsp_name, mode); + if (rc == 1) + return SMB_VFS_NEXT_FCHMOD(handle, fsp, mode); + return rc; } /* VFS operations structure */ static vfs_op_tuple gpfs_op_tuples[] = { - - { SMB_VFS_OP(vfs_gpfs_kernel_flock), SMB_VFS_OP_KERNEL_FLOCK, - SMB_VFS_LAYER_OPAQUE }, - - { SMB_VFS_OP(vfs_gpfs_setlease), SMB_VFS_OP_LINUX_SETLEASE, - SMB_VFS_LAYER_OPAQUE }, - - { SMB_VFS_OP(gpfsacl_fget_nt_acl), SMB_VFS_OP_FGET_NT_ACL, - SMB_VFS_LAYER_TRANSPARENT }, - - { SMB_VFS_OP(gpfsacl_get_nt_acl), SMB_VFS_OP_GET_NT_ACL, - SMB_VFS_LAYER_TRANSPARENT }, - - { SMB_VFS_OP(gpfsacl_fset_nt_acl), SMB_VFS_OP_FSET_NT_ACL, - SMB_VFS_LAYER_TRANSPARENT }, - - { SMB_VFS_OP(gpfsacl_set_nt_acl), SMB_VFS_OP_SET_NT_ACL, - SMB_VFS_LAYER_TRANSPARENT }, - - { SMB_VFS_OP(gpfsacl_sys_acl_get_file), SMB_VFS_OP_SYS_ACL_GET_FILE, - SMB_VFS_LAYER_TRANSPARENT }, - - { SMB_VFS_OP(gpfsacl_sys_acl_get_fd), SMB_VFS_OP_SYS_ACL_GET_FD, - SMB_VFS_LAYER_TRANSPARENT }, - - { SMB_VFS_OP(gpfsacl_sys_acl_set_file), SMB_VFS_OP_SYS_ACL_SET_FILE, - SMB_VFS_LAYER_TRANSPARENT }, - - { SMB_VFS_OP(gpfsacl_sys_acl_set_fd), SMB_VFS_OP_SYS_ACL_SET_FD, - SMB_VFS_LAYER_TRANSPARENT }, - + + { SMB_VFS_OP(vfs_gpfs_kernel_flock), + SMB_VFS_OP_KERNEL_FLOCK, + SMB_VFS_LAYER_OPAQUE }, + + { SMB_VFS_OP(vfs_gpfs_setlease), + SMB_VFS_OP_LINUX_SETLEASE, + SMB_VFS_LAYER_OPAQUE }, + + { SMB_VFS_OP(gpfsacl_fget_nt_acl), + SMB_VFS_OP_FGET_NT_ACL, + SMB_VFS_LAYER_TRANSPARENT }, + + { SMB_VFS_OP(gpfsacl_get_nt_acl), + SMB_VFS_OP_GET_NT_ACL, + SMB_VFS_LAYER_TRANSPARENT }, + + { SMB_VFS_OP(gpfsacl_fset_nt_acl), + SMB_VFS_OP_FSET_NT_ACL, + SMB_VFS_LAYER_TRANSPARENT }, + + { SMB_VFS_OP(gpfsacl_set_nt_acl), + SMB_VFS_OP_SET_NT_ACL, + SMB_VFS_LAYER_TRANSPARENT }, + + { SMB_VFS_OP(gpfsacl_sys_acl_get_file), + SMB_VFS_OP_SYS_ACL_GET_FILE, + SMB_VFS_LAYER_TRANSPARENT }, + + { SMB_VFS_OP(gpfsacl_sys_acl_get_fd), + SMB_VFS_OP_SYS_ACL_GET_FD, + SMB_VFS_LAYER_TRANSPARENT }, + + { SMB_VFS_OP(gpfsacl_sys_acl_set_file), + SMB_VFS_OP_SYS_ACL_SET_FILE, + SMB_VFS_LAYER_TRANSPARENT }, + + { SMB_VFS_OP(gpfsacl_sys_acl_set_fd), + SMB_VFS_OP_SYS_ACL_SET_FD, + SMB_VFS_LAYER_TRANSPARENT }, + { SMB_VFS_OP(gpfsacl_sys_acl_delete_def_file), - SMB_VFS_OP_SYS_ACL_DELETE_DEF_FILE, - SMB_VFS_LAYER_TRANSPARENT }, - - { SMB_VFS_OP(vfs_gpfs_chmod), SMB_VFS_OP_CHMOD, - SMB_VFS_LAYER_TRANSPARENT }, - - { SMB_VFS_OP(vfs_gpfs_fchmod), SMB_VFS_OP_FCHMOD, - SMB_VFS_LAYER_TRANSPARENT }, + SMB_VFS_OP_SYS_ACL_DELETE_DEF_FILE, + SMB_VFS_LAYER_TRANSPARENT }, + + { SMB_VFS_OP(vfs_gpfs_chmod), + SMB_VFS_OP_CHMOD, + SMB_VFS_LAYER_TRANSPARENT }, + + { SMB_VFS_OP(vfs_gpfs_fchmod), + SMB_VFS_OP_FCHMOD, + SMB_VFS_LAYER_TRANSPARENT }, { SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP } |