>From fb6a0f5cf0a930a1054c7f1ed05649e27755a769 Mon Sep 17 00:00:00 2001 From: Abhidnya Joshi Date: Mon, 1 Sep 2014 14:10:10 +0530 Subject: [PATCH] s3: vfs module: Adding new vfs module for Symantec VxFS. This mainly handles ACL related functions. Signed-off-by: Abhidnya Joshi Reviewed-by: Jeremy Allison --- source3/modules/vfs_vxfs.c | 681 ++++++++++++++++++++++++++++++++++++++++++ source3/modules/wscript_build | 7 + source3/wscript | 10 + 3 files changed, 698 insertions(+) create mode 100644 source3/modules/vfs_vxfs.c diff --git a/source3/modules/vfs_vxfs.c b/source3/modules/vfs_vxfs.c new file mode 100644 index 0000000..f59331b --- /dev/null +++ b/source3/modules/vfs_vxfs.c @@ -0,0 +1,681 @@ +/* +Unix SMB/CIFS implementation. +Wrap VxFS calls in vfs functions. +This module is for ACL handling. + +Copyright (C) Symantec Corporation 2014 + +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 3 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, see . +*/ + +#include "includes.h" +#include "smbd/smbd.h" +#include "librpc/gen_ndr/ndr_xattr.h" +#include "../libcli/security/security.h" +#include "../librpc/gen_ndr/ndr_security.h" +#include "system/filesys.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_VFS + +#define MODULE_NAME "vxfs" +#define XATTR_USER_NTACL "user.NTACL" + +/* type values */ +#define VXFS_ACL_UNDEFINED_TYPE 0 +#define VXFS_ACL_USER_OBJ 1 +#define VXFS_ACL_GROUP_OBJ 2 +#define VXFS_ACL_USER 3 +#define VXFS_ACL_GROUP 4 +#define VXFS_ACL_OTHER 5 +#define VXFS_ACL_MASK 6 + + +/* + * Compare aces + * This will compare two ace entries for sorting + * each entry contains: type, perms and id + * Sort by type first, if type is same sort by id. + */ +static int vxfs_ace_cmp(const void *ace1, const void *ace2) +{ + int ret = 0; + uint16_t type_a1, type_a2; + uint32_t id_a1, id_a2; + + /* Type must be compared first */ + type_a1 = SVAL(ace1, 0); + type_a2 = SVAL(ace2, 0); + + ret = (type_a1 - type_a2); + if (!ret) { + /* Compare ID under type */ + /* skip perm thus take offset as 4*/ + id_a1 = IVAL(ace1, 4); + id_a2 = IVAL(ace2, 4); + ret = id_a1 - id_a2; + } + + return ret; +} + +static void vxfs_print_ace_buf(char *buf, int count) { + + int i, offset = 0; + uint16_t type, perm; + uint32_t id; + + DEBUG(10, ("vfs_vxfs: Printing aces:\n")); + for (i = 0; i < count; i++) { + type = SVAL(buf, offset); + offset += 2; + perm = SVAL(buf, offset); + offset += 2; + id = IVAL(buf, offset); + offset += 4; + + DEBUG(10, ("vfs_vxfs: type = %u, perm = %u, id = %u\n", + (unsigned int)type, (unsigned int)perm, + (unsigned int)id)); + } +} + +/* + * Sort aces so that comparing 2 ACLs will be straight forward. + * This function will fill buffer as follows: + * For each ace: + * 1. ace->a_type will be filled as first 2 bytes in buf. + * 2. ace->a_perm will be filled as next 2 bytes. + * 3. ace->xid will be filled as next 4 bytes. + * Thus each ace entry in buf is equal to 8 bytes. + * Also a_type is mapped to VXFS_ACL_* so that ordering aces + * becomes easy. + */ +static char * vxfs_sort_acl(SMB_ACL_T theacl, TALLOC_CTX *mem_ctx, + uint32_t o_uid, + uint32_t o_gid) { + + struct smb_acl_entry *smb_ace; + int i, count; + uint16_t type, perm; + uint32_t id; + int offset = 0; + char *buf = NULL; + + count = theacl->count; + + buf = talloc_zero_size(mem_ctx, count * 8); + if (!buf) { + return NULL; + } + + smb_ace = theacl->acl; + + for (i = 0; i < count; i++) { + /* Calculate type */ + /* Map type to SMB_ACL_* to VXFS_ACL_* */ + switch(smb_ace->a_type) { + case SMB_ACL_USER: + type = VXFS_ACL_USER; + break; + case SMB_ACL_USER_OBJ: + type = VXFS_ACL_USER_OBJ; + break; + case SMB_ACL_GROUP: + type = VXFS_ACL_GROUP; + break; + case SMB_ACL_GROUP_OBJ: + type = VXFS_ACL_GROUP_OBJ; + break; + case SMB_ACL_OTHER: + type = VXFS_ACL_OTHER; + break; + case SMB_ACL_MASK: + type = VXFS_ACL_MASK; + break; + default: + type = -1; + talloc_free(buf); + return NULL; + } + + type = type & 0xff; + + /* Calculate id: + * We get owner uid and owner group gid in o_uid and o_gid + * Put these ids instead of -1 + */ + switch(smb_ace->a_type) { + case SMB_ACL_USER: + id = smb_ace->info.user.uid; + break; + case SMB_ACL_GROUP: + id = smb_ace->info.group.gid; + break; + case SMB_ACL_USER_OBJ: + id = o_uid; + break; + case SMB_ACL_GROUP_OBJ: + id = o_gid; + break; + case SMB_ACL_MASK: + case SMB_ACL_OTHER: + id = -1; + break; + default: + /* Can't happen.. */ + id = -1; + break; + } + + /* Calculate perm */ + perm = smb_ace->a_perm & 0xff; + + /* TYPE is the first 2 bytes of an entry */ + SSVAL(buf, offset, type); + offset += 2; + + /* PERM is the next 2 bytes of an entry */ + SSVAL(buf, offset, perm); + offset += 2; + + /* ID is the last 4 bytes of an entry */ + SIVAL(buf, offset, id); + offset += 4; + + smb_ace++; + } + + qsort(buf, count, 8, vxfs_ace_cmp); + + DEBUG(10, ("vfs_vxfs: Print sorted aces:\n")); + vxfs_print_ace_buf(buf, count); + + return buf; +} + +/* This function gets e_buf as an arg which is sorted and created out of + * existing ACL. This function will compact this e_buf to c_buf where USER + * and GROUP aces matching with USER_OBJ and GROUP_OBJ will be merged + * respectively. + * This is similar to what posix_acls.c does. This will make sure existing + * acls are converted much similar to what posix_acls calculates. + */ + +static char * vxfs_compact_buf(char *e_buf, int *new_count, int count, + TALLOC_CTX *mem_ctx) +{ + int i, e_offset = 0, c_offset = 0; + uint16_t type, perm, o_perm; + uint32_t id, owner_id, group_id; + char *c_buf = NULL; + + + if (count < 2) { + return NULL; + } + + c_buf = talloc_zero_size(mem_ctx, count * 8); + if (!c_buf) { + return NULL; + } + + /*Copy first two enries from e_buf to c_buf + *These are USER_OBJ and GROUP_OBJ + */ + + memcpy(c_buf, e_buf, 16); + + (*new_count) = 2; + + owner_id = IVAL(e_buf, 4); + group_id = IVAL(e_buf, 12); + + c_offset = e_offset = 16; + + /* Start comparing other entries */ + for (i = 2; i < count; i++) { + + type = SVAL(e_buf, e_offset); + e_offset += 2; + perm = SVAL(e_buf, e_offset); + e_offset += 2; + id = IVAL(e_buf, e_offset); + e_offset += 4; + + switch(type) { + case VXFS_ACL_USER: + if (id == owner_id) { + o_perm = SVAL(c_buf, 2); + o_perm |= perm; + SSVAL(c_buf, 2, o_perm); + DEBUG(10, ("vfs_vxfs: merging with owner" + "e_type = %u," + "e_perm = %u," + "e_id = %u\n", (unsigned int)type, + (unsigned int)perm, + (unsigned int)id)); + continue; + } + break; + case VXFS_ACL_GROUP: + if (id == group_id) { + o_perm = SVAL(c_buf, 10); + o_perm |= perm; + SSVAL(c_buf, 10, o_perm); + DEBUG(10, ("vfs_vxfs: merging with owner group" + "e_type = %u," + "e_perm = %u," + "e_id = %u\n", (unsigned int)type, + (unsigned int)perm, + (unsigned int)id)); + continue; + } + break; + } + + SSVAL(c_buf, c_offset, type); + c_offset += 2; + + SSVAL(c_buf, c_offset, perm); + c_offset += 2; + + SIVAL(c_buf, c_offset, id); + c_offset += 4; + + (*new_count)++; + } + DEBUG(10, ("vfs_vxfs: new_count is %d\n", *new_count)); + return c_buf; +} + +/* Actually compare New ACL and existing ACL buf */ +static bool vxfs_compare_acls(char *e_buf, char *n_buf, int n_count, + int e_count) { + + uint16_t e_type, n_type, e_perm, n_perm; + uint32_t e_id, n_id; + int i, offset = 0; + + if (!e_buf && !n_buf) { + DEBUG(10, ("vfs_vxfs: Empty buffers!\n")); + return false; + } + + if ((e_count < 2) || (n_count < 2)) { + return false; + } + /*Get type from last entry from both buffers. + * It may or may not be ACL_MASK + */ + n_type = SVAL(n_buf, offset + (8 * (n_count-1))); + e_type = SVAL(e_buf, offset + (8 * (e_count-1))); + + /* Check for ACL_MASK entry properly. Handle all 4 cases*/ + + /* If ACL_MASK entry is present in any of the buffers, + * it will be always the last one. Calculate count to compare + * based on if ACL_MASK is present on new and existing ACL + */ + if ((n_type != VXFS_ACL_MASK) && (e_type == VXFS_ACL_MASK)){ + DEBUG(10, ("vfs_vxfs: New ACL does not have mask entry," + "reduce count by 1 and compare\n")); + e_count = e_count -1; + } + if ((n_type == VXFS_ACL_MASK) && (e_type != VXFS_ACL_MASK)){ + DEBUG(10, ("vfs_vxfs: new ACL to be set contains mask" + "existing ACL does not have mask entry\n" + "Need to set New ACL\n")); + return false; + } + + if (memcmp(e_buf, n_buf, (e_count * 8)) != 0) { + DEBUG(10, ("vfs_vxfs: Compare with memcmp," + "buffers not same!\n")); + return false; + } + + return true; +} + +/* In VxFS, POSIX ACLs are pointed by separate inode for each file/dir. + * However, files/dir share same POSIX ACL inode if ACLs are inherited + * from parent. + * To retain this behaviour, below function avoids ACL set call if + * underlying ACLs are already same and thus saves creating extra inode. + * + * This function will execute following steps: + * 1. Get existing ACL + * 2. Sort New ACL and existing ACL into buffers + * 3. Compact existing ACL buf + * 4. Finally compare New ACL buf and Compact buf + * 5. If same, return true + * 6. Else need to set New ACL + */ + +static bool vxfs_compare(connection_struct *conn, char *name, SMB_ACL_T the_acl, + SMB_ACL_TYPE_T the_acl_type) +{ + SMB_ACL_T existing_acl = NULL; + bool ret = false; + int i, count = 0; + TALLOC_CTX *mem_ctx = talloc_tos(); + char *existing_buf = NULL, *new_buf = NULL, *compact_buf = NULL; + struct smb_filename *smb_fname = NULL; + int status; + + DEBUG(10, ("vfs_vxfs: Getting existing ACL for %s\n", name)); + existing_acl = SMB_VFS_SYS_ACL_GET_FILE(conn, name, the_acl_type, + mem_ctx); + if (existing_acl == NULL) { + DEBUG(10, ("vfs_vxfs: Failed to get ACL\n")); + goto out; + } + + DEBUG(10, ("vfs_vxfs: Existing ACL count=%d\n", existing_acl->count)); + DEBUG(10, ("vfs_vxfs: New ACL count=%d\n", the_acl->count)); + + if (existing_acl->count == 0) { + DEBUG(10, ("vfs_vxfs: ACL count is 0, Need to set\n")); + goto out; + } + + smb_fname = synthetic_smb_fname(mem_ctx, name, NULL, NULL); + if (smb_fname == NULL) { + DEBUG(10, ("vfs_vxfs: Failed to create smb_fname\n")); + goto out; + } + + status = SMB_VFS_STAT(conn, smb_fname); + if (status == -1) { + DEBUG(10, ("vfs_vxfs: stat failed!\n")); + goto out; + } + + DEBUG(10, ("vfs_vxfs: Sorting existing ACL\n")); + existing_buf = vxfs_sort_acl(existing_acl, mem_ctx, + smb_fname->st.st_ex_uid, + smb_fname->st.st_ex_gid); + if (!existing_buf) + goto out; + + DEBUG(10, ("vfs_vxfs: Sorting new ACL\n")); + new_buf = vxfs_sort_acl(the_acl, mem_ctx, smb_fname->st.st_ex_uid, + smb_fname->st.st_ex_gid); + if (!new_buf) { + goto out; + } + + DEBUG(10, ("vfs_vxfs: Compact existing buf\n")); + compact_buf = vxfs_compact_buf(existing_buf, &count, + existing_acl->count, + mem_ctx); + if (!compact_buf) { + goto out; + } + + vxfs_print_ace_buf(compact_buf, count); + + /* COmpare ACLs only if count is same or mismatch by 1 */ + if ((count == the_acl->count) || + (count == the_acl->count + 1) || + (count+1 == the_acl->count)) { + + if (vxfs_compare_acls(compact_buf, new_buf, the_acl->count, + count)) { + DEBUG(10, ("vfs_vxfs: ACLs matched. Not setting.\n")); + ret = true; + goto out; + } else + DEBUG(10, ("vfs_vxfs: ACLs NOT matched. Setting\n")); + } else { + DEBUG(10, ("vfs_vxfs: ACLs count does not match. Setting\n")); + } + +out: + + TALLOC_FREE(existing_acl); + TALLOC_FREE(smb_fname); + TALLOC_FREE(existing_buf); + TALLOC_FREE(compact_buf); + TALLOC_FREE(new_buf); + + return ret; +} + +static int vxfs_sys_acl_set_fd(vfs_handle_struct *handle, files_struct *fsp, + SMB_ACL_T theacl) +{ + + if (vxfs_compare(fsp->conn, fsp->fsp_name->base_name, theacl, + SMB_ACL_TYPE_ACCESS)) { + return 0; + } + + return SMB_VFS_NEXT_SYS_ACL_SET_FD(handle, fsp, theacl); +} + +static int vxfs_sys_acl_set_file(vfs_handle_struct *handle, const char *name, + SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl) +{ + if (vxfs_compare(handle->conn, (char *)name, theacl, acltype)) { + return 0; + } + + return SMB_VFS_NEXT_SYS_ACL_SET_FILE(handle, name, acltype, theacl); +} + +static int vxfs_set_xattr(struct vfs_handle_struct *handle, const char *path, + const char *name, const void *value, size_t size, + int flags){ + + DEBUG(10, ("In vxfs_set_xattr\n")); + + if (strcmp(name, XATTR_NTACL_NAME) == 0) { + return SMB_VFS_NEXT_SETXATTR(handle, path, XATTR_USER_NTACL, + value, size, flags); + } + + /* Clients can't set XATTR_USER_NTACL directly. */ + if (strcasecmp(name, XATTR_USER_NTACL) == 0) { + errno = EACCES; + return -1; + } + + return SMB_VFS_NEXT_SETXATTR(handle, path, name, value, size, flags); +} + +static int vxfs_fset_xattr(struct vfs_handle_struct *handle, + struct files_struct *fsp, const char *name, + const void *value, size_t size, int flags){ + + DEBUG(10, ("In vxfs_fset_xattr\n")); + + if (strcmp(name, XATTR_NTACL_NAME) == 0) { + return SMB_VFS_NEXT_FSETXATTR(handle, fsp, XATTR_USER_NTACL, + value, size, flags); + } + + /* Clients can't set XATTR_USER_NTACL directly. */ + if (strcasecmp(name, XATTR_USER_NTACL) == 0) { + errno = EACCES; + return -1; + } + + return SMB_VFS_NEXT_FSETXATTR(handle, fsp, name, value, size, flags); +} + +static ssize_t vxfs_get_xattr(struct vfs_handle_struct *handle, + const char *path, const char *name, + void *value, size_t size){ + + DEBUG(10, ("In vxfs_get_xattr\n")); + + if (strcmp(name, XATTR_NTACL_NAME) == 0) { + return SMB_VFS_NEXT_GETXATTR(handle, path, XATTR_USER_NTACL, + value, size); + } + + /* Clients can't see XATTR_USER_NTACL directly. */ + if (strcasecmp(name, XATTR_USER_NTACL) == 0) { + errno = ENOATTR; + return -1; + } + + return SMB_VFS_NEXT_GETXATTR(handle, path, name, value, size); +} + +static ssize_t vxfs_fget_xattr(struct vfs_handle_struct *handle, + struct files_struct *fsp, const char *name, + void *value, size_t size){ + + DEBUG(10, ("In vxfs_fget_xattr\n")); + + if (strcmp(name, XATTR_NTACL_NAME) == 0) { + return SMB_VFS_NEXT_FGETXATTR(handle, fsp, XATTR_USER_NTACL, + value, size); + } + + /* Clients can't see XATTR_USER_NTACL directly. */ + if (strcasecmp(name, XATTR_USER_NTACL) == 0) { + errno = ENOATTR; + return -1; + } + + return SMB_VFS_NEXT_FGETXATTR(handle, fsp, name, value, size); +} + +static int vxfs_remove_xattr(struct vfs_handle_struct *handle, + const char *path, const char *name){ + + DEBUG(10, ("In vxfs_remove_xattr\n")); + + if (strcmp(name, XATTR_NTACL_NAME) == 0) { + return SMB_VFS_NEXT_REMOVEXATTR(handle, path, XATTR_USER_NTACL); + } + + /* Clients can't see XATTR_USER_NTACL directly. */ + if (strcasecmp(name, XATTR_USER_NTACL) == 0) { + errno = ENOATTR; + return -1; + } + + return SMB_VFS_NEXT_REMOVEXATTR(handle, path, name); +} + +static int vxfs_fremove_xattr(struct vfs_handle_struct *handle, + struct files_struct *fsp, const char *name){ + + DEBUG(10, ("In vxfs_fremove_xattr\n")); + + if (strcmp(name, XATTR_NTACL_NAME) == 0) { + return SMB_VFS_NEXT_FREMOVEXATTR(handle, fsp, XATTR_USER_NTACL); + } + + /* Clients can't remove XATTR_USER_NTACL directly. */ + if (strcasecmp(name, XATTR_USER_NTACL) == 0) { + errno = ENOATTR; + return -1; + } + + return SMB_VFS_NEXT_FREMOVEXATTR(handle, fsp, name); +} + +static size_t vxfs_filter_list(char *list, size_t size) +{ + char *str = list; + + while (str - list < size) { + size_t element_len = strlen(str) + 1; + if (strcasecmp(str, XATTR_USER_NTACL) == 0) { + memmove(str, + str + element_len, + size - (str - list) - element_len); + size -= element_len; + continue; + } + str += element_len; + } + return size; +} + +static ssize_t vxfs_listxattr(vfs_handle_struct *handle, const char *path, + char *list, size_t size) +{ + ssize_t result; + + result = SMB_VFS_NEXT_LISTXATTR(handle, path, list, size); + + if (result <= 0) { + return result; + } + + /* Remove any XATTR_USER_NTACL elements from the returned list. */ + result = vxfs_filter_list(list, result); + + return result; +} + +static ssize_t vxfs_flistxattr(struct vfs_handle_struct *handle, + struct files_struct *fsp, char *list, + size_t size) +{ + ssize_t result; + + result = SMB_VFS_NEXT_FLISTXATTR(handle, fsp, list, size); + + if (result <= 0) { + return result; + } + + /* Remove any XATTR_USER_NTACL elements from the returned list. */ + result = vxfs_filter_list(list, result); + + return result; +} + +static int vfs_vxfs_connect(struct vfs_handle_struct *handle, + const char *service, const char *user) +{ + + int ret = SMB_VFS_NEXT_CONNECT(handle, service, user); + + if (ret < 0) { + return ret; + } + return 0; +} + +static struct vfs_fn_pointers vfs_vxfs_fns = { + .connect_fn = vfs_vxfs_connect, + + .sys_acl_set_file_fn = vxfs_sys_acl_set_file, + .sys_acl_set_fd_fn = vxfs_sys_acl_set_fd, + + .getxattr_fn = vxfs_get_xattr, + .fgetxattr_fn = vxfs_fget_xattr, + .listxattr_fn = vxfs_listxattr, + .flistxattr_fn = vxfs_flistxattr, + .removexattr_fn = vxfs_remove_xattr, + .fremovexattr_fn = vxfs_fremove_xattr, + .setxattr_fn = vxfs_set_xattr, + .fsetxattr_fn = vxfs_fset_xattr, +}; + +NTSTATUS vfs_vxfs_init(void); +NTSTATUS vfs_vxfs_init(void) +{ + return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "vxfs", + &vfs_vxfs_fns); +} diff --git a/source3/modules/wscript_build b/source3/modules/wscript_build index a4a56f7..e5d04f7 100644 --- a/source3/modules/wscript_build +++ b/source3/modules/wscript_build @@ -505,3 +505,10 @@ bld.SAMBA3_MODULE('vfs_snapper', init_function='', internal_module=bld.SAMBA3_IS_STATIC_MODULE('vfs_snapper'), enabled=bld.SAMBA3_IS_ENABLED_MODULE('vfs_snapper')) + +bld.SAMBA3_MODULE('vfs_vxfs', + subsystem='vfs', + source='vfs_vxfs.c', + init_function='', + internal_module=bld.SAMBA3_IS_STATIC_MODULE('vfs_vxfs'), + enabled=bld.SAMBA3_IS_ENABLED_MODULE('vfs_vxfs')) diff --git a/source3/wscript b/source3/wscript index 44cce16..b285c5f 100644 --- a/source3/wscript +++ b/source3/wscript @@ -65,6 +65,10 @@ def set_options(opt): opt.SAMBA3_ADD_OPTION('glusterfs', with_name="enable", without_name="disable", default=True) + opt.add_option('--enable-vxfs', + help=("enable support for VxFS (default=no)"), + action="store_true", dest='enable_vxfs', default=False) + def configure(conf): from samba_utils import TO_LIST @@ -1796,6 +1800,9 @@ main() { conf.SET_TARGET_TYPE('gfapi', 'EMPTY') conf.undefine('HAVE_GLUSTERFS') + if Options.options.enable_vxfs: + conf.DEFINE('HAVE_VXFS', '1') + if conf.check_cfg(package='dbus-1', args='--cflags --libs', msg='Checking for dbus', uselib_store="DBUS-1"): if (conf.CHECK_HEADERS('dbus/dbus.h', lib='dbus-1') @@ -1905,6 +1912,9 @@ main() { if conf.CONFIG_SET('HAVE_GLUSTERFS'): default_shared_modules.extend(TO_LIST('vfs_glusterfs')) + if conf.CONFIG_SET('HAVE_VXFS'): + default_shared_modules.extend(TO_LIST('vfs_vxfs')) + if conf.CONFIG_SET('HAVE_DBUS'): default_shared_modules.extend(TO_LIST('vfs_snapper')) -- 2.1.0.rc2.206.gedb03e5