s3:modules use vfs_gpfs_getacl in gpfs_get_nfs4_acl
[samba.git] / source3 / modules / vfs_gpfs.c
index 8e8c69455eccc04402da1382b505d27f00332cc5..8417a6c153dae0cbaaa6287e9c90e02b390bb4ce 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
@@ -219,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;
@@ -242,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;
                }
@@ -250,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;
        }
 
@@ -275,8 +351,9 @@ static int gpfs_get_nfs4_acl(const char *fname, SMB4ACL_T **ppacl)
        struct gpfs_acl *gacl = NULL;
        DEBUG(10, ("gpfs_get_nfs4_acl invoked for %s\n", fname));
 
-       /* First get the real acl length */
-       gacl = gpfs_getacl_alloc(fname, 0);
+       /* Get the ACL */
+       gacl = (struct gpfs_acl*) vfs_gpfs_getacl(talloc_tos(), fname,
+                                                 false, 0);
        if (gacl == NULL) {
                DEBUG(9, ("gpfs_getacl failed for %s with %s\n",
                           fname, strerror(errno)));
@@ -286,6 +363,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;
        }
 
@@ -346,6 +424,8 @@ static int gpfs_get_nfs4_acl(const char *fname, SMB4ACL_T **ppacl)
                smb_add_ace4(*ppacl, &smbace);
        }
 
+       talloc_free(gacl);
+
        return 0;
 }
 
@@ -570,6 +650,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];
@@ -603,8 +690,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;
                }
 
@@ -658,6 +745,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);
        }
@@ -721,7 +811,8 @@ static int gpfsacl_sys_acl_blob_get_file(vfs_handle_struct *handle,
                                      DATA_BLOB *blob)
 {
        struct gpfs_config_data *config;
-       SMB4ACL_T *pacl = NULL;
+       struct gpfs_opaque_acl *acl = NULL;
+       DATA_BLOB aclblob;
        int result;
 
        SMB_VFS_HANDLE_GET_DATA(handle, config,
@@ -735,15 +826,51 @@ static int gpfsacl_sys_acl_blob_get_file(vfs_handle_struct *handle,
                                                          blob);
        }
 
-       result = gpfs_get_nfs4_acl(path_p, &pacl);
-       if (result == 0) {
-               /* We don't have a way to linearlise the NFS4 ACL
-                * right now, and it is much closer to the NT ACL
-                * anyway */
-               errno = EINVAL;
-               return -1;
+       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);
 }
@@ -755,7 +882,8 @@ static int gpfsacl_sys_acl_blob_get_fd(vfs_handle_struct *handle,
                                      DATA_BLOB *blob)
 {
        struct gpfs_config_data *config;
-       SMB4ACL_T *pacl = NULL;
+       struct gpfs_opaque_acl *acl = NULL;
+       DATA_BLOB aclblob;
        int result;
 
        SMB_VFS_HANDLE_GET_DATA(handle, config,
@@ -767,15 +895,50 @@ static int gpfsacl_sys_acl_blob_get_fd(vfs_handle_struct *handle,
                                                        blob_description, blob);
        }
 
-       result = gpfs_get_nfs4_acl(fsp->fsp_name->base_name, &pacl);
-       if (result == 0) {
-               /* We don't have a way to linearlise the NFS4 ACL
-                * right now, and it is much closer to the NT ACL
-                * anyway */
-               errno = EINVAL;
-               return -1;
+       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);
 }