s3:vfs_gpfs use non_posix_sys_acl_blob_get_*_helper
[obnox/samba/samba-obnox.git] / source3 / modules / vfs_gpfs.c
index 412c48c440462cbe5969d18b6f9bb2f6eaa1f5c8..d098bdee04c4a5e4e9fb1c0971a4109f2bbc14dc 100644 (file)
@@ -25,6 +25,7 @@
 #include "smbd/smbd.h"
 #include "librpc/gen_ndr/ndr_xattr.h"
 #include "include/smbprofile.h"
+#include "modules/non_posix_acls.h"
 
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_VFS
@@ -50,7 +51,7 @@ struct gpfs_config_data {
 };
 
 
-static int vfs_gpfs_kernel_flock(vfs_handle_struct *handle, files_struct *fsp, 
+static int vfs_gpfs_kernel_flock(vfs_handle_struct *handle, files_struct *fsp,
                                 uint32 share_mode, uint32 access_mask)
 {
 
@@ -61,12 +62,15 @@ static int vfs_gpfs_kernel_flock(vfs_handle_struct *handle, files_struct *fsp,
                                struct gpfs_config_data,
                                return -1);
 
+       if(!config->sharemodes) {
+               return 0;
+       }
+
        START_PROFILE(syscall_kernel_flock);
 
        kernel_flock(fsp->fh->fd, share_mode, access_mask);
 
-       if (config->sharemodes
-               && !set_gpfs_sharemode(fsp, access_mask, fsp->share_access)) {
+       if (!set_gpfs_sharemode(fsp, access_mask, fsp->share_access)) {
                ret = -1;
        }
 
@@ -216,6 +220,79 @@ static void gpfs_dumpacl(int level, struct gpfs_acl *gacl)
        }
 }
 
+/*
+ * get the ACL from GPFS, allocated on the specified mem_ctx
+ * internally retries when initial buffer was too small
+ *
+ * caller needs to cast result to either
+ * raw = yes: struct gpfs_opaque_acl
+ * raw = no: struct gpfs_acl
+ *
+ */
+static void *vfs_gpfs_getacl(TALLOC_CTX *mem_ctx,
+                        const char *fname,
+                        const bool raw,
+                        const gpfs_aclType_t type)
+{
+
+       void *aclbuf;
+       size_t size = 512;
+       int ret, flags;
+       unsigned int *len;
+       size_t struct_size;
+
+again:
+
+       aclbuf = talloc_zero_size(mem_ctx, size);
+       if (aclbuf == NULL) {
+               errno = ENOMEM;
+               return NULL;
+       }
+
+       if (raw) {
+               struct gpfs_opaque_acl *buf = (struct gpfs_opaque_acl *) aclbuf;
+               buf->acl_type = type;
+               flags = GPFS_GETACL_NATIVE;
+               len = (unsigned int *) &(buf->acl_buffer_len);
+               struct_size = sizeof(struct gpfs_opaque_acl);
+       } else {
+               struct gpfs_acl *buf = (struct gpfs_acl *) aclbuf;
+               buf->acl_type = type;
+               flags = GPFS_GETACL_STRUCT;
+               len = &(buf->acl_len);
+               struct_size = sizeof(struct gpfs_acl);
+       }
+
+       /* set the length of the buffer as input value */
+       *len = size;
+
+       errno = 0;
+       ret = smbd_gpfs_getacl((char *)fname, flags, aclbuf);
+       if ((ret != 0) && (errno == ENOSPC)) {
+               /*
+                * get the size needed to accommodate the complete buffer
+                *
+                * the value returned only applies to the ACL blob in the
+                * struct so make sure to also have headroom for the first
+                * struct members by adding room for the complete struct
+                * (might be a few bytes too much then)
+                */
+               size = *len + struct_size;
+               talloc_free(aclbuf);
+               DEBUG(10, ("Increasing ACL buffer size to %zu\n", size));
+               goto again;
+       }
+
+       if (ret != 0) {
+               DEBUG(5, ("smbd_gpfs_getacl failed with %s\n",
+                         strerror(errno)));
+               talloc_free(aclbuf);
+               return NULL;
+       }
+
+       return aclbuf;
+}
+
 static struct gpfs_acl *gpfs_getacl_alloc(const char *fname, gpfs_aclType_t type)
 {
        struct gpfs_acl *acl;
@@ -239,6 +316,7 @@ static struct gpfs_acl *gpfs_getacl_alloc(const char *fname, gpfs_aclType_t type
                struct gpfs_acl *new_acl = (struct gpfs_acl *)TALLOC_SIZE(
                        mem_ctx, acl->acl_len + sizeof(struct gpfs_acl));
                if (new_acl == NULL) {
+                       talloc_free(acl);
                        errno = ENOMEM;
                        return NULL;
                }
@@ -247,13 +325,14 @@ static struct gpfs_acl *gpfs_getacl_alloc(const char *fname, gpfs_aclType_t type
                new_acl->acl_level = acl->acl_level;
                new_acl->acl_version = acl->acl_version;
                new_acl->acl_type = acl->acl_type;
+               talloc_free(acl);
                acl = new_acl;
 
                ret = smbd_gpfs_getacl((char *)fname, GPFS_GETACL_STRUCT, acl);
        }
-       if (ret != 0)
-       {
+       if (ret != 0) {
                DEBUG(8, ("smbd_gpfs_getacl failed with %s\n",strerror(errno)));
+               talloc_free(acl);
                return NULL;
        }
 
@@ -283,6 +362,7 @@ static int gpfs_get_nfs4_acl(const char *fname, SMB4ACL_T **ppacl)
        if (gacl->acl_type != GPFS_ACL_TYPE_NFS4) {
                DEBUG(10, ("Got non-nfsv4 acl\n"));
                /* Retry with POSIX ACLs check */
+               talloc_free(gacl);
                return 1;
        }
 
@@ -348,6 +428,7 @@ static int gpfs_get_nfs4_acl(const char *fname, SMB4ACL_T **ppacl)
 
 static NTSTATUS gpfsacl_fget_nt_acl(vfs_handle_struct *handle,
        files_struct *fsp, uint32 security_info,
+       TALLOC_CTX *mem_ctx,
        struct security_descriptor **ppdesc)
 {
        SMB4ACL_T *pacl = NULL;
@@ -361,17 +442,18 @@ static NTSTATUS gpfsacl_fget_nt_acl(vfs_handle_struct *handle,
                                return NT_STATUS_INTERNAL_ERROR);
 
        if (!config->acl) {
-               return SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info, ppdesc);
+               return SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
+                                               mem_ctx, ppdesc);
        }
 
        result = gpfs_get_nfs4_acl(fsp->fsp_name->base_name, &pacl);
 
        if (result == 0)
-               return smb_fget_nt_acl_nfs4(fsp, security_info, ppdesc, pacl);
+               return smb_fget_nt_acl_nfs4(fsp, security_info, mem_ctx, ppdesc, pacl);
 
        if (result > 0) {
                DEBUG(10, ("retrying with posix acl...\n"));
-               return posix_fget_nt_acl(fsp, security_info, ppdesc);
+               return posix_fget_nt_acl(fsp, security_info, mem_ctx, ppdesc);
        }
 
        /* GPFS ACL was not read, something wrong happened, error code is set in errno */
@@ -380,7 +462,8 @@ static NTSTATUS gpfsacl_fget_nt_acl(vfs_handle_struct *handle,
 
 static NTSTATUS gpfsacl_get_nt_acl(vfs_handle_struct *handle,
        const char *name,
-       uint32 security_info, struct security_descriptor **ppdesc)
+       uint32 security_info,
+       TALLOC_CTX *mem_ctx, struct security_descriptor **ppdesc)
 {
        SMB4ACL_T *pacl = NULL;
        int     result;
@@ -393,17 +476,20 @@ static NTSTATUS gpfsacl_get_nt_acl(vfs_handle_struct *handle,
                                return NT_STATUS_INTERNAL_ERROR);
 
        if (!config->acl) {
-               return SMB_VFS_NEXT_GET_NT_ACL(handle, name, security_info, ppdesc);
+               return SMB_VFS_NEXT_GET_NT_ACL(handle, name, security_info,
+                                              mem_ctx, ppdesc);
        }
 
        result = gpfs_get_nfs4_acl(name, &pacl);
 
        if (result == 0)
-               return smb_get_nt_acl_nfs4(handle->conn, name, security_info, ppdesc, pacl);
+               return smb_get_nt_acl_nfs4(handle->conn, name, security_info,
+                                          mem_ctx, ppdesc, pacl);
 
        if (result > 0) {
                DEBUG(10, ("retrying with posix acl...\n"));
-               return posix_get_nt_acl(handle->conn, name, security_info, ppdesc);
+               return posix_get_nt_acl(handle->conn, name, security_info,
+                                       mem_ctx, ppdesc);
        }
 
        /* GPFS ACL was not read, something wrong happened, error code is set in errno */
@@ -561,6 +647,13 @@ static SMB_ACL_T gpfs2smb_acl(const struct gpfs_acl *pacl, TALLOC_CTX *mem_ctx)
        }
 
        result->count = pacl->acl_nace;
+       result->acl = talloc_realloc(result, result->acl, struct smb_acl_entry,
+                                    result->count);
+       if (result->acl == NULL) {
+               TALLOC_FREE(result);
+               errno = ENOMEM;
+               return NULL;
+       }
 
        for (i=0; i<pacl->acl_nace; i++) {
                struct smb_acl_entry *ace = &result->acl[i];
@@ -594,8 +687,8 @@ static SMB_ACL_T gpfs2smb_acl(const struct gpfs_acl *pacl, TALLOC_CTX *mem_ctx)
                default:
                        DEBUG(10, ("Got invalid ace_type: %d\n",
                                   g_ace->ace_type));
-                       errno = EINVAL;
                        TALLOC_FREE(result);
+                       errno = EINVAL;
                        return NULL;
                }
 
@@ -649,6 +742,9 @@ static SMB_ACL_T gpfsacl_get_posix_acl(const char *path, gpfs_aclType_t type,
 
  done:
 
+       if (pacl != NULL) {
+               talloc_free(pacl);
+       }
        if (errno != 0) {
                TALLOC_FREE(result);
        }
@@ -705,6 +801,145 @@ static SMB_ACL_T gpfsacl_sys_acl_get_fd(vfs_handle_struct *handle,
                                     GPFS_ACL_TYPE_ACCESS, mem_ctx);
 }
 
+static int gpfsacl_sys_acl_blob_get_file(vfs_handle_struct *handle,
+                                     const char *path_p,
+                                     TALLOC_CTX *mem_ctx,
+                                     char **blob_description,
+                                     DATA_BLOB *blob)
+{
+       struct gpfs_config_data *config;
+       struct gpfs_opaque_acl *acl = NULL;
+       DATA_BLOB aclblob;
+       int result;
+
+       SMB_VFS_HANDLE_GET_DATA(handle, config,
+                               struct gpfs_config_data,
+                               return -1);
+
+       if (!config->acl) {
+               return SMB_VFS_NEXT_SYS_ACL_BLOB_GET_FILE(handle, path_p,
+                                                         mem_ctx,
+                                                         blob_description,
+                                                         blob);
+       }
+
+       errno = 0;
+       acl = (struct gpfs_opaque_acl *)
+                       vfs_gpfs_getacl(mem_ctx,
+                                       path_p,
+                                       true,
+                                       GPFS_ACL_TYPE_NFS4);
+
+       if (errno) {
+               DEBUG(5, ("vfs_gpfs_getacl finished with errno %d: %s\n",
+                                       errno, strerror(errno)));
+
+               /* EINVAL means POSIX ACL, bail out on other cases */
+               if (errno != EINVAL) {
+                       return -1;
+               }
+       }
+
+       if (acl != NULL) {
+               /*
+                * file has NFSv4 ACL
+                *
+                * we only need the actual ACL blob here
+                * acl_version will always be NFS4 because we asked
+                * for NFS4
+                * acl_type is only used for POSIX ACLs
+                */
+               aclblob.data = (uint8_t*) acl->acl_var_data;
+               aclblob.length = acl->acl_buffer_len;
+
+               *blob_description = talloc_strdup(mem_ctx, "gpfs_nfs4_acl");
+               if (!*blob_description) {
+                       talloc_free(acl);
+                       errno = ENOMEM;
+                       return -1;
+               }
+
+               result = non_posix_sys_acl_blob_get_file_helper(handle, path_p,
+                                                               aclblob,
+                                                               mem_ctx, blob);
+
+               talloc_free(acl);
+               return result;
+       }
+
+       /* fall back to POSIX ACL */
+       return posix_sys_acl_blob_get_file(handle, path_p, mem_ctx,
+                                          blob_description, blob);
+}
+
+static int gpfsacl_sys_acl_blob_get_fd(vfs_handle_struct *handle,
+                                     files_struct *fsp,
+                                     TALLOC_CTX *mem_ctx,
+                                     char **blob_description,
+                                     DATA_BLOB *blob)
+{
+       struct gpfs_config_data *config;
+       struct gpfs_opaque_acl *acl = NULL;
+       DATA_BLOB aclblob;
+       int result;
+
+       SMB_VFS_HANDLE_GET_DATA(handle, config,
+                               struct gpfs_config_data,
+                               return -1);
+
+       if (!config->acl) {
+               return SMB_VFS_NEXT_SYS_ACL_BLOB_GET_FD(handle, fsp, mem_ctx,
+                                                       blob_description, blob);
+       }
+
+       errno = 0;
+       acl = (struct gpfs_opaque_acl *) vfs_gpfs_getacl(mem_ctx,
+                                               fsp->fsp_name->base_name,
+                                               true,
+                                               GPFS_ACL_TYPE_NFS4);
+
+       if (errno) {
+               DEBUG(5, ("vfs_gpfs_getacl finished with errno %d: %s\n",
+                                       errno, strerror(errno)));
+
+               /* EINVAL means POSIX ACL, bail out on other cases */
+               if (errno != EINVAL) {
+                       return -1;
+               }
+       }
+
+       if (acl != NULL) {
+               /*
+                * file has NFSv4 ACL
+                *
+                * we only need the actual ACL blob here
+                * acl_version will always be NFS4 because we asked
+                * for NFS4
+                * acl_type is only used for POSIX ACLs
+                */
+               aclblob.data = (uint8_t*) acl->acl_var_data;
+               aclblob.length = acl->acl_buffer_len;
+
+               *blob_description = talloc_strdup(mem_ctx, "gpfs_nfs4_acl");
+               if (!*blob_description) {
+                       talloc_free(acl);
+                       errno = ENOMEM;
+                       return -1;
+               }
+
+               result = non_posix_sys_acl_blob_get_fd_helper(handle, fsp,
+                                                             aclblob, mem_ctx,
+                                                             blob);
+
+               talloc_free(acl);
+               return result;
+       }
+
+       /* fall back to POSIX ACL */
+       return posix_sys_acl_blob_get_fd(handle, fsp, mem_ctx,
+                                        blob_description, blob);
+}
+
 static struct gpfs_acl *smb2gpfs_acl(const SMB_ACL_T pacl,
                                     SMB_ACL_TYPE_T type)
 {
@@ -1912,6 +2147,8 @@ static struct vfs_fn_pointers vfs_gpfs_fns = {
        .fset_nt_acl_fn = gpfsacl_fset_nt_acl,
        .sys_acl_get_file_fn = gpfsacl_sys_acl_get_file,
        .sys_acl_get_fd_fn = gpfsacl_sys_acl_get_fd,
+       .sys_acl_blob_get_file_fn = gpfsacl_sys_acl_blob_get_file,
+       .sys_acl_blob_get_fd_fn = gpfsacl_sys_acl_blob_get_fd,
        .sys_acl_set_file_fn = gpfsacl_sys_acl_set_file,
        .sys_acl_set_fd_fn = gpfsacl_sys_acl_set_fd,
        .sys_acl_delete_def_file_fn = gpfsacl_sys_acl_delete_def_file,