gpfs: Move definition of GPFS_GETACL_NATIVE to vfs_gpfs.c
[samba.git] / source3 / modules / vfs_gpfs.c
index 4b0f9ebaf724c564f7eb2beb4c7262b363fb0d8a..12d163e7b1304fdafea0d58533215c688bf822fc 100644 (file)
 #include "smbd/smbd.h"
 #include "librpc/gen_ndr/ndr_xattr.h"
 #include "include/smbprofile.h"
+#include "modules/non_posix_acls.h"
+#include "libcli/security/security.h"
 
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_VFS
 
-#include <gpfs_gpl.h>
+#include <gpfs_fcntl.h>
 #include "nfs4_acls.h"
 #include "vfs_gpfs.h"
 #include "system/filesys.h"
 #include "auth.h"
+#include "lib/util/tevent_unix.h"
+
+#ifndef GPFS_GETACL_NATIVE
+#define GPFS_GETACL_NATIVE 0x00000004
+#endif
 
 struct gpfs_config_data {
        bool sharemodes;
@@ -45,26 +52,95 @@ struct gpfs_config_data {
        bool getrealfilename;
        bool dfreequota;
        bool prealloc;
+       bool acl;
+       bool settimes;
+       bool recalls;
 };
 
+static inline unsigned int gpfs_acl_flags(gpfs_acl_t *gacl)
+{
+       if (gacl->acl_level == 1) { /* GPFS_ACL_LEVEL_V4FLAGS */
+               /* gacl->v4Level1.acl_flags requires gpfs 3.5 */
+               return *(unsigned int *)&gacl->ace_v4;
+       }
+       return 0;
+}
+
+static inline gpfs_ace_v4_t *gpfs_ace_ptr(gpfs_acl_t *gacl, unsigned int i)
+{
+       if (gacl->acl_level == 1) { /* GPFS_ACL_LEVEL_V4FLAGS */
+               /* &gacl->v4Level1.ace_v4[i] requires gpfs 3.5 */
+               char *ptr = (char *)&gacl->ace_v4[i] + sizeof(unsigned int);
+               return (gpfs_ace_v4_t *)ptr;
+       }
+       return &gacl->ace_v4[i];
+}
+
+static bool set_gpfs_sharemode(files_struct *fsp, uint32 access_mask,
+                              uint32 share_access)
+{
+       unsigned int allow = GPFS_SHARE_NONE;
+       unsigned int deny = GPFS_DENY_NONE;
+       int result;
+
+       if ((fsp == NULL) || (fsp->fh == NULL) || (fsp->fh->fd < 0)) {
+               /* No real file, don't disturb */
+               return True;
+       }
 
-static int vfs_gpfs_kernel_flock(vfs_handle_struct *handle, files_struct *fsp, 
+       allow |= (access_mask & (FILE_WRITE_DATA|FILE_APPEND_DATA|
+                                DELETE_ACCESS)) ? GPFS_SHARE_WRITE : 0;
+       allow |= (access_mask & (FILE_READ_DATA|FILE_EXECUTE)) ?
+               GPFS_SHARE_READ : 0;
+
+       if (allow == GPFS_SHARE_NONE) {
+               DEBUG(10, ("special case am=no_access:%x\n",access_mask));
+       }
+       else {
+               deny |= (share_access & FILE_SHARE_WRITE) ?
+                       0 : GPFS_DENY_WRITE;
+               deny |= (share_access & (FILE_SHARE_READ)) ?
+                       0 : GPFS_DENY_READ;
+       }
+       DEBUG(10, ("am=%x, allow=%d, sa=%x, deny=%d\n",
+                  access_mask, allow, share_access, deny));
+
+       result = gpfswrap_set_share(fsp->fh->fd, allow, deny);
+       if (result != 0) {
+               if (errno == ENOSYS) {
+                       DEBUG(5, ("VFS module vfs_gpfs loaded, but gpfs "
+                                 "set_share function support not available. "
+                                 "Allowing access\n"));
+                       return True;
+               } else {
+                       DEBUG(10, ("gpfs_set_share failed: %s\n",
+                                  strerror(errno)));
+               }
+       }
+
+       return (result == 0);
+}
+
+static int vfs_gpfs_kernel_flock(vfs_handle_struct *handle, files_struct *fsp,
                                 uint32 share_mode, uint32 access_mask)
 {
 
        struct gpfs_config_data *config;
        int ret = 0;
 
+       START_PROFILE(syscall_kernel_flock);
+
        SMB_VFS_HANDLE_GET_DATA(handle, config,
                                struct gpfs_config_data,
                                return -1);
 
-       START_PROFILE(syscall_kernel_flock);
+       if(!config->sharemodes) {
+               return 0;
+       }
 
        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;
        }
 
@@ -89,12 +165,34 @@ static int vfs_gpfs_close(vfs_handle_struct *handle, files_struct *fsp)
        return SMB_VFS_NEXT_CLOSE(handle, fsp);
 }
 
+static int set_gpfs_lease(int fd, int leasetype)
+{
+       int gpfs_type = GPFS_LEASE_NONE;
+
+       if (leasetype == F_RDLCK) {
+               gpfs_type = GPFS_LEASE_READ;
+       }
+       if (leasetype == F_WRLCK) {
+               gpfs_type = GPFS_LEASE_WRITE;
+       }
+
+       /* we unconditionally set CAP_LEASE, rather than looking for
+          -1/EACCES as there is a bug in some versions of
+          libgpfs_gpl.so which results in a leaked fd on /dev/ss0
+          each time we try this with the wrong capabilities set
+       */
+       linux_set_lease_capability();
+       return gpfswrap_set_lease(fd, gpfs_type);
+}
+
 static int vfs_gpfs_setlease(vfs_handle_struct *handle, files_struct *fsp, 
                             int leasetype)
 {
        struct gpfs_config_data *config;
        int ret=0;
 
+       START_PROFILE(syscall_linux_setlease);
+
        SMB_VFS_HANDLE_GET_DATA(handle, config,
                                struct gpfs_config_data,
                                return -1);
@@ -102,10 +200,14 @@ static int vfs_gpfs_setlease(vfs_handle_struct *handle, files_struct *fsp,
        if (linux_set_lease_sighandler(fsp->fh->fd) == -1)
                return -1;
 
-       START_PROFILE(syscall_linux_setlease);
-
        if (config->leases) {
+               /*
+                * Ensure the lease owner is root to allow
+                * correct delivery of lease-break signals.
+                */
+               become_root();
                ret = set_gpfs_lease(fsp->fh->fd,leasetype);
+               unbecome_root();
        }
 
        END_PROFILE(syscall_linux_setlease);
@@ -149,8 +251,8 @@ static int vfs_gpfs_get_real_filename(struct vfs_handle_struct *handle,
 
        buflen = sizeof(real_pathname) - 1;
 
-       result = smbd_gpfs_get_realfilename_path(full_path, real_pathname,
-                                                &buflen);
+       result = gpfswrap_get_realfilename_path(full_path, real_pathname,
+                                               &buflen);
 
        TALLOC_FREE(full_path);
 
@@ -194,6 +296,34 @@ static int vfs_gpfs_get_real_filename(struct vfs_handle_struct *handle,
        return 0;
 }
 
+static void sd2gpfs_control(uint16_t control, struct gpfs_acl *gacl)
+{
+       unsigned int gpfs_aclflags = 0;
+       control &= SEC_DESC_DACL_PROTECTED | SEC_DESC_SACL_PROTECTED |
+               SEC_DESC_DACL_AUTO_INHERITED | SEC_DESC_SACL_AUTO_INHERITED |
+               SEC_DESC_DACL_DEFAULTED | SEC_DESC_SACL_DEFAULTED |
+               SEC_DESC_DACL_PRESENT | SEC_DESC_SACL_PRESENT;
+       gpfs_aclflags = control << 8;
+       if (!(control & SEC_DESC_DACL_PRESENT))
+               gpfs_aclflags |= 0x00800000; /* ACL4_FLAG_NULL_DACL; */
+       if (!(control & SEC_DESC_SACL_PRESENT))
+               gpfs_aclflags |= 0x01000000; /* ACL4_FLAG_NULL_SACL; */
+       gacl->acl_level = 1; /* GPFS_ACL_LEVEL_V4FLAGS*/
+       /* gacl->v4Level1.acl_flags requires gpfs 3.5 */
+       *(unsigned int *)&gacl->ace_v4 = gpfs_aclflags;
+}
+
+static uint16_t gpfs2sd_control(unsigned int gpfs_aclflags)
+{
+       uint16_t control = gpfs_aclflags >> 8;
+       control &= SEC_DESC_DACL_PROTECTED | SEC_DESC_SACL_PROTECTED |
+               SEC_DESC_DACL_AUTO_INHERITED | SEC_DESC_SACL_AUTO_INHERITED |
+               SEC_DESC_DACL_DEFAULTED | SEC_DESC_SACL_DEFAULTED |
+               SEC_DESC_DACL_PRESENT | SEC_DESC_SACL_PRESENT;
+       control |= SEC_DESC_SELF_RELATIVE;
+       return control;
+}
+
 static void gpfs_dumpacl(int level, struct gpfs_acl *gacl)
 {
        gpfs_aclCount_t i;
@@ -203,59 +333,94 @@ static void gpfs_dumpacl(int level, struct gpfs_acl *gacl)
                return;
        }
 
-       DEBUG(level, ("gpfs acl: nace: %d, type:%d, version:%d, level:%d, len:%d\n",
-               gacl->acl_nace, gacl->acl_type, gacl->acl_version, gacl->acl_level, gacl->acl_len));
+       DEBUG(level, ("len: %d, level: %d, version: %d, nace: %d, "
+                     "control: %x\n",
+                     gacl->acl_len, gacl->acl_level, gacl->acl_version,
+                     gacl->acl_nace, gpfs_acl_flags(gacl)));
+
        for(i=0; i<gacl->acl_nace; i++)
        {
-               struct gpfs_ace_v4 *gace = gacl->ace_v4 + i;
-               DEBUG(level, ("\tace[%d]: type:%d, flags:0x%x, mask:0x%x, iflags:0x%x, who:%u\n",
-                       i, gace->aceType, gace->aceFlags, gace->aceMask,
-                       gace->aceIFlags, gace->aceWho));
+               struct gpfs_ace_v4 *gace = gpfs_ace_ptr(gacl, i);
+               DEBUG(level, ("\tace[%d]: type:%d, flags:0x%x, mask:0x%x, "
+                             "iflags:0x%x, who:%u\n",
+                             i, gace->aceType, gace->aceFlags, gace->aceMask,
+                             gace->aceIFlags, gace->aceWho));
        }
 }
 
-static struct gpfs_acl *gpfs_getacl_alloc(const char *fname, gpfs_aclType_t type)
+/*
+ * 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)
 {
-       struct gpfs_acl *acl;
-       size_t len = 200;
-       int ret;
-       TALLOC_CTX *mem_ctx = talloc_tos();
 
-       acl = (struct gpfs_acl *)TALLOC_SIZE(mem_ctx, len);
-       if (acl == NULL) {
+       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;
        }
 
-       acl->acl_len = len;
-       acl->acl_level = 0;
-       acl->acl_version = 0;
-       acl->acl_type = type;
-
-       ret = smbd_gpfs_getacl((char *)fname, GPFS_GETACL_STRUCT, acl);
-       if ((ret != 0) && (errno == ENOSPC)) {
-               struct gpfs_acl *new_acl = (struct gpfs_acl *)TALLOC_SIZE(
-                       mem_ctx, acl->acl_len + sizeof(struct gpfs_acl));
-               if (new_acl == 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;
+               buf->acl_level = 1; /* GPFS_ACL_LEVEL_V4FLAGS */
+               flags = GPFS_GETACL_STRUCT;
+               len = &(buf->acl_len);
+               /* reserve space for control flags in gpfs 3.5 and beyond */
+               struct_size = sizeof(struct gpfs_acl) + sizeof(unsigned int);
+       }
 
-               new_acl->acl_len = acl->acl_len;
-               new_acl->acl_level = acl->acl_level;
-               new_acl->acl_version = acl->acl_version;
-               new_acl->acl_type = acl->acl_type;
-               acl = new_acl;
+       /* set the length of the buffer as input value */
+       *len = size;
 
-               ret = smbd_gpfs_getacl((char *)fname, GPFS_GETACL_STRUCT, acl);
+       errno = 0;
+       ret = gpfswrap_getacl(discard_const_p(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(8, ("smbd_gpfs_getacl failed with %s\n",strerror(errno)));
+
+       if (ret != 0) {
+               DEBUG(5, ("smbd_gpfs_getacl failed with %s\n",
+                         strerror(errno)));
+               talloc_free(aclbuf);
                return NULL;
        }
 
-       return acl;
+       return aclbuf;
 }
 
 /* Tries to get nfs4 acls and returns SMB ACL allocated.
@@ -264,14 +429,15 @@ static struct gpfs_acl *gpfs_getacl_alloc(const char *fname, gpfs_aclType_t type
  * On failure returns -1 if there is system (GPFS) error, check errno.
  * Returns 0 on success
  */
-static int gpfs_get_nfs4_acl(const char *fname, SMB4ACL_T **ppacl)
+static int gpfs_get_nfs4_acl(TALLOC_CTX *mem_ctx, const char *fname, SMB4ACL_T **ppacl)
 {
        gpfs_aclCount_t i;
        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)));
@@ -281,17 +447,23 @@ 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;
        }
 
-       *ppacl = smb_create_smb4acl();
+       *ppacl = smb_create_smb4acl(mem_ctx);
 
-       DEBUG(10, ("len: %d, level: %d, version: %d, nace: %d\n",
+       if (gacl->acl_level == 1) { /* GPFS_ACL_LEVEL_V4FLAGS */
+               uint16_t control = gpfs2sd_control(gpfs_acl_flags(gacl));
+               smbacl4_set_controlflags(*ppacl, control);
+       }
+
+       DEBUG(10, ("len: %d, level: %d, version: %d, nace: %d, control: %x\n",
                   gacl->acl_len, gacl->acl_level, gacl->acl_version,
-                  gacl->acl_nace));
+                  gacl->acl_nace, gpfs_acl_flags(gacl)));
 
        for (i=0; i<gacl->acl_nace; i++) {
-               struct gpfs_ace_v4 *gace = &gacl->ace_v4[i];
+               struct gpfs_ace_v4 *gace = gpfs_ace_ptr(gacl, i);
                SMB_ACE4PROP_T smbace;
                DEBUG(10, ("type: %d, iflags: %x, flags: %x, mask: %x, "
                           "who: %d\n", gace->aceType, gace->aceIFlags,
@@ -322,17 +494,17 @@ static int gpfs_get_nfs4_acl(const char *fname, SMB4ACL_T **ppacl)
                                smbace.who.uid = gace->aceWho;
                }
 
-               /* remove redundent deny entries */
+               /* remove redundant deny entries */
                if (i > 0 && gace->aceType == SMB_ACE4_ACCESS_DENIED_ACE_TYPE) {
-                       struct gpfs_ace_v4 *prev = &gacl->ace_v4[i-1];
+                       struct gpfs_ace_v4 *prev = gpfs_ace_ptr(gacl, 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) {
-                               /* its redundent - skip it */
+                               /* it's redundant - skip it */
                                continue;
-                       }                                                
+                       }
                }
 
                smbace.aceType = gace->aceType;
@@ -341,79 +513,135 @@ static int gpfs_get_nfs4_acl(const char *fname, SMB4ACL_T **ppacl)
                smb_add_ace4(*ppacl, &smbace);
        }
 
+       talloc_free(gacl);
+
        return 0;
 }
 
 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;
        int     result;
+       struct gpfs_config_data *config;
+       TALLOC_CTX *frame = talloc_stackframe();
+       NTSTATUS status;
 
        *ppdesc = NULL;
-       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);
+       SMB_VFS_HANDLE_GET_DATA(handle, config,
+                               struct gpfs_config_data,
+                               return NT_STATUS_INTERNAL_ERROR);
+
+       if (!config->acl) {
+               status = SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
+                                                 mem_ctx, ppdesc);
+               TALLOC_FREE(frame);
+               return status;
+       }
+
+       result = gpfs_get_nfs4_acl(frame, fsp->fsp_name->base_name, &pacl);
+
+       if (result == 0) {
+               status = smb_fget_nt_acl_nfs4(fsp, security_info, mem_ctx,
+                                             ppdesc, pacl);
+               TALLOC_FREE(frame);
+               return status;
+       }
 
        if (result > 0) {
                DEBUG(10, ("retrying with posix acl...\n"));
-               return posix_fget_nt_acl(fsp, security_info, ppdesc);
+               status = posix_fget_nt_acl(fsp, security_info,
+                                          mem_ctx, ppdesc);
+               TALLOC_FREE(frame);
+               return status;
        }
 
+       TALLOC_FREE(frame);
+
        /* GPFS ACL was not read, something wrong happened, error code is set in errno */
        return map_nt_error_from_unix(errno);
 }
 
 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;
+       struct gpfs_config_data *config;
+       TALLOC_CTX *frame = talloc_stackframe();
+       NTSTATUS status;
 
        *ppdesc = NULL;
-       result = gpfs_get_nfs4_acl(name, &pacl);
 
-       if (result == 0)
-               return smb_get_nt_acl_nfs4(handle->conn, name, security_info, ppdesc, pacl);
+       SMB_VFS_HANDLE_GET_DATA(handle, config,
+                               struct gpfs_config_data,
+                               return NT_STATUS_INTERNAL_ERROR);
+
+       if (!config->acl) {
+               status = SMB_VFS_NEXT_GET_NT_ACL(handle, name, security_info,
+                                                mem_ctx, ppdesc);
+               TALLOC_FREE(frame);
+               return status;
+       }
+
+       result = gpfs_get_nfs4_acl(frame, name, &pacl);
+
+       if (result == 0) {
+               status = smb_get_nt_acl_nfs4(handle->conn, name, security_info,
+                                          mem_ctx, ppdesc, pacl);
+               TALLOC_FREE(frame);
+               return status;
+       }
 
        if (result > 0) {
                DEBUG(10, ("retrying with posix acl...\n"));
-               return posix_get_nt_acl(handle->conn, name, security_info, ppdesc);
+               status =  posix_get_nt_acl(handle->conn, name, security_info,
+                                          mem_ctx, ppdesc);
+               TALLOC_FREE(frame);
+               return status;
        }
 
        /* GPFS ACL was not read, something wrong happened, error code is set in errno */
+       TALLOC_FREE(frame);
        return map_nt_error_from_unix(errno);
 }
 
-static bool gpfsacl_process_smbacl(files_struct *fsp, SMB4ACL_T *smbacl)
+static struct gpfs_acl *vfs_gpfs_smbacl2gpfsacl(TALLOC_CTX *mem_ctx,
+                                               files_struct *fsp,
+                                               SMB4ACL_T *smbacl,
+                                               bool controlflags)
 {
-       int ret;
-       gpfs_aclLen_t gacl_len;
-       SMB4ACE_T       *smbace;
        struct gpfs_acl *gacl;
-       TALLOC_CTX *mem_ctx  = talloc_tos();
+       gpfs_aclLen_t gacl_len;
+       SMB4ACE_T *smbace;
 
-       gacl_len = sizeof(struct gpfs_acl) +
-               (smb_get_naces(smbacl)-1)*sizeof(gpfs_ace_v4_t);
+       gacl_len = offsetof(gpfs_acl_t, ace_v4) + sizeof(unsigned int)
+               + smb_get_naces(smbacl) * sizeof(gpfs_ace_v4_t);
 
        gacl = (struct gpfs_acl *)TALLOC_SIZE(mem_ctx, gacl_len);
        if (gacl == NULL) {
                DEBUG(0, ("talloc failed\n"));
                errno = ENOMEM;
-               return False;
+               return NULL;
        }
 
-       gacl->acl_len = gacl_len;
-       gacl->acl_level = 0;
+       gacl->acl_level = 0; /* GPFS_ACL_LEVEL_BASE */
        gacl->acl_version = GPFS_ACL_VERSION_NFS4;
        gacl->acl_type = GPFS_ACL_TYPE_NFS4;
        gacl->acl_nace = 0; /* change later... */
 
+       if (controlflags) {
+               gacl->acl_level = 1; /* GPFS_ACL_LEVEL_V4FLAGS */
+               sd2gpfs_control(smbacl4_get_controlflags(smbacl), gacl);
+       }
+
        for (smbace=smb_first_ace4(smbacl); smbace!=NULL; smbace = smb_next_ace4(smbace)) {
-               struct gpfs_ace_v4 *gace = &gacl->ace_v4[gacl->acl_nace];
+               struct gpfs_ace_v4 *gace = gpfs_ace_ptr(gacl, gacl->acl_nace);
                SMB_ACE4PROP_T  *aceprop = smb_get_ace4(smbace);
 
                gace->aceType = aceprop->aceType;
@@ -471,9 +699,38 @@ static bool gpfsacl_process_smbacl(files_struct *fsp, SMB4ACL_T *smbacl)
 
                gacl->acl_nace++;
        }
+       gacl->acl_len = (char *)gpfs_ace_ptr(gacl, gacl->acl_nace)
+               - (char *)gacl;
+       return gacl;
+}
+
+static bool gpfsacl_process_smbacl(vfs_handle_struct *handle,
+                                  files_struct *fsp,
+                                  SMB4ACL_T *smbacl)
+{
+       int ret;
+       struct gpfs_acl *gacl;
+       TALLOC_CTX *mem_ctx = talloc_tos();
+
+       gacl = vfs_gpfs_smbacl2gpfsacl(mem_ctx, fsp, smbacl, true);
+       if (gacl == NULL) { /* out of memory */
+               return False;
+       }
+       ret = gpfswrap_putacl(fsp->fsp_name->base_name,
+                             GPFS_PUTACL_STRUCT | GPFS_ACL_SAMBA, gacl);
+
+       if ((ret != 0) && (errno == EINVAL)) {
+               DEBUG(10, ("Retry without nfs41 control flags\n"));
+               talloc_free(gacl);
+               gacl = vfs_gpfs_smbacl2gpfsacl(mem_ctx, fsp, smbacl, false);
+               if (gacl == NULL) { /* out of memory */
+                       return False;
+               }
+               ret = gpfswrap_putacl(fsp->fsp_name->base_name,
+                                     GPFS_PUTACL_STRUCT | GPFS_ACL_SAMBA,
+                                     gacl);
+       }
 
-       ret = smbd_gpfs_putacl(fsp->fsp_name->base_name,
-                              GPFS_PUTACL_STRUCT | GPFS_ACL_SAMBA, gacl);
        if (ret != 0) {
                DEBUG(8, ("gpfs_putacl failed with %s\n", strerror(errno)));
                gpfs_dumpacl(8, gacl);
@@ -484,51 +741,72 @@ static bool gpfsacl_process_smbacl(files_struct *fsp, SMB4ACL_T *smbacl)
        return True;
 }
 
-static NTSTATUS gpfsacl_set_nt_acl_internal(files_struct *fsp, uint32 security_info_sent, const struct security_descriptor *psd)
+static NTSTATUS gpfsacl_set_nt_acl_internal(vfs_handle_struct *handle, files_struct *fsp, uint32 security_info_sent, const struct security_descriptor *psd)
 {
        struct gpfs_acl *acl;
        NTSTATUS result = NT_STATUS_ACCESS_DENIED;
 
-       acl = gpfs_getacl_alloc(fsp->fsp_name->base_name, 0);
-       if (acl == NULL)
-               return result;
+       acl = (struct gpfs_acl*) vfs_gpfs_getacl(talloc_tos(),
+                                                fsp->fsp_name->base_name,
+                                                false, 0);
+       if (acl == NULL) {
+               return map_nt_error_from_unix(errno);
+       }
 
-       if (acl->acl_version&GPFS_ACL_VERSION_NFS4)
-       {
+       if (acl->acl_version == GPFS_ACL_VERSION_NFS4) {
                if (lp_parm_bool(fsp->conn->params->service, "gpfs",
                                 "refuse_dacl_protected", false)
                    && (psd->type&SEC_DESC_DACL_PROTECTED)) {
                        DEBUG(2, ("Rejecting unsupported ACL with DACL_PROTECTED bit set\n"));
+                       talloc_free(acl);
                        return NT_STATUS_NOT_SUPPORTED;
                }
 
-               result = smb_set_nt_acl_nfs4(
+               result = smb_set_nt_acl_nfs4(handle,
                        fsp, security_info_sent, psd,
                        gpfsacl_process_smbacl);
        } else { /* assume POSIX ACL - by default... */
                result = set_nt_acl(fsp, security_info_sent, psd);
        }
 
+       talloc_free(acl);
        return result;
 }
 
 static NTSTATUS gpfsacl_fset_nt_acl(vfs_handle_struct *handle, files_struct *fsp, uint32 security_info_sent, const struct security_descriptor *psd)
 {
-       return gpfsacl_set_nt_acl_internal(fsp, security_info_sent, psd);
+       struct gpfs_config_data *config;
+
+       SMB_VFS_HANDLE_GET_DATA(handle, config,
+                               struct gpfs_config_data,
+                               return NT_STATUS_INTERNAL_ERROR);
+
+       if (!config->acl) {
+               return SMB_VFS_NEXT_FSET_NT_ACL(handle, fsp, security_info_sent, psd);
+       }
+
+       return gpfsacl_set_nt_acl_internal(handle, fsp, security_info_sent, psd);
 }
 
-static SMB_ACL_T gpfs2smb_acl(const struct gpfs_acl *pacl)
+static SMB_ACL_T gpfs2smb_acl(const struct gpfs_acl *pacl, TALLOC_CTX *mem_ctx)
 {
        SMB_ACL_T result;
        gpfs_aclCount_t i;
 
-       result = sys_acl_init(pacl->acl_nace);
+       result = sys_acl_init(mem_ctx);
        if (result == NULL) {
                errno = ENOMEM;
                return NULL;
        }
 
        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];
@@ -541,14 +819,14 @@ static SMB_ACL_T gpfs2smb_acl(const struct gpfs_acl *pacl)
                switch (g_ace->ace_type) {
                case GPFS_ACL_USER:
                        ace->a_type = SMB_ACL_USER;
-                       ace->uid = (uid_t)g_ace->ace_who;
+                       ace->info.user.uid = (uid_t)g_ace->ace_who;
                        break;
                case GPFS_ACL_USER_OBJ:
                        ace->a_type = SMB_ACL_USER_OBJ;
                        break;
                case GPFS_ACL_GROUP:
                        ace->a_type = SMB_ACL_GROUP;
-                       ace->gid = (gid_t)g_ace->ace_who;
+                       ace->info.group.gid = (gid_t)g_ace->ace_who;
                        break;
                case GPFS_ACL_GROUP_OBJ:
                        ace->a_type = SMB_ACL_GROUP_OBJ;
@@ -562,8 +840,8 @@ static SMB_ACL_T gpfs2smb_acl(const struct gpfs_acl *pacl)
                default:
                        DEBUG(10, ("Got invalid ace_type: %d\n",
                                   g_ace->ace_type));
+                       TALLOC_FREE(result);
                        errno = EINVAL;
-                       SAFE_FREE(result);
                        return NULL;
                }
 
@@ -582,15 +860,16 @@ static SMB_ACL_T gpfs2smb_acl(const struct gpfs_acl *pacl)
        return result;
 }
 
-static SMB_ACL_T gpfsacl_get_posix_acl(const char *path, gpfs_aclType_t type)
+static SMB_ACL_T gpfsacl_get_posix_acl(const char *path, gpfs_aclType_t type,
+                                      TALLOC_CTX *mem_ctx)
 {
        struct gpfs_acl *pacl;
        SMB_ACL_T result = NULL;
 
-       pacl = gpfs_getacl_alloc(path, type);
+       pacl = vfs_gpfs_getacl(talloc_tos(), path, false, type);
 
        if (pacl == NULL) {
-               DEBUG(10, ("gpfs_getacl failed for %s with %s\n",
+               DEBUG(10, ("vfs_gpfs_getacl failed for %s with %s\n",
                           path, strerror(errno)));
                if (errno == 0) {
                        errno = EINVAL;
@@ -609,24 +888,38 @@ static SMB_ACL_T gpfsacl_get_posix_acl(const char *path, gpfs_aclType_t type)
                   pacl->acl_len, pacl->acl_level, pacl->acl_version,
                   pacl->acl_nace));
 
-       result = gpfs2smb_acl(pacl);
-       if (result == NULL) {
-               goto done;
+       result = gpfs2smb_acl(pacl, mem_ctx);
+       if (result != NULL) {
+               errno = 0;
        }
 
  done:
 
+       if (pacl != NULL) {
+               talloc_free(pacl);
+       }
        if (errno != 0) {
-               SAFE_FREE(result);
+               TALLOC_FREE(result);
        }
-       return result;  
+       return result;
 }
 
 static SMB_ACL_T gpfsacl_sys_acl_get_file(vfs_handle_struct *handle,
                                          const char *path_p,
-                                         SMB_ACL_TYPE_T type)
+                                         SMB_ACL_TYPE_T type,
+                                         TALLOC_CTX *mem_ctx)
 {
        gpfs_aclType_t gpfs_type;
+       struct gpfs_config_data *config;
+
+       SMB_VFS_HANDLE_GET_DATA(handle, config,
+                               struct gpfs_config_data,
+                               return NULL);
+
+       if (!config->acl) {
+               return SMB_VFS_NEXT_SYS_ACL_GET_FILE(handle, path_p,
+                                                    type, mem_ctx);
+       }
 
        switch(type) {
        case SMB_ACL_TYPE_ACCESS:
@@ -640,14 +933,164 @@ static SMB_ACL_T gpfsacl_sys_acl_get_file(vfs_handle_struct *handle,
                smb_panic("exiting");
        }
 
-       return gpfsacl_get_posix_acl(path_p, gpfs_type);
+       return gpfsacl_get_posix_acl(path_p, gpfs_type, mem_ctx);
 }
 
 static SMB_ACL_T gpfsacl_sys_acl_get_fd(vfs_handle_struct *handle,
-                                       files_struct *fsp)
+                                       files_struct *fsp,
+                                       TALLOC_CTX *mem_ctx)
 {
+       struct gpfs_config_data *config;
+
+       SMB_VFS_HANDLE_GET_DATA(handle, config,
+                               struct gpfs_config_data,
+                               return NULL);
+
+       if (!config->acl) {
+               return SMB_VFS_NEXT_SYS_ACL_GET_FD(handle, fsp, mem_ctx);
+       }
+
        return gpfsacl_get_posix_acl(fsp->fsp_name->base_name,
-                                    GPFS_ACL_TYPE_ACCESS);
+                                    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,
@@ -656,16 +1099,11 @@ static struct gpfs_acl *smb2gpfs_acl(const SMB_ACL_T pacl,
        gpfs_aclLen_t len;
        struct gpfs_acl *result;
        int i;
-       union gpfs_ace_union
-       {
-               gpfs_ace_v1_t  ace_v1[1]; /* when GPFS_ACL_VERSION_POSIX */
-               gpfs_ace_v4_t  ace_v4[1]; /* when GPFS_ACL_VERSION_NFS4  */
-       };
 
        DEBUG(10, ("smb2gpfs_acl: Got ACL with %d entries\n", pacl->count));
 
-       len = sizeof(struct gpfs_acl) - sizeof(union gpfs_ace_union) +
-               (pacl->count)*sizeof(gpfs_ace_v1_t);
+       len = offsetof(gpfs_acl_t, ace_v1) + (pacl->count) *
+               sizeof(gpfs_ace_v1_t);
 
        result = (struct gpfs_acl *)SMB_MALLOC(len);
        if (result == NULL) {
@@ -692,7 +1130,7 @@ static struct gpfs_acl *smb2gpfs_acl(const SMB_ACL_T pacl,
                switch(ace->a_type) {
                case SMB_ACL_USER:
                        g_ace->ace_type = GPFS_ACL_USER;
-                       g_ace->ace_who = (gpfs_uid_t)ace->uid;
+                       g_ace->ace_who = (gpfs_uid_t)ace->info.user.uid;
                        break;
                case SMB_ACL_USER_OBJ:
                        g_ace->ace_type = GPFS_ACL_USER_OBJ;
@@ -701,7 +1139,7 @@ static struct gpfs_acl *smb2gpfs_acl(const SMB_ACL_T pacl,
                        break;
                case SMB_ACL_GROUP:
                        g_ace->ace_type = GPFS_ACL_GROUP;
-                       g_ace->ace_who = (gpfs_uid_t)ace->gid;
+                       g_ace->ace_who = (gpfs_uid_t)ace->info.group.gid;
                        break;
                case SMB_ACL_GROUP_OBJ:
                        g_ace->ace_type = GPFS_ACL_GROUP_OBJ;
@@ -744,13 +1182,23 @@ static int gpfsacl_sys_acl_set_file(vfs_handle_struct *handle,
 {
        struct gpfs_acl *gpfs_acl;
        int result;
+       struct gpfs_config_data *config;
+
+       SMB_VFS_HANDLE_GET_DATA(handle, config,
+                               struct gpfs_config_data,
+                               return -1);
+
+       if (!config->acl) {
+               return SMB_VFS_NEXT_SYS_ACL_SET_FILE(handle, name, type, theacl);
+       }
 
        gpfs_acl = smb2gpfs_acl(theacl, type);
        if (gpfs_acl == NULL) {
                return -1;
        }
 
-       result = smbd_gpfs_putacl((char *)name, GPFS_PUTACL_STRUCT | GPFS_ACL_SAMBA, gpfs_acl);
+       result = gpfswrap_putacl(discard_const_p(char, name),
+                                GPFS_PUTACL_STRUCT|GPFS_ACL_SAMBA, gpfs_acl);
 
        SAFE_FREE(gpfs_acl);
        return result;
@@ -760,6 +1208,16 @@ static int gpfsacl_sys_acl_set_fd(vfs_handle_struct *handle,
                                  files_struct *fsp,
                                  SMB_ACL_T theacl)
 {
+       struct gpfs_config_data *config;
+
+       SMB_VFS_HANDLE_GET_DATA(handle, config,
+                               struct gpfs_config_data,
+                               return -1);
+
+       if (!config->acl) {
+               return SMB_VFS_NEXT_SYS_ACL_SET_FD(handle, fsp, theacl);
+       }
+
        return gpfsacl_sys_acl_set_file(handle, fsp->fsp_name->base_name,
                                        SMB_ACL_TYPE_ACCESS, theacl);
 }
@@ -767,6 +1225,16 @@ static int gpfsacl_sys_acl_set_fd(vfs_handle_struct *handle,
 static int gpfsacl_sys_acl_delete_def_file(vfs_handle_struct *handle,
                                           const char *path)
 {
+       struct gpfs_config_data *config;
+
+       SMB_VFS_HANDLE_GET_DATA(handle, config,
+                               struct gpfs_config_data,
+                               return -1);
+
+       if (!config->acl) {
+               return SMB_VFS_NEXT_SYS_ACL_DELETE_DEF_FILE(handle, path);
+       }
+
        errno = ENOTSUP;
        return -1;
 }
@@ -810,7 +1278,8 @@ static uint32 gpfsacl_mask_filter(uint32 aceType, uint32 aceMask, uint32 rwx)
        return aceMask;
 }
 
-static int gpfsacl_emu_chmod(const char *path, mode_t mode)
+static int gpfsacl_emu_chmod(vfs_handle_struct *handle,
+                            const char *path, mode_t mode)
 {
        SMB4ACL_T *pacl = NULL;
        int     result;
@@ -818,13 +1287,15 @@ static int gpfsacl_emu_chmod(const char *path, mode_t mode)
        int     i;
        files_struct    fake_fsp; /* TODO: rationalize parametrization */
        SMB4ACE_T       *smbace;
-       NTSTATUS status;
+       TALLOC_CTX *frame = talloc_stackframe();
 
        DEBUG(10, ("gpfsacl_emu_chmod invoked for %s mode %o\n", path, mode));
 
-       result = gpfs_get_nfs4_acl(path, &pacl);
-       if (result)
+       result = gpfs_get_nfs4_acl(frame, path, &pacl);
+       if (result) {
+               TALLOC_FREE(frame);
                return result;
+       }
 
        if (mode & ~(S_IRWXU | S_IRWXG | S_IRWXO)) {
                DEBUG(2, ("WARNING: cutting extra mode bits %o on %s\n", mode, path));
@@ -888,19 +1359,20 @@ static int gpfsacl_emu_chmod(const char *path, mode_t mode)
 
        /* don't add complementary DENY ACEs here */
        ZERO_STRUCT(fake_fsp);
-       status = create_synthetic_smb_fname(talloc_tos(), path, NULL, NULL,
-                                           &fake_fsp.fsp_name);
-       if (!NT_STATUS_IS_OK(status)) {
-               errno = map_errno_from_nt_status(status);
+       fake_fsp.fsp_name = synthetic_smb_fname(
+               frame, path, NULL, NULL);
+       if (fake_fsp.fsp_name == NULL) {
+               errno = ENOMEM;
+               TALLOC_FREE(frame);
                return -1;
        }
        /* put the acl */
-       if (gpfsacl_process_smbacl(&fake_fsp, pacl) == False) {
-               TALLOC_FREE(fake_fsp.fsp_name);
+       if (gpfsacl_process_smbacl(handle, &fake_fsp, pacl) == False) {
+               TALLOC_FREE(frame);
                return -1;
        }
 
-       TALLOC_FREE(fake_fsp.fsp_name);
+       TALLOC_FREE(frame);
        return 0; /* ok for [f]chmod */
 }
 
@@ -908,10 +1380,12 @@ static int vfs_gpfs_chmod(vfs_handle_struct *handle, const char *path, mode_t mo
 {
        struct smb_filename *smb_fname_cpath;
        int rc;
-       NTSTATUS status;
 
-       status = create_synthetic_smb_fname(
-               talloc_tos(), path, NULL, NULL, &smb_fname_cpath);
+       smb_fname_cpath = synthetic_smb_fname(talloc_tos(), path, NULL, NULL);
+       if (smb_fname_cpath == NULL) {
+               errno = ENOMEM;
+               return -1;
+       }
 
        if (SMB_VFS_NEXT_STAT(handle, smb_fname_cpath) != 0) {
                return -1;
@@ -922,7 +1396,7 @@ static int vfs_gpfs_chmod(vfs_handle_struct *handle, const char *path, mode_t mo
                return 0;
        }
 
-       rc = gpfsacl_emu_chmod(path, mode);
+       rc = gpfsacl_emu_chmod(handle, path, mode);
        if (rc == 1)
                return SMB_VFS_NEXT_CHMOD(handle, path, mode);
        return rc;
@@ -942,7 +1416,8 @@ static int vfs_gpfs_fchmod(vfs_handle_struct *handle, files_struct *fsp, mode_t
                         return 0;
                 }
 
-                rc = gpfsacl_emu_chmod(fsp->fsp_name->base_name, mode);
+                rc = gpfsacl_emu_chmod(handle, fsp->fsp_name->base_name,
+                                       mode);
                 if (rc == 1)
                         return SMB_VFS_NEXT_FCHMOD(handle, fsp, mode);
                 return rc;
@@ -953,7 +1428,6 @@ static int gpfs_set_xattr(struct vfs_handle_struct *handle,  const char *path,
        struct xattr_DOSATTRIB dosattrib;
         enum ndr_err_code ndr_err;
         DATA_BLOB blob;
-        const char *attrstr = value;
         unsigned int dosmode=0;
         struct gpfs_winattr attrs;
         int ret = 0;
@@ -976,7 +1450,7 @@ static int gpfs_set_xattr(struct vfs_handle_struct *handle,  const char *path,
                return SMB_VFS_NEXT_SETXATTR(handle,path,name,value,size,flags);
         }
 
-       blob.data = (uint8_t *)attrstr;
+       blob.data = discard_const_p(uint8_t, value);
        blob.length = size;
 
        ndr_err = ndr_pull_struct_blob(&blob, talloc_tos(), &dosattrib,
@@ -1021,8 +1495,8 @@ static int gpfs_set_xattr(struct vfs_handle_struct *handle,  const char *path,
        }
 
 
-        ret = set_gpfs_winattrs(discard_const_p(char, path),
-                               GPFS_WINATTR_SET_ATTRS, &attrs);
+       ret = gpfswrap_set_winattrs_path(discard_const_p(char, path),
+                                        GPFS_WINATTR_SET_ATTRS, &attrs);
         if ( ret == -1){
                if (errno == ENOSYS) {
                        return SMB_VFS_NEXT_SETXATTR(handle, path, name, value,
@@ -1062,15 +1536,22 @@ static ssize_t gpfs_get_xattr(struct vfs_handle_struct *handle,  const char *pat
                 return SMB_VFS_NEXT_GETXATTR(handle,path,name,value,size);
         }
 
-        ret = get_gpfs_winattrs(discard_const_p(char, path), &attrs);
+       ret = gpfswrap_get_winattrs_path(discard_const_p(char, path), &attrs);
         if ( ret == -1){
+               int dbg_lvl;
+
                if (errno == ENOSYS) {
                        return SMB_VFS_NEXT_GETXATTR(handle, path, name, value,
                                                     size);
                }
 
-                DEBUG(1, ("gpfs_get_xattr: Get GPFS attributes failed: "
-                         "%d (%s)\n", ret, strerror(errno)));
+               if (errno != EPERM && errno != EACCES) {
+                       dbg_lvl = 1;
+               } else {
+                       dbg_lvl = 5;
+               }
+               DEBUG(dbg_lvl, ("gpfs_get_xattr: Get GPFS attributes failed: "
+                             "%d (%s)\n", ret, strerror(errno)));
                 return -1;
         }
 
@@ -1099,6 +1580,46 @@ static ssize_t gpfs_get_xattr(struct vfs_handle_struct *handle,  const char *pat
         return 4;
 }
 
+#if defined(HAVE_FSTATAT)
+static int stat_with_capability(struct vfs_handle_struct *handle,
+                               struct smb_filename *smb_fname, int flag)
+{
+       int fd = -1;
+       bool b;
+       char *dir_name;
+       const char *rel_name = NULL;
+       struct stat st;
+       int ret = -1;
+
+       b = parent_dirname(talloc_tos(), smb_fname->base_name,
+                          &dir_name, &rel_name);
+       if (!b) {
+               errno = ENOMEM;
+               return -1;
+       }
+
+       fd = open(dir_name, O_RDONLY, 0);
+       TALLOC_FREE(dir_name);
+       if (fd == -1) {
+               return -1;
+       }
+
+       set_effective_capability(DAC_OVERRIDE_CAPABILITY);
+       ret = fstatat(fd, rel_name, &st, flag);
+       drop_effective_capability(DAC_OVERRIDE_CAPABILITY);
+
+       close(fd);
+
+       if (ret == 0) {
+               init_stat_ex_from_stat(
+                       &smb_fname->st, &st,
+                       lp_fake_directory_create_times(SNUM(handle->conn)));
+       }
+
+       return ret;
+}
+#endif
+
 static int vfs_gpfs_stat(struct vfs_handle_struct *handle,
                         struct smb_filename *smb_fname)
 {
@@ -1113,6 +1634,13 @@ static int vfs_gpfs_stat(struct vfs_handle_struct *handle,
                                return -1);
 
        ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
+#if defined(HAVE_FSTATAT)
+       if (ret == -1 && errno == EACCES) {
+               DEBUG(10, ("Trying stat with capability for %s\n",
+                          smb_fname->base_name));
+               ret = stat_with_capability(handle, smb_fname, 0);
+       }
+#endif
        if (ret == -1) {
                return -1;
        }
@@ -1126,13 +1654,12 @@ static int vfs_gpfs_stat(struct vfs_handle_struct *handle,
                errno = map_errno_from_nt_status(status);
                return -1;
        }
-       ret = get_gpfs_winattrs(discard_const_p(char, fname), &attrs);
+       ret = gpfswrap_get_winattrs_path(discard_const_p(char, fname), &attrs);
        TALLOC_FREE(fname);
        if (ret == 0) {
                smb_fname->st.st_ex_calculated_birthtime = false;
                smb_fname->st.st_ex_btime.tv_sec = attrs.creationTime.tv_sec;
                smb_fname->st.st_ex_btime.tv_nsec = attrs.creationTime.tv_nsec;
-               smb_fname->st.vfs_private = attrs.winAttrs;
        }
        return 0;
 }
@@ -1159,7 +1686,7 @@ static int vfs_gpfs_fstat(struct vfs_handle_struct *handle,
                return 0;
        }
 
-       ret = smbd_fget_gpfs_winattrs(fsp->fh->fd, &attrs);
+       ret = gpfswrap_get_winattrs(fsp->fh->fd, &attrs);
        if (ret == 0) {
                sbuf->st_ex_calculated_birthtime = false;
                sbuf->st_ex_btime.tv_sec = attrs.creationTime.tv_sec;
@@ -1182,6 +1709,15 @@ static int vfs_gpfs_lstat(struct vfs_handle_struct *handle,
                                return -1);
 
        ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
+#if defined(HAVE_FSTATAT)
+       if (ret == -1 && errno == EACCES) {
+               DEBUG(10, ("Trying lstat with capability for %s\n",
+                          smb_fname->base_name));
+               ret = stat_with_capability(handle, smb_fname,
+                                          AT_SYMLINK_NOFOLLOW);
+       }
+#endif
+
        if (ret == -1) {
                return -1;
        }
@@ -1194,17 +1730,54 @@ static int vfs_gpfs_lstat(struct vfs_handle_struct *handle,
                errno = map_errno_from_nt_status(status);
                return -1;
        }
-       ret = get_gpfs_winattrs(discard_const_p(char, path), &attrs);
+       ret = gpfswrap_get_winattrs_path(discard_const_p(char, path), &attrs);
        TALLOC_FREE(path);
        if (ret == 0) {
                smb_fname->st.st_ex_calculated_birthtime = false;
                smb_fname->st.st_ex_btime.tv_sec = attrs.creationTime.tv_sec;
                smb_fname->st.st_ex_btime.tv_nsec = attrs.creationTime.tv_nsec;
-               smb_fname->st.vfs_private = attrs.winAttrs;
        }
        return 0;
 }
 
+static void timespec_to_gpfs_time(struct timespec ts, gpfs_timestruc_t *gt,
+                                 int idx, int *flags)
+{
+       if (!null_timespec(ts)) {
+               *flags |= 1 << idx;
+               gt[idx].tv_sec = ts.tv_sec;
+               gt[idx].tv_nsec = ts.tv_nsec;
+               DEBUG(10, ("Setting GPFS time %d, flags 0x%x\n", idx, *flags));
+       }
+}
+
+static int smbd_gpfs_set_times_path(char *path, struct smb_file_time *ft)
+{
+       gpfs_timestruc_t gpfs_times[4];
+       int flags = 0;
+       int rc;
+
+       ZERO_ARRAY(gpfs_times);
+       timespec_to_gpfs_time(ft->atime, gpfs_times, 0, &flags);
+       timespec_to_gpfs_time(ft->mtime, gpfs_times, 1, &flags);
+       /* No good mapping from LastChangeTime to ctime, not storing */
+       timespec_to_gpfs_time(ft->create_time, gpfs_times, 3, &flags);
+
+       if (!flags) {
+               DEBUG(10, ("nothing to do, return to avoid EINVAL\n"));
+               return 0;
+       }
+
+       rc = gpfswrap_set_times_path(path, flags, gpfs_times);
+
+       if (rc != 0 && errno != ENOSYS) {
+               DEBUG(1,("gpfs_set_times() returned with error %s\n",
+                       strerror(errno)));
+       }
+
+       return rc;
+}
+
 static int vfs_gpfs_ntimes(struct vfs_handle_struct *handle,
                         const struct smb_filename *smb_fname,
                        struct smb_file_time *ft)
@@ -1220,6 +1793,24 @@ static int vfs_gpfs_ntimes(struct vfs_handle_struct *handle,
                                struct gpfs_config_data,
                                return -1);
 
+       status = get_full_smb_filename(talloc_tos(), smb_fname, &path);
+       if (!NT_STATUS_IS_OK(status)) {
+               errno = map_errno_from_nt_status(status);
+               return -1;
+       }
+
+       /* Try to use gpfs_set_times if it is enabled and available */
+       if (config->settimes) {
+               ret = smbd_gpfs_set_times_path(path, ft);
+
+               if (ret == 0 || (ret == -1 && errno != ENOSYS)) {
+                       return ret;
+               }
+       }
+
+       DEBUG(10,("gpfs_set_times() not available or disabled, "
+                 "use ntimes and winattr\n"));
+
         ret = SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
         if(ret == -1){
                /* don't complain if access was denied */
@@ -1239,18 +1830,13 @@ static int vfs_gpfs_ntimes(struct vfs_handle_struct *handle,
                return 0;
        }
 
-        status = get_full_smb_filename(talloc_tos(), smb_fname, &path);
-        if (!NT_STATUS_IS_OK(status)) {
-                errno = map_errno_from_nt_status(status);
-                return -1;
-        }
-
         attrs.winAttrs = 0;
         attrs.creationTime.tv_sec = ft->create_time.tv_sec;
         attrs.creationTime.tv_nsec = ft->create_time.tv_nsec;
 
-        ret = set_gpfs_winattrs(discard_const_p(char, path),
-                                GPFS_WINATTR_SET_CREATION_TIME, &attrs);
+       ret = gpfswrap_set_winattrs_path(discard_const_p(char, path),
+                                        GPFS_WINATTR_SET_CREATION_TIME,
+                                        &attrs);
         if(ret == -1 && errno != ENOSYS){
                 DEBUG(1,("vfs_gpfs_ntimes: set GPFS ntimes failed %d\n",ret));
                return -1;
@@ -1282,7 +1868,7 @@ static int vfs_gpfs_fallocate(struct vfs_handle_struct *handle,
                return -1;
        }
 
-       ret = smbd_gpfs_prealloc(fsp->fh->fd, offset, len);
+       ret = gpfswrap_prealloc(fsp->fh->fd, offset, len);
 
        if (ret == -1 && errno != ENOSYS) {
                DEBUG(0, ("GPFS prealloc failed: %s\n", strerror(errno)));
@@ -1309,7 +1895,7 @@ static int vfs_gpfs_ftruncate(vfs_handle_struct *handle, files_struct *fsp,
                return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, len);
        }
 
-       result = smbd_gpfs_ftruncate(fsp->fh->fd, len);
+       result = gpfswrap_ftruncate(fsp->fh->fd, len);
        if ((result == -1) && (errno == ENOSYS)) {
                return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, len);
        }
@@ -1324,6 +1910,7 @@ static bool vfs_gpfs_is_offline(struct vfs_handle_struct *handle,
        char *path = NULL;
        NTSTATUS status;
        struct gpfs_config_data *config;
+       int ret;
 
        SMB_VFS_HANDLE_GET_DATA(handle, config,
                                struct gpfs_config_data,
@@ -1339,17 +1926,12 @@ static bool vfs_gpfs_is_offline(struct vfs_handle_struct *handle,
                return -1;
        }
 
-       if (VALID_STAT(*sbuf)) {
-               attrs.winAttrs = sbuf->vfs_private;
-       } else {
-               int ret;
-               ret = get_gpfs_winattrs(path, &attrs);
-
-               if (ret == -1) {
-                       TALLOC_FREE(path);
-                       return false;
-               }
+       ret = gpfswrap_get_winattrs_path(path, &attrs);
+       if (ret == -1) {
+               TALLOC_FREE(path);
+               return false;
        }
+
        if ((attrs.winAttrs & GPFS_WINATTR_OFFLINE) != 0) {
                DEBUG(10, ("%s is offline\n", path));
                TALLOC_FREE(path);
@@ -1370,7 +1952,8 @@ static ssize_t vfs_gpfs_sendfile(vfs_handle_struct *handle, int tofd,
                                 files_struct *fsp, const DATA_BLOB *hdr,
                                 off_t offset, size_t n)
 {
-       if ((fsp->fsp_name->st.vfs_private & GPFS_WINATTR_OFFLINE) != 0) {
+       if (SMB_VFS_IS_OFFLINE(handle->conn, fsp->fsp_name, &fsp->fsp_name->st))
+       {
                errno = ENOSYS;
                return -1;
        }
@@ -1383,21 +1966,21 @@ static int vfs_gpfs_connect(struct vfs_handle_struct *handle,
        struct gpfs_config_data *config;
        int ret;
 
-       smbd_gpfs_lib_init();
-
-       ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
-
-       if (ret < 0) {
-               return ret;
-       }
+       gpfswrap_lib_init(0);
 
        config = talloc_zero(handle->conn, struct gpfs_config_data);
        if (!config) {
-               SMB_VFS_NEXT_DISCONNECT(handle);
                DEBUG(0, ("talloc_zero() failed\n"));
+               errno = ENOMEM;
                return -1;
        }
 
+       ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
+       if (ret < 0) {
+               TALLOC_FREE(config);
+               return ret;
+       }
+
        config->sharemodes = lp_parm_bool(SNUM(handle->conn), "gpfs",
                                        "sharemodes", true);
 
@@ -1425,13 +2008,116 @@ static int vfs_gpfs_connect(struct vfs_handle_struct *handle,
        config->prealloc = lp_parm_bool(SNUM(handle->conn), "gpfs",
                                   "prealloc", true);
 
+       config->acl = lp_parm_bool(SNUM(handle->conn), "gpfs", "acl", true);
+
+       config->settimes = lp_parm_bool(SNUM(handle->conn), "gpfs",
+                                       "settimes", true);
+       config->recalls = lp_parm_bool(SNUM(handle->conn), "gpfs",
+                                      "recalls", true);
+
        SMB_VFS_HANDLE_SET_DATA(handle, config,
                                NULL, struct gpfs_config_data,
                                return -1);
 
+       if (config->leases) {
+               /*
+                * GPFS lease code is based on kernel oplock code
+                * so make sure it is turned on
+                */
+               if (!lp_kernel_oplocks(SNUM(handle->conn))) {
+                       DEBUG(5, ("Enabling kernel oplocks for "
+                                 "gpfs:leases to work\n"));
+                       lp_do_parameter(SNUM(handle->conn), "kernel oplocks",
+                                       "true");
+               }
+
+               /*
+                * as the kernel does not properly support Level II oplocks
+                * and GPFS leases code is based on kernel infrastructure, we
+                * need to turn off Level II oplocks if gpfs:leases is enabled
+                */
+               if (lp_level2_oplocks(SNUM(handle->conn))) {
+                       DEBUG(5, ("gpfs:leases are enabled, disabling "
+                                 "Level II oplocks\n"));
+                       lp_do_parameter(SNUM(handle->conn), "level2 oplocks",
+                                       "false");
+               }
+       }
+
        return 0;
 }
 
+static int get_gpfs_fset_id(const char *pathname, int *fset_id)
+{
+       int err, fd, errno_fcntl;
+
+       struct {
+               gpfsFcntlHeader_t hdr;
+               gpfsGetFilesetName_t fsn;
+       } arg;
+
+       arg.hdr.totalLength = sizeof(arg);
+       arg.hdr.fcntlVersion = GPFS_FCNTL_CURRENT_VERSION;
+       arg.hdr.fcntlReserved = 0;
+       arg.fsn.structLen = sizeof(arg.fsn);
+       arg.fsn.structType = GPFS_FCNTL_GET_FILESETNAME;
+
+       fd = open(pathname, O_RDONLY);
+       if (fd == -1) {
+               DEBUG(1, ("Could not open %s: %s\n",
+                         pathname, strerror(errno)));
+               return fd;
+       }
+
+       err = gpfswrap_fcntl(fd, &arg);
+       errno_fcntl = errno;
+       close(fd);
+
+       if (err) {
+               errno = errno_fcntl;
+               if (errno != ENOSYS) {
+                       DEBUG(1, ("GPFS_FCNTL_GET_FILESETNAME for %s failed: "
+                                 "%s\n", pathname, strerror(errno)));
+               }
+               return err;
+       }
+
+       err = gpfswrap_getfilesetid(discard_const_p(char, pathname),
+                                   arg.fsn.buffer, fset_id);
+       if (err && errno != ENOSYS) {
+               DEBUG(1, ("gpfs_getfilesetid for %s failed: %s\n",
+                         pathname, strerror(errno)));
+       }
+       return err;
+}
+
+static int get_gpfs_quota(const char *pathname, int type, int id,
+                         struct gpfs_quotaInfo *qi)
+{
+       int ret;
+
+       ZERO_STRUCTP(qi);
+       ret = gpfswrap_quotactl(discard_const_p(char, pathname),
+                               GPFS_QCMD(Q_GETQUOTA, type), id, qi);
+
+       if (ret) {
+               if (errno == GPFS_E_NO_QUOTA_INST) {
+                       DEBUG(10, ("Quotas disabled on GPFS filesystem.\n"));
+               } else if (errno != ENOSYS) {
+                       DEBUG(0, ("Get quota failed, type %d, id, %d, "
+                                 "errno %d.\n", type, id, errno));
+               }
+
+               return ret;
+       }
+
+       DEBUG(10, ("quota type %d, id %d, blk u:%lld h:%lld s:%lld gt:%u\n",
+                  type, id, qi->blockUsage, qi->blockHardLimit,
+                  qi->blockSoftLimit, qi->blockGraceTime));
+
+       return ret;
+}
+
 static int vfs_gpfs_get_quotas(const char *path, uid_t uid, gid_t gid,
                               int *fset_id,
                               struct gpfs_quotaInfo *qi_user,
@@ -1439,10 +2125,30 @@ static int vfs_gpfs_get_quotas(const char *path, uid_t uid, gid_t gid,
                               struct gpfs_quotaInfo *qi_fset)
 {
        int err;
+       char *dir_path;
+       bool b;
 
-       err = get_gpfs_fset_id(path, fset_id);
+       /*
+        * We want to always use the directory to get the fileset id,
+        * because files might have a share mode. We also do not want
+        * to get the parent directory when there is already a
+        * directory to avoid stepping in a different fileset.  The
+        * path passed here is currently either "." or a filename, so
+        * this is ok. The proper solution would be having a way to
+        * query the fileset id without opening the file.
+        */
+       b = parent_dirname(talloc_tos(), path, &dir_path, NULL);
+       if (!b) {
+               errno = ENOMEM;
+               return -1;
+       }
+
+       DEBUG(10, ("path %s, directory %s\n", path, dir_path));
+
+       err = get_gpfs_fset_id(dir_path, fset_id);
        if (err) {
-               DEBUG(0, ("Get fset id failed, errno %d.\n", errno));
+               DEBUG(0, ("Get fset id failed path %s, dir %s, errno %d.\n",
+                         path, dir_path, errno));
                return err;
        }
 
@@ -1481,7 +2187,8 @@ static void vfs_gpfs_disk_free_quota(struct gpfs_quotaInfo qi, time_t cur_time,
         * When the grace time for the exceeded soft block quota has been
         * exceeded, the soft block quota becomes an additional hard limit.
         */
-       if (qi.blockGraceTime && cur_time > qi.blockGraceTime) {
+       if (qi.blockSoftLimit &&
+           qi.blockGraceTime && cur_time > qi.blockGraceTime) {
                /* report disk as full */
                *dfree = 0;
                *dsize = MIN(*dsize, usage);
@@ -1503,7 +2210,7 @@ static void vfs_gpfs_disk_free_quota(struct gpfs_quotaInfo qi, time_t cur_time,
 }
 
 static uint64_t vfs_gpfs_disk_free(vfs_handle_struct *handle, const char *path,
-                                  bool small_query, uint64_t *bsize,
+                                  uint64_t *bsize,
                                   uint64_t *dfree, uint64_t *dsize)
 {
        struct security_unix_token *utok;
@@ -1515,14 +2222,14 @@ static uint64_t vfs_gpfs_disk_free(vfs_handle_struct *handle, const char *path,
        SMB_VFS_HANDLE_GET_DATA(handle, config, struct gpfs_config_data,
                                return (uint64_t)-1);
        if (!config->dfreequota) {
-               return SMB_VFS_NEXT_DISK_FREE(handle, path, small_query,
+               return SMB_VFS_NEXT_DISK_FREE(handle, path,
                                              bsize, dfree, dsize);
        }
 
        err = sys_fsusage(path, dfree, dsize);
        if (err) {
                DEBUG (0, ("Could not get fs usage, errno %d\n", errno));
-               return SMB_VFS_NEXT_DISK_FREE(handle, path, small_query,
+               return SMB_VFS_NEXT_DISK_FREE(handle, path,
                                              bsize, dfree, dsize);
        }
 
@@ -1536,7 +2243,7 @@ static uint64_t vfs_gpfs_disk_free(vfs_handle_struct *handle, const char *path,
        err = vfs_gpfs_get_quotas(path, utok->uid, utok->gid, &fset_id,
                                  &qi_user, &qi_group, &qi_fset);
        if (err) {
-               return SMB_VFS_NEXT_DISK_FREE(handle, path, small_query,
+               return SMB_VFS_NEXT_DISK_FREE(handle, path,
                                              bsize, dfree, dsize);
        }
 
@@ -1551,7 +2258,7 @@ static uint64_t vfs_gpfs_disk_free(vfs_handle_struct *handle, const char *path,
                vfs_gpfs_disk_free_quota(qi_fset, cur_time, dfree, dsize);
        }
 
-       disk_norm(small_query, bsize, dfree, dsize);
+       disk_norm(bsize, dfree, dsize);
        return *dfree;
 }
 
@@ -1583,12 +2290,199 @@ static int vfs_gpfs_open(struct vfs_handle_struct *handle,
                                struct gpfs_config_data,
                                return -1);
 
+       if (config->hsm && !config->recalls) {
+               if (SMB_VFS_IS_OFFLINE(handle->conn, smb_fname, &smb_fname->st))
+               {
+                       DEBUG(10, ("Refusing access to offline file %s\n",
+                                 fsp_str_dbg(fsp)));
+                       errno = EACCES;
+                       return -1;
+               }
+       }
+
        if (config->syncio) {
                flags |= O_SYNC;
        }
        return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
 }
 
+static ssize_t vfs_gpfs_pread(vfs_handle_struct *handle, files_struct *fsp,
+                             void *data, size_t n, off_t offset)
+{
+       ssize_t ret;
+       bool was_offline;
+
+       was_offline = SMB_VFS_IS_OFFLINE(handle->conn, fsp->fsp_name,
+                                        &fsp->fsp_name->st);
+
+       ret = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
+
+       if ((ret != -1) && was_offline) {
+               notify_fname(handle->conn, NOTIFY_ACTION_MODIFIED,
+                            FILE_NOTIFY_CHANGE_ATTRIBUTES,
+                            fsp->fsp_name->base_name);
+       }
+
+       return ret;
+}
+
+struct vfs_gpfs_pread_state {
+       struct files_struct *fsp;
+       ssize_t ret;
+       int err;
+       bool was_offline;
+};
+
+static void vfs_gpfs_pread_done(struct tevent_req *subreq);
+
+static struct tevent_req *vfs_gpfs_pread_send(struct vfs_handle_struct *handle,
+                                             TALLOC_CTX *mem_ctx,
+                                             struct tevent_context *ev,
+                                             struct files_struct *fsp,
+                                             void *data, size_t n,
+                                             off_t offset)
+{
+       struct tevent_req *req, *subreq;
+       struct vfs_gpfs_pread_state *state;
+
+       req = tevent_req_create(mem_ctx, &state, struct vfs_gpfs_pread_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       state->was_offline = SMB_VFS_IS_OFFLINE(handle->conn, fsp->fsp_name,
+                                               &fsp->fsp_name->st);
+       state->fsp = fsp;
+       subreq = SMB_VFS_NEXT_PREAD_SEND(state, ev, handle, fsp, data,
+                                        n, offset);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, vfs_gpfs_pread_done, req);
+       return req;
+}
+
+static void vfs_gpfs_pread_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct vfs_gpfs_pread_state *state = tevent_req_data(
+               req, struct vfs_gpfs_pread_state);
+
+       state->ret = SMB_VFS_PREAD_RECV(subreq, &state->err);
+       TALLOC_FREE(subreq);
+       tevent_req_done(req);
+}
+
+static ssize_t vfs_gpfs_pread_recv(struct tevent_req *req, int *err)
+{
+       struct vfs_gpfs_pread_state *state = tevent_req_data(
+               req, struct vfs_gpfs_pread_state);
+       struct files_struct *fsp = state->fsp;
+
+       if (tevent_req_is_unix_error(req, err)) {
+               return -1;
+       }
+       *err = state->err;
+
+       if ((state->ret != -1) && state->was_offline) {
+               DEBUG(10, ("sending notify\n"));
+               notify_fname(fsp->conn, NOTIFY_ACTION_MODIFIED,
+                            FILE_NOTIFY_CHANGE_ATTRIBUTES,
+                            fsp->fsp_name->base_name);
+       }
+
+       return state->ret;
+}
+
+static ssize_t vfs_gpfs_pwrite(vfs_handle_struct *handle, files_struct *fsp,
+                              const void *data, size_t n, off_t offset)
+{
+       ssize_t ret;
+       bool was_offline;
+
+       was_offline = SMB_VFS_IS_OFFLINE(handle->conn, fsp->fsp_name,
+                                        &fsp->fsp_name->st);
+
+       ret = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
+
+       if ((ret != -1) && was_offline) {
+               notify_fname(handle->conn, NOTIFY_ACTION_MODIFIED,
+                            FILE_NOTIFY_CHANGE_ATTRIBUTES,
+                            fsp->fsp_name->base_name);
+       }
+
+       return ret;
+}
+
+struct vfs_gpfs_pwrite_state {
+       struct files_struct *fsp;
+       ssize_t ret;
+       int err;
+       bool was_offline;
+};
+
+static void vfs_gpfs_pwrite_done(struct tevent_req *subreq);
+
+static struct tevent_req *vfs_gpfs_pwrite_send(
+       struct vfs_handle_struct *handle,
+       TALLOC_CTX *mem_ctx,
+       struct tevent_context *ev,
+       struct files_struct *fsp,
+       const void *data, size_t n,
+       off_t offset)
+{
+       struct tevent_req *req, *subreq;
+       struct vfs_gpfs_pwrite_state *state;
+
+       req = tevent_req_create(mem_ctx, &state, struct vfs_gpfs_pwrite_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       state->was_offline = SMB_VFS_IS_OFFLINE(handle->conn, fsp->fsp_name,
+                                               &fsp->fsp_name->st);
+       state->fsp = fsp;
+       subreq = SMB_VFS_NEXT_PWRITE_SEND(state, ev, handle, fsp, data,
+                                        n, offset);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, vfs_gpfs_pwrite_done, req);
+       return req;
+}
+
+static void vfs_gpfs_pwrite_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct vfs_gpfs_pwrite_state *state = tevent_req_data(
+               req, struct vfs_gpfs_pwrite_state);
+
+       state->ret = SMB_VFS_PWRITE_RECV(subreq, &state->err);
+       TALLOC_FREE(subreq);
+       tevent_req_done(req);
+}
+
+static ssize_t vfs_gpfs_pwrite_recv(struct tevent_req *req, int *err)
+{
+       struct vfs_gpfs_pwrite_state *state = tevent_req_data(
+               req, struct vfs_gpfs_pwrite_state);
+       struct files_struct *fsp = state->fsp;
+
+       if (tevent_req_is_unix_error(req, err)) {
+               return -1;
+       }
+       *err = state->err;
+
+       if ((state->ret != -1) && state->was_offline) {
+               DEBUG(10, ("sending notify\n"));
+               notify_fname(fsp->conn, NOTIFY_ACTION_MODIFIED,
+                            FILE_NOTIFY_CHANGE_ATTRIBUTES,
+                            fsp->fsp_name->base_name);
+       }
+
+       return state->ret;
+}
+
 
 static struct vfs_fn_pointers vfs_gpfs_fns = {
        .connect_fn = vfs_gpfs_connect,
@@ -1602,6 +2496,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,
@@ -1619,13 +2515,24 @@ static struct vfs_fn_pointers vfs_gpfs_fns = {
        .sendfile_fn = vfs_gpfs_sendfile,
        .fallocate_fn = vfs_gpfs_fallocate,
        .open_fn = vfs_gpfs_open,
+       .pread_fn = vfs_gpfs_pread,
+       .pread_send_fn = vfs_gpfs_pread_send,
+       .pread_recv_fn = vfs_gpfs_pread_recv,
+       .pwrite_fn = vfs_gpfs_pwrite,
+       .pwrite_send_fn = vfs_gpfs_pwrite_send,
+       .pwrite_recv_fn = vfs_gpfs_pwrite_recv,
        .ftruncate_fn = vfs_gpfs_ftruncate
 };
 
 NTSTATUS vfs_gpfs_init(void);
 NTSTATUS vfs_gpfs_init(void)
 {
-       init_gpfs();
+       int ret;
+
+       ret = gpfswrap_init();
+       if (ret != 0) {
+               DEBUG(1, ("Could not initialize GPFS library wrapper\n"));
+       }
 
        return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "gpfs",
                                &vfs_gpfs_fns);