fuzzing: fix fuzz_stable_sort_r_unstable comparison
[samba.git] / source3 / modules / vfs_gpfs.c
index 999e83b16a01dbf8ccfd330e684f166afae71343..a8b4e38ff88c00c61285258f92c4f4a67de152a2 100644 (file)
@@ -22,7 +22,6 @@
 
 #include "includes.h"
 #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"
 #include "system/filesys.h"
 #include "auth.h"
 #include "lib/util/tevent_unix.h"
-#include "gpfswrap.h"
+#include "lib/util/gpfswrap.h"
+
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+#include "lib/crypto/gnutls_helpers.h"
 
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_VFS
@@ -40,6 +43,7 @@
 #endif
 
 struct gpfs_config_data {
+       struct smbacl4_vfs_params nfs4_params;
        bool sharemodes;
        bool leases;
        bool hsm;
@@ -48,85 +52,119 @@ struct gpfs_config_data {
        bool ftruncate;
        bool getrealfilename;
        bool dfreequota;
-       bool prealloc;
        bool acl;
        bool settimes;
        bool recalls;
+       struct {
+               bool gpfs_fstat_x;
+       } pathref_ok;
+};
+
+struct gpfs_fsp_extension {
+       bool offline;
 };
 
 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;
+       if (gacl->acl_level == GPFS_ACL_LEVEL_V4FLAGS) {
+               return gacl->v4Level1.acl_flags;
        }
        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;
+       if (gacl->acl_level == GPFS_ACL_LEVEL_V4FLAGS) {
+               return &gacl->v4Level1.ace_v4[i];
        }
        return &gacl->ace_v4[i];
 }
 
-static bool set_gpfs_sharemode(files_struct *fsp, uint32 access_mask,
-                              uint32 share_access)
+static unsigned int vfs_gpfs_access_mask_to_allow(uint32_t access_mask)
 {
        unsigned int allow = GPFS_SHARE_NONE;
+
+       if (access_mask & (FILE_WRITE_DATA|FILE_APPEND_DATA)) {
+               allow |= GPFS_SHARE_WRITE;
+       }
+       if (access_mask & (FILE_READ_DATA|FILE_EXECUTE)) {
+               allow |= GPFS_SHARE_READ;
+       }
+
+       return allow;
+}
+
+static unsigned int vfs_gpfs_share_access_to_deny(uint32_t share_access)
+{
        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;
+       if (!(share_access & FILE_SHARE_WRITE)) {
+               deny |= GPFS_DENY_WRITE;
+       }
+       if (!(share_access & FILE_SHARE_READ)) {
+               deny |= GPFS_DENY_READ;
+       }
+
+       /*
+        * GPFS_DENY_DELETE can only be set together with either
+        * GPFS_DENY_WRITE or GPFS_DENY_READ.
+        */
+       if ((deny & (GPFS_DENY_WRITE|GPFS_DENY_READ)) &&
+           !(share_access & FILE_SHARE_DELETE)) {
+               deny |= GPFS_DENY_DELETE;
        }
 
-       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;
+       return deny;
+}
+
+static int set_gpfs_sharemode(files_struct *fsp, uint32_t access_mask,
+                             uint32_t share_access)
+{
+       unsigned int allow = GPFS_SHARE_NONE;
+       unsigned int deny = GPFS_DENY_NONE;
+       int result;
 
-       if (allow == GPFS_SHARE_NONE) {
-               DEBUG(10, ("special case am=no_access:%x\n",access_mask));
+       if (access_mask == 0) {
+               DBG_DEBUG("Clearing file system share mode.\n");
+       } else {
+               allow = vfs_gpfs_access_mask_to_allow(access_mask);
+               deny = vfs_gpfs_share_access_to_deny(share_access);
        }
-       else {
-               deny |= (share_access & FILE_SHARE_WRITE) ?
-                       0 : GPFS_DENY_WRITE;
-               deny |= (share_access & (FILE_SHARE_READ)) ?
-                       0 : GPFS_DENY_READ;
+       DBG_DEBUG("access_mask=0x%x, allow=0x%x, share_access=0x%x, "
+                 "deny=0x%x\n", access_mask, allow, share_access, deny);
+
+       result = gpfswrap_set_share(fsp_get_io_fd(fsp), allow, deny);
+       if (result == 0) {
+               return 0;
        }
-       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)));
-               }
+       if (errno == EACCES) {
+               DBG_NOTICE("GPFS share mode denied for %s/%s.\n",
+                          fsp->conn->connectpath,
+                          fsp->fsp_name->base_name);
+       } else if (errno == EPERM) {
+               DBG_ERR("Samba requested GPFS sharemode for %s/%s, but the "
+                       "GPFS file system is not configured accordingly. "
+                       "Configure file system with mmchfs -D nfs4 or "
+                       "set gpfs:sharemodes=no in Samba.\n",
+                       fsp->conn->connectpath,
+                       fsp->fsp_name->base_name);
+       } else {
+               DBG_ERR("gpfs_set_share failed: %s\n", strerror(errno));
        }
 
-       return (result == 0);
+       return result;
 }
 
-static int vfs_gpfs_kernel_flock(vfs_handle_struct *handle, files_struct *fsp,
-                                uint32 share_mode, uint32 access_mask)
+static int vfs_gpfs_filesystem_sharemode(vfs_handle_struct *handle,
+                                        files_struct *fsp,
+                                        uint32_t share_access,
+                                        uint32_t 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);
@@ -135,13 +173,19 @@ static int vfs_gpfs_kernel_flock(vfs_handle_struct *handle, files_struct *fsp,
                return 0;
        }
 
-       kernel_flock(fsp->fh->fd, share_mode, access_mask);
-
-       if (!set_gpfs_sharemode(fsp, access_mask, fsp->share_access)) {
-               ret = -1;
+       /*
+        * A named stream fsp will have the basefile open in the fsp
+        * fd, so lacking a distinct fd for the stream we have to skip
+        * set_gpfs_sharemode for stream.
+        */
+       if (fsp_is_alternate_stream(fsp)) {
+               DBG_NOTICE("Not requesting GPFS sharemode on stream: %s/%s\n",
+                          fsp->conn->connectpath,
+                          fsp_str_dbg(fsp));
+               return 0;
        }
 
-       END_PROFILE(syscall_kernel_flock);
+       ret = set_gpfs_sharemode(fsp, access_mask, share_access);
 
        return ret;
 }
@@ -155,34 +199,43 @@ static int vfs_gpfs_close(vfs_handle_struct *handle, files_struct *fsp)
                                struct gpfs_config_data,
                                return -1);
 
-       if (config->sharemodes && (fsp->fh != NULL) && (fsp->fh->fd != -1)) {
-               set_gpfs_sharemode(fsp, 0, 0);
+       if (config->sharemodes &&
+           (fsp->fsp_flags.kernel_share_modes_taken))
+       {
+               /*
+                * Always clear GPFS sharemode in case the actual
+                * close gets deferred due to outstanding POSIX locks
+                * (see fd_close_posix)
+                */
+               int ret = gpfswrap_set_share(fsp_get_io_fd(fsp), 0, 0);
+               if (ret != 0) {
+                       DBG_ERR("Clearing GPFS sharemode on close failed for "
+                               " %s/%s: %s\n",
+                               fsp->conn->connectpath,
+                               fsp->fsp_name->base_name,
+                               strerror(errno));
+               }
        }
 
        return SMB_VFS_NEXT_CLOSE(handle, fsp);
 }
 
-static int set_gpfs_lease(int fd, int leasetype)
+#ifdef HAVE_KERNEL_OPLOCKS_LINUX
+static int lease_type_to_gpfs(int leasetype)
 {
-       int gpfs_type = GPFS_LEASE_NONE;
-
        if (leasetype == F_RDLCK) {
-               gpfs_type = GPFS_LEASE_READ;
+               return GPFS_LEASE_READ;
        }
+
        if (leasetype == F_WRLCK) {
-               gpfs_type = GPFS_LEASE_WRITE;
+               return 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);
+       return GPFS_LEASE_NONE;
 }
 
-static int vfs_gpfs_setlease(vfs_handle_struct *handle, files_struct *fsp, 
+static int vfs_gpfs_setlease(vfs_handle_struct *handle,
+                            files_struct *fsp,
                             int leasetype)
 {
        struct gpfs_config_data *config;
@@ -194,56 +247,82 @@ static int vfs_gpfs_setlease(vfs_handle_struct *handle, files_struct *fsp,
                                struct gpfs_config_data,
                                return -1);
 
-       if (linux_set_lease_sighandler(fsp->fh->fd) == -1)
-               return -1;
+       ret = linux_set_lease_sighandler(fsp_get_io_fd(fsp));
+       if (ret == -1) {
+               goto failure;
+       }
 
        if (config->leases) {
+               int gpfs_lease_type = lease_type_to_gpfs(leasetype);
+               int saved_errno = 0;
+
                /*
                 * Ensure the lease owner is root to allow
                 * correct delivery of lease-break signals.
                 */
                become_root();
-               ret = set_gpfs_lease(fsp->fh->fd,leasetype);
+               ret = gpfswrap_set_lease(fsp_get_io_fd(fsp), gpfs_lease_type);
+               if (ret < 0) {
+                       saved_errno = errno;
+               }
                unbecome_root();
+
+               if (saved_errno != 0) {
+                       errno = saved_errno;
+               }
        }
 
+failure:
        END_PROFILE(syscall_linux_setlease);
 
        return ret;
 }
 
-static int vfs_gpfs_get_real_filename(struct vfs_handle_struct *handle,
-                                     const char *path,
-                                     const char *name,
-                                     TALLOC_CTX *mem_ctx,
-                                     char **found_name)
+#else /* HAVE_KERNEL_OPLOCKS_LINUX */
+
+static int vfs_gpfs_setlease(vfs_handle_struct *handle,
+                               files_struct *fsp,
+                               int leasetype)
+{
+       return ENOSYS;
+}
+#endif /* HAVE_KERNEL_OPLOCKS_LINUX */
+
+static NTSTATUS vfs_gpfs_get_real_filename_at(struct vfs_handle_struct *handle,
+                                             struct files_struct *dirfsp,
+                                             const char *name,
+                                             TALLOC_CTX *mem_ctx,
+                                             char **found_name)
 {
        int result;
-       char *full_path;
-       char real_pathname[PATH_MAX+1];
+       char *full_path = NULL;
+       char *to_free = NULL;
+       char real_pathname[PATH_MAX+1], tmpbuf[PATH_MAX];
+       size_t full_path_len;
        int buflen;
        bool mangled;
        struct gpfs_config_data *config;
 
        SMB_VFS_HANDLE_GET_DATA(handle, config,
                                struct gpfs_config_data,
-                               return -1);
+                               return NT_STATUS_INTERNAL_ERROR);
 
        if (!config->getrealfilename) {
-               return SMB_VFS_NEXT_GET_REAL_FILENAME(handle, path, name,
-                                                     mem_ctx, found_name);
+               return SMB_VFS_NEXT_GET_REAL_FILENAME_AT(
+                       handle, dirfsp, name, mem_ctx, found_name);
        }
 
        mangled = mangle_is_mangled(name, handle->conn->params);
        if (mangled) {
-               return SMB_VFS_NEXT_GET_REAL_FILENAME(handle, path, name,
-                                                     mem_ctx, found_name);
+               return SMB_VFS_NEXT_GET_REAL_FILENAME_AT(
+                       handle, dirfsp, name, mem_ctx, found_name);
        }
 
-       full_path = talloc_asprintf(talloc_tos(), "%s/%s", path, name);
-       if (full_path == NULL) {
-               errno = ENOMEM;
-               return -1;
+       full_path_len = full_path_tos(dirfsp->fsp_name->base_name, name,
+                                     tmpbuf, sizeof(tmpbuf),
+                                     &full_path, &to_free);
+       if (full_path_len == -1) {
+               return NT_STATUS_NO_MEMORY;
        }
 
        buflen = sizeof(real_pathname) - 1;
@@ -251,17 +330,17 @@ static int vfs_gpfs_get_real_filename(struct vfs_handle_struct *handle,
        result = gpfswrap_get_realfilename_path(full_path, real_pathname,
                                                &buflen);
 
-       TALLOC_FREE(full_path);
+       TALLOC_FREE(to_free);
 
        if ((result == -1) && (errno == ENOSYS)) {
-               return SMB_VFS_NEXT_GET_REAL_FILENAME(
-                       handle, path, name, mem_ctx, found_name);
+               return SMB_VFS_NEXT_GET_REAL_FILENAME_AT(
+                       handle, dirfsp, name, mem_ctx, found_name);
        }
 
        if (result == -1) {
                DEBUG(10, ("smbd_gpfs_get_realfilename_path returned %s\n",
                           strerror(errno)));
-               return -1;
+               return map_nt_error_from_unix(errno);
        }
 
        /*
@@ -275,22 +354,22 @@ static int vfs_gpfs_get_real_filename(struct vfs_handle_struct *handle,
                real_pathname[sizeof(real_pathname)-1] = '\0';
        }
 
-       DEBUG(10, ("smbd_gpfs_get_realfilename_path: %s/%s -> %s\n",
-                  path, name, real_pathname));
+       DBG_DEBUG("%s/%s -> %s\n",
+                 fsp_str_dbg(dirfsp),
+                 name,
+                 real_pathname);
 
        name = strrchr_m(real_pathname, '/');
        if (name == NULL) {
-               errno = ENOENT;
-               return -1;
+               return NT_STATUS_OBJECT_NAME_NOT_FOUND;
        }
 
        *found_name = talloc_strdup(mem_ctx, name+1);
        if (*found_name == NULL) {
-               errno = ENOMEM;
-               return -1;
+               return NT_STATUS_NO_MEMORY;
        }
 
-       return 0;
+       return NT_STATUS_OK;
 }
 
 static void sd2gpfs_control(uint16_t control, struct gpfs_acl *gacl)
@@ -302,12 +381,11 @@ static void sd2gpfs_control(uint16_t control, struct gpfs_acl *gacl)
                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; */
+               gpfs_aclflags |= 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;
+               gpfs_aclflags |= ACL4_FLAG_NULL_SACL;
+       gacl->acl_level = GPFS_ACL_LEVEL_V4FLAGS;
+       gacl->v4Level1.acl_flags = gpfs_aclflags;
 }
 
 static uint16_t gpfs2sd_control(unsigned int gpfs_aclflags)
@@ -345,6 +423,23 @@ static void gpfs_dumpacl(int level, struct gpfs_acl *gacl)
        }
 }
 
+static int gpfs_getacl_with_capability(struct files_struct *fsp,
+                                      int flags,
+                                      void *buf)
+{
+       int ret, saved_errno;
+
+       set_effective_capability(DAC_OVERRIDE_CAPABILITY);
+
+       ret = gpfswrap_fgetacl(fsp_get_pathref_fd(fsp), flags, buf);
+       saved_errno = errno;
+
+       drop_effective_capability(DAC_OVERRIDE_CAPABILITY);
+
+       errno = saved_errno;
+       return ret;
+}
+
 /*
  * get the ACL from GPFS, allocated on the specified mem_ctx
  * internally retries when initial buffer was too small
@@ -355,16 +450,17 @@ static void gpfs_dumpacl(int level, struct gpfs_acl *gacl)
  *
  */
 static void *vfs_gpfs_getacl(TALLOC_CTX *mem_ctx,
-                        const char *fname,
+                        struct files_struct *fsp,
                         const bool raw,
                         const gpfs_aclType_t type)
 {
-
+       const char *fname = fsp->fsp_name->base_name;
        void *aclbuf;
        size_t size = 512;
        int ret, flags;
        unsigned int *len;
        size_t struct_size;
+       bool use_capability = false;
 
 again:
 
@@ -383,7 +479,7 @@ again:
        } else {
                struct gpfs_acl *buf = (struct gpfs_acl *) aclbuf;
                buf->acl_type = type;
-               buf->acl_level = 1; /* GPFS_ACL_LEVEL_V4FLAGS */
+               buf->acl_level = GPFS_ACL_LEVEL_V4FLAGS;
                flags = GPFS_GETACL_STRUCT;
                len = &(buf->acl_len);
                /* reserve space for control flags in gpfs 3.5 and beyond */
@@ -393,8 +489,17 @@ again:
        /* set the length of the buffer as input value */
        *len = size;
 
-       errno = 0;
-       ret = gpfswrap_getacl(discard_const_p(char, fname), flags, aclbuf);
+       if (use_capability) {
+               ret = gpfs_getacl_with_capability(fsp, flags, aclbuf);
+       } else {
+               ret = gpfswrap_fgetacl(fsp_get_pathref_fd(fsp), flags, aclbuf);
+               if ((ret != 0) && (errno == EACCES)) {
+                       DBG_DEBUG("Retry with DAC capability for %s\n", fname);
+                       use_capability = true;
+                       ret = gpfs_getacl_with_capability(fsp, flags, aclbuf);
+               }
+       }
+
        if ((ret != 0) && (errno == ENOSPC)) {
                /*
                 * get the size needed to accommodate the complete buffer
@@ -426,18 +531,29 @@ again:
  * On failure returns -1 if there is system (GPFS) error, check errno.
  * Returns 0 on success
  */
-static int gpfs_get_nfs4_acl(TALLOC_CTX *mem_ctx, const char *fname, SMB4ACL_T **ppacl)
+static int gpfs_get_nfs4_acl(TALLOC_CTX *mem_ctx,
+                            struct files_struct *fsp,
+                            struct SMB4ACL_T **ppacl)
 {
+       const char *fname = fsp->fsp_name->base_name;
        gpfs_aclCount_t i;
        struct gpfs_acl *gacl = NULL;
        DEBUG(10, ("gpfs_get_nfs4_acl invoked for %s\n", fname));
 
        /* Get the ACL */
-       gacl = (struct gpfs_acl*) vfs_gpfs_getacl(talloc_tos(), fname,
+       gacl = (struct gpfs_acl*) vfs_gpfs_getacl(talloc_tos(), fsp,
                                                  false, 0);
        if (gacl == NULL) {
                DEBUG(9, ("gpfs_getacl failed for %s with %s\n",
                           fname, strerror(errno)));
+               if (errno == ENODATA) {
+                       /*
+                        * GPFS returns ENODATA for snapshot
+                        * directories. Retry with POSIX ACLs check.
+                        */
+                       return 1;
+               }
+
                return -1;
        }
 
@@ -450,7 +566,7 @@ static int gpfs_get_nfs4_acl(TALLOC_CTX *mem_ctx, const char *fname, SMB4ACL_T *
 
        *ppacl = smb_create_smb4acl(mem_ctx);
 
-       if (gacl->acl_level == 1) { /* GPFS_ACL_LEVEL_V4FLAGS */
+       if (gacl->acl_level == GPFS_ACL_LEVEL_V4FLAGS) {
                uint16_t control = gpfs2sd_control(gpfs_acl_flags(gacl));
                smbacl4_set_controlflags(*ppacl, control);
        }
@@ -461,12 +577,11 @@ static int gpfs_get_nfs4_acl(TALLOC_CTX *mem_ctx, const char *fname, SMB4ACL_T *
 
        for (i=0; i<gacl->acl_nace; i++) {
                struct gpfs_ace_v4 *gace = gpfs_ace_ptr(gacl, i);
-               SMB_ACE4PROP_T smbace;
+               SMB_ACE4PROP_T smbace = { 0 };
                DEBUG(10, ("type: %d, iflags: %x, flags: %x, mask: %x, "
                           "who: %d\n", gace->aceType, gace->aceIFlags,
                           gace->aceFlags, gace->aceMask, gace->aceWho));
 
-               ZERO_STRUCT(smbace);
                if (gace->aceIFlags & ACE4_IFLAG_SPECIAL_ID) {
                        smbace.flags |= SMB_ACE4_ID_SPECIAL;
                        switch (gace->aceWho) {
@@ -516,11 +631,11 @@ static int gpfs_get_nfs4_acl(TALLOC_CTX *mem_ctx, const char *fname, SMB4ACL_T *
 }
 
 static NTSTATUS gpfsacl_fget_nt_acl(vfs_handle_struct *handle,
-       files_struct *fsp, uint32 security_info,
+       files_struct *fsp, uint32_t security_info,
        TALLOC_CTX *mem_ctx,
        struct security_descriptor **ppdesc)
 {
-       SMB4ACL_T *pacl = NULL;
+       struct SMB4ACL_T *pacl = NULL;
        int     result;
        struct gpfs_config_data *config;
        TALLOC_CTX *frame = talloc_stackframe();
@@ -539,11 +654,12 @@ static NTSTATUS gpfsacl_fget_nt_acl(vfs_handle_struct *handle,
                return status;
        }
 
-       result = gpfs_get_nfs4_acl(frame, fsp->fsp_name->base_name, &pacl);
+       result = gpfs_get_nfs4_acl(frame, fsp, &pacl);
 
        if (result == 0) {
-               status = smb_fget_nt_acl_nfs4(fsp, security_info, mem_ctx,
-                                             ppdesc, pacl);
+               status = smb_fget_nt_acl_nfs4(fsp, &config->nfs4_params,
+                                             security_info,
+                                             mem_ctx, ppdesc, pacl);
                TALLOC_FREE(frame);
                return status;
        }
@@ -562,60 +678,79 @@ static NTSTATUS gpfsacl_fget_nt_acl(vfs_handle_struct *handle,
        return map_nt_error_from_unix(errno);
 }
 
-static NTSTATUS gpfsacl_get_nt_acl(vfs_handle_struct *handle,
-       const char *name,
-       uint32 security_info,
-       TALLOC_CTX *mem_ctx, struct security_descriptor **ppdesc)
+static bool vfs_gpfs_nfs4_ace_to_gpfs_ace(SMB_ACE4PROP_T *nfs4_ace,
+                                         struct gpfs_ace_v4 *gace,
+                                         uid_t owner_uid)
 {
-       SMB4ACL_T *pacl = NULL;
-       int     result;
-       struct gpfs_config_data *config;
-       TALLOC_CTX *frame = talloc_stackframe();
-       NTSTATUS status;
-
-       *ppdesc = NULL;
-
-       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);
+       gace->aceType = nfs4_ace->aceType;
+       gace->aceFlags = nfs4_ace->aceFlags;
+       gace->aceMask = nfs4_ace->aceMask;
+
+       if (nfs4_ace->flags & SMB_ACE4_ID_SPECIAL) {
+               switch(nfs4_ace->who.special_id) {
+               case SMB_ACE4_WHO_EVERYONE:
+                       gace->aceIFlags = ACE4_IFLAG_SPECIAL_ID;
+                       gace->aceWho = ACE4_SPECIAL_EVERYONE;
+                       break;
+               case SMB_ACE4_WHO_OWNER:
+                       /*
+                        * With GPFS it is not possible to deny ACL or
+                        * attribute access to the owner. Setting an
+                        * ACL with such an entry is not possible.
+                        * Denying ACL or attribute access for the
+                        * owner through a named ACL entry can be
+                        * stored in an ACL, it is just not effective.
+                        *
+                        * Map this case to a named entry to allow at
+                        * least setting this ACL, which will be
+                        * enforced by the smbd permission check. Do
+                        * not do this for an inheriting OWNER entry,
+                        * as this represents a CREATOR OWNER ACE. The
+                        * remaining limitation is that CREATOR OWNER
+                        * cannot deny ACL or attribute access.
+                        */
+                       if (!nfs_ace_is_inherit(nfs4_ace) &&
+                           nfs4_ace->aceType ==
+                                       SMB_ACE4_ACCESS_DENIED_ACE_TYPE &&
+                           nfs4_ace->aceMask & (SMB_ACE4_READ_ATTRIBUTES|
+                                                SMB_ACE4_WRITE_ATTRIBUTES|
+                                                SMB_ACE4_READ_ACL|
+                                                SMB_ACE4_WRITE_ACL)) {
+                               gace->aceIFlags = 0;
+                               gace->aceWho = owner_uid;
+                       } else {
+                               gace->aceIFlags = ACE4_IFLAG_SPECIAL_ID;
+                               gace->aceWho = ACE4_SPECIAL_OWNER;
+                       }
+                       break;
+               case SMB_ACE4_WHO_GROUP:
+                       gace->aceIFlags = ACE4_IFLAG_SPECIAL_ID;
+                       gace->aceWho = ACE4_SPECIAL_GROUP;
+                       break;
+               default:
+                       DBG_WARNING("Unsupported special_id %d\n",
+                                   nfs4_ace->who.special_id);
+                       return false;
+               }
 
-       if (result == 0) {
-               status = smb_get_nt_acl_nfs4(handle->conn, name, security_info,
-                                          mem_ctx, ppdesc, pacl);
-               TALLOC_FREE(frame);
-               return status;
+               return true;
        }
 
-       if (result > 0) {
-               DEBUG(10, ("retrying with posix acl...\n"));
-               status =  posix_get_nt_acl(handle->conn, name, security_info,
-                                          mem_ctx, ppdesc);
-               TALLOC_FREE(frame);
-               return status;
-       }
+       gace->aceIFlags = 0;
+       gace->aceWho = (nfs4_ace->aceFlags & SMB_ACE4_IDENTIFIER_GROUP) ?
+               nfs4_ace->who.gid : nfs4_ace->who.uid;
 
-       /* GPFS ACL was not read, something wrong happened, error code is set in errno */
-       TALLOC_FREE(frame);
-       return map_nt_error_from_unix(errno);
+       return true;
 }
 
 static struct gpfs_acl *vfs_gpfs_smbacl2gpfsacl(TALLOC_CTX *mem_ctx,
                                                files_struct *fsp,
-                                               SMB4ACL_T *smbacl,
+                                               struct SMB4ACL_T *smbacl,
                                                bool controlflags)
 {
        struct gpfs_acl *gacl;
        gpfs_aclLen_t gacl_len;
-       SMB4ACE_T *smbace;
+       struct SMB4ACE_T *smbace;
 
        gacl_len = offsetof(gpfs_acl_t, ace_v4) + sizeof(unsigned int)
                + smb_get_naces(smbacl) * sizeof(gpfs_ace_v4_t);
@@ -627,71 +762,25 @@ static struct gpfs_acl *vfs_gpfs_smbacl2gpfsacl(TALLOC_CTX *mem_ctx,
                return NULL;
        }
 
-       gacl->acl_level = 0; /* GPFS_ACL_LEVEL_BASE */
+       gacl->acl_level = 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 */
+               gacl->acl_level = 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 = gpfs_ace_ptr(gacl, gacl->acl_nace);
                SMB_ACE4PROP_T  *aceprop = smb_get_ace4(smbace);
+               bool add_ace;
 
-               gace->aceType = aceprop->aceType;
-               gace->aceFlags = aceprop->aceFlags;
-               gace->aceMask = aceprop->aceMask;
-
-               /*
-                * GPFS can't distinguish between WRITE and APPEND on
-                * files, so one being set without the other is an
-                * error. Sorry for the many ()'s :-)
-                */
-
-               if (!fsp->is_directory
-                   &&
-                   ((((gace->aceMask & ACE4_MASK_WRITE) == 0)
-                     && ((gace->aceMask & ACE4_MASK_APPEND) != 0))
-                    ||
-                    (((gace->aceMask & ACE4_MASK_WRITE) != 0)
-                     && ((gace->aceMask & ACE4_MASK_APPEND) == 0)))
-                   &&
-                   lp_parm_bool(fsp->conn->params->service, "gpfs",
-                                "merge_writeappend", True)) {
-                       DEBUG(2, ("vfs_gpfs.c: file [%s]: ACE contains "
-                                 "WRITE^APPEND, setting WRITE|APPEND\n",
-                                 fsp_str_dbg(fsp)));
-                       gace->aceMask |= ACE4_MASK_WRITE|ACE4_MASK_APPEND;
-               }
-
-               gace->aceIFlags = (aceprop->flags&SMB_ACE4_ID_SPECIAL) ? ACE4_IFLAG_SPECIAL_ID : 0;
-
-               if (aceprop->flags&SMB_ACE4_ID_SPECIAL)
-               {
-                       switch(aceprop->who.special_id)
-                       {
-                       case SMB_ACE4_WHO_EVERYONE:
-                               gace->aceWho = ACE4_SPECIAL_EVERYONE;
-                               break;
-                       case SMB_ACE4_WHO_OWNER:
-                               gace->aceWho = ACE4_SPECIAL_OWNER;
-                               break;
-                       case SMB_ACE4_WHO_GROUP:
-                               gace->aceWho = ACE4_SPECIAL_GROUP;
-                               break;
-                       default:
-                               DEBUG(8, ("unsupported special_id %d\n", aceprop->who.special_id));
-                               continue; /* don't add it !!! */
-                       }
-               } else {
-                       /* just only for the type safety... */
-                       if (aceprop->aceFlags&SMB_ACE4_IDENTIFIER_GROUP)
-                               gace->aceWho = aceprop->who.gid;
-                       else
-                               gace->aceWho = aceprop->who.uid;
+               add_ace = vfs_gpfs_nfs4_ace_to_gpfs_ace(aceprop, gace,
+                                                       fsp->fsp_name->st.st_ex_uid);
+               if (!add_ace) {
+                       continue;
                }
 
                gacl->acl_nace++;
@@ -703,7 +792,7 @@ static struct gpfs_acl *vfs_gpfs_smbacl2gpfsacl(TALLOC_CTX *mem_ctx,
 
 static bool gpfsacl_process_smbacl(vfs_handle_struct *handle,
                                   files_struct *fsp,
-                                  SMB4ACL_T *smbacl)
+                                  struct SMB4ACL_T *smbacl)
 {
        int ret;
        struct gpfs_acl *gacl;
@@ -738,29 +827,27 @@ static bool gpfsacl_process_smbacl(vfs_handle_struct *handle,
        return True;
 }
 
-static NTSTATUS gpfsacl_set_nt_acl_internal(vfs_handle_struct *handle, 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_t security_info_sent, const struct security_descriptor *psd)
 {
        struct gpfs_acl *acl;
        NTSTATUS result = NT_STATUS_ACCESS_DENIED;
 
        acl = (struct gpfs_acl*) vfs_gpfs_getacl(talloc_tos(),
-                                                fsp->fsp_name->base_name,
+                                                fsp,
                                                 false, 0);
        if (acl == NULL) {
                return map_nt_error_from_unix(errno);
        }
 
        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;
-               }
+               struct gpfs_config_data *config;
+
+               SMB_VFS_HANDLE_GET_DATA(handle, config,
+                                       struct gpfs_config_data,
+                                       return NT_STATUS_INTERNAL_ERROR);
 
                result = smb_set_nt_acl_nfs4(handle,
-                       fsp, security_info_sent, psd,
+                       fsp, &config->nfs4_params, security_info_sent, psd,
                        gpfsacl_process_smbacl);
        } else { /* assume POSIX ACL - by default... */
                result = set_nt_acl(fsp, security_info_sent, psd);
@@ -770,7 +857,7 @@ static NTSTATUS gpfsacl_set_nt_acl_internal(vfs_handle_struct *handle, files_str
        return result;
 }
 
-static NTSTATUS gpfsacl_fset_nt_acl(vfs_handle_struct *handle, files_struct *fsp, uint32 security_info_sent, const struct security_descriptor *psd)
+static NTSTATUS gpfsacl_fset_nt_acl(vfs_handle_struct *handle, files_struct *fsp, uint32_t security_info_sent, const struct security_descriptor *psd)
 {
        struct gpfs_config_data *config;
 
@@ -857,17 +944,18 @@ static SMB_ACL_T gpfs2smb_acl(const struct gpfs_acl *pacl, TALLOC_CTX *mem_ctx)
        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(struct files_struct *fsp,
+                                      gpfs_aclType_t type,
                                       TALLOC_CTX *mem_ctx)
 {
        struct gpfs_acl *pacl;
        SMB_ACL_T result = NULL;
 
-       pacl = vfs_gpfs_getacl(talloc_tos(), path, false, type);
+       pacl = vfs_gpfs_getacl(talloc_tos(), fsp, false, type);
 
        if (pacl == NULL) {
-               DEBUG(10, ("vfs_gpfs_getacl failed for %s with %s\n",
-                          path, strerror(errno)));
+               DBG_DEBUG("vfs_gpfs_getacl failed for %s with %s\n",
+                          fsp_str_dbg(fsp), strerror(errno));
                if (errno == 0) {
                        errno = EINVAL;
                }
@@ -901,10 +989,10 @@ static SMB_ACL_T gpfsacl_get_posix_acl(const char *path, gpfs_aclType_t type,
        return result;
 }
 
-static SMB_ACL_T gpfsacl_sys_acl_get_file(vfs_handle_struct *handle,
-                                         const char *path_p,
-                                         SMB_ACL_TYPE_T type,
-                                         TALLOC_CTX *mem_ctx)
+static SMB_ACL_T gpfsacl_sys_acl_get_fd(vfs_handle_struct *handle,
+                                       files_struct *fsp,
+                                       SMB_ACL_TYPE_T type,
+                                       TALLOC_CTX *mem_ctx)
 {
        gpfs_aclType_t gpfs_type;
        struct gpfs_config_data *config;
@@ -914,8 +1002,7 @@ static SMB_ACL_T gpfsacl_sys_acl_get_file(vfs_handle_struct *handle,
                                return NULL);
 
        if (!config->acl) {
-               return SMB_VFS_NEXT_SYS_ACL_GET_FILE(handle, path_p,
-                                                    type, mem_ctx);
+               return SMB_VFS_NEXT_SYS_ACL_GET_FD(handle, fsp, type, mem_ctx);
        }
 
        switch(type) {
@@ -929,97 +1016,7 @@ static SMB_ACL_T gpfsacl_sys_acl_get_file(vfs_handle_struct *handle,
                DEBUG(0, ("Got invalid type: %d\n", type));
                smb_panic("exiting");
        }
-
-       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,
-                                       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, 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);
+       return gpfsacl_get_posix_acl(fsp, gpfs_type, mem_ctx);
 }
 
 static int gpfsacl_sys_acl_blob_get_fd(vfs_handle_struct *handle,
@@ -1044,7 +1041,7 @@ static int gpfsacl_sys_acl_blob_get_fd(vfs_handle_struct *handle,
 
        errno = 0;
        acl = (struct gpfs_opaque_acl *) vfs_gpfs_getacl(mem_ctx,
-                                               fsp->fsp_name->base_name,
+                                               fsp,
                                                true,
                                                GPFS_ACL_TYPE_NFS4);
 
@@ -1172,21 +1169,21 @@ static struct gpfs_acl *smb2gpfs_acl(const SMB_ACL_T pacl,
        return result;
 }
 
-static int gpfsacl_sys_acl_set_file(vfs_handle_struct *handle,
-                                   const char *name,
-                                   SMB_ACL_TYPE_T type,
-                                   SMB_ACL_T theacl)
+static int gpfsacl_sys_acl_set_fd(vfs_handle_struct *handle,
+                                 files_struct *fsp,
+                                 SMB_ACL_TYPE_T type,
+                                 SMB_ACL_T theacl)
 {
-       struct gpfs_acl *gpfs_acl;
-       int result;
        struct gpfs_config_data *config;
+       struct gpfs_acl *gpfs_acl = NULL;
+       int result;
 
        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);
+               return SMB_VFS_NEXT_SYS_ACL_SET_FD(handle, fsp, type, theacl);
        }
 
        gpfs_acl = smb2gpfs_acl(theacl, type);
@@ -1194,33 +1191,18 @@ static int gpfsacl_sys_acl_set_file(vfs_handle_struct *handle,
                return -1;
        }
 
-       result = gpfswrap_putacl(discard_const_p(char, name),
-                                GPFS_PUTACL_STRUCT|GPFS_ACL_SAMBA, gpfs_acl);
-
+       /*
+        * This is no longer a handle based call.
+        */
+       result = gpfswrap_putacl(fsp->fsp_name->base_name,
+                                GPFS_PUTACL_STRUCT|GPFS_ACL_SAMBA,
+                                gpfs_acl);
        SAFE_FREE(gpfs_acl);
        return result;
 }
 
-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);
-}
-
-static int gpfsacl_sys_acl_delete_def_file(vfs_handle_struct *handle,
-                                          const char *path)
+static int gpfsacl_sys_acl_delete_def_fd(vfs_handle_struct *handle,
+                               files_struct *fsp)
 {
        struct gpfs_config_data *config;
 
@@ -1229,20 +1211,21 @@ static int gpfsacl_sys_acl_delete_def_file(vfs_handle_struct *handle,
                                return -1);
 
        if (!config->acl) {
-               return SMB_VFS_NEXT_SYS_ACL_DELETE_DEF_FILE(handle, path);
+               return SMB_VFS_NEXT_SYS_ACL_DELETE_DEF_FD(handle, fsp);
        }
 
        errno = ENOTSUP;
        return -1;
 }
 
+
 /*
  * Assumed: mode bits are shiftable and standard
  * Output: the new aceMask field for an smb nfs4 ace
  */
-static uint32 gpfsacl_mask_filter(uint32 aceType, uint32 aceMask, uint32 rwx)
+static uint32_t gpfsacl_mask_filter(uint32_t aceType, uint32_t aceMask, uint32_t rwx)
 {
-       const uint32 posix_nfs4map[3] = {
+       const uint32_t posix_nfs4map[3] = {
                 SMB_ACE4_EXECUTE, /* execute */
                SMB_ACE4_WRITE_DATA | SMB_ACE4_APPEND_DATA, /* write; GPFS specific */
                 SMB_ACE4_READ_DATA /* read */
@@ -1276,19 +1259,22 @@ static uint32 gpfsacl_mask_filter(uint32 aceType, uint32 aceMask, uint32 rwx)
 }
 
 static int gpfsacl_emu_chmod(vfs_handle_struct *handle,
-                            const char *path, mode_t mode)
+                            struct files_struct *fsp,
+                            mode_t mode)
 {
-       SMB4ACL_T *pacl = NULL;
+       struct smb_filename *fname = fsp->fsp_name;
+       char *path = fsp->fsp_name->base_name;
+       struct SMB4ACL_T *pacl = NULL;
        int     result;
        bool    haveAllowEntry[SMB_ACE4_WHO_EVERYONE + 1] = {False, False, False, False};
        int     i;
-       files_struct    fake_fsp; /* TODO: rationalize parametrization */
-       SMB4ACE_T       *smbace;
+       files_struct fake_fsp = { 0 }; /* TODO: rationalize parametrization */
+       struct SMB4ACE_T *smbace;
        TALLOC_CTX *frame = talloc_stackframe();
 
        DEBUG(10, ("gpfsacl_emu_chmod invoked for %s mode %o\n", path, mode));
 
-       result = gpfs_get_nfs4_acl(frame, path, &pacl);
+       result = gpfs_get_nfs4_acl(frame, fsp, &pacl);
        if (result) {
                TALLOC_FREE(frame);
                return result;
@@ -1328,12 +1314,11 @@ static int gpfsacl_emu_chmod(vfs_handle_struct *handle,
         * - if necessary
         */
        for(i = SMB_ACE4_WHO_OWNER; i<=SMB_ACE4_WHO_EVERYONE; i++) {
-               SMB_ACE4PROP_T  ace;
+               SMB_ACE4PROP_T ace = { 0 };
 
                if (haveAllowEntry[i]==True)
                        continue;
 
-               ZERO_STRUCT(ace);
                ace.aceType = SMB_ACE4_ACCESS_ALLOWED_ACE_TYPE;
                ace.flags |= SMB_ACE4_ID_SPECIAL;
                ace.who.special_id = i;
@@ -1355,9 +1340,12 @@ static int gpfsacl_emu_chmod(vfs_handle_struct *handle,
        }
 
        /* don't add complementary DENY ACEs here */
-       ZERO_STRUCT(fake_fsp);
-       fake_fsp.fsp_name = synthetic_smb_fname(
-               frame, path, NULL, NULL);
+       fake_fsp.fsp_name = synthetic_smb_fname(frame,
+                                               path,
+                                               NULL,
+                                               NULL,
+                                               fname->twrp,
+                                               0);
        if (fake_fsp.fsp_name == NULL) {
                errno = ENOMEM;
                TALLOC_FREE(frame);
@@ -1373,510 +1361,430 @@ static int gpfsacl_emu_chmod(vfs_handle_struct *handle,
        return 0; /* ok for [f]chmod */
 }
 
-static int vfs_gpfs_chmod(vfs_handle_struct *handle, const char *path, mode_t mode)
+static int vfs_gpfs_fchmod(vfs_handle_struct *handle, files_struct *fsp, mode_t mode)
 {
-       struct smb_filename *smb_fname_cpath;
+       SMB_STRUCT_STAT st;
        int rc;
 
-       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) {
+       rc = SMB_VFS_NEXT_FSTAT(handle, fsp, &st);
+       if (rc != 0) {
                return -1;
        }
 
        /* avoid chmod() if possible, to preserve acls */
-       if ((smb_fname_cpath->st.st_ex_mode & ~S_IFMT) == mode) {
+       if ((st.st_ex_mode & ~S_IFMT) == mode) {
                return 0;
        }
 
-       rc = gpfsacl_emu_chmod(handle, path, mode);
-       if (rc == 1)
-               return SMB_VFS_NEXT_CHMOD(handle, path, mode);
+       rc = gpfsacl_emu_chmod(handle, fsp, mode);
+       if (rc == 1) {
+               return SMB_VFS_NEXT_FCHMOD(handle, fsp, mode);
+       }
        return rc;
 }
 
-static int vfs_gpfs_fchmod(vfs_handle_struct *handle, files_struct *fsp, mode_t mode)
+static uint32_t vfs_gpfs_winattrs_to_dosmode(unsigned int winattrs)
 {
-                SMB_STRUCT_STAT st;
-                int rc;
-
-                if (SMB_VFS_NEXT_FSTAT(handle, fsp, &st) != 0) {
-                        return -1;
-                }
-
-                /* avoid chmod() if possible, to preserve acls */
-                if ((st.st_ex_mode & ~S_IFMT) == mode) {
-                        return 0;
-                }
-
-                rc = gpfsacl_emu_chmod(handle, fsp->fsp_name->base_name,
-                                       mode);
-                if (rc == 1)
-                        return SMB_VFS_NEXT_FCHMOD(handle, fsp, mode);
-                return rc;
-}
-
-static int gpfs_set_xattr(struct vfs_handle_struct *handle,  const char *path,
-                           const char *name, const void *value, size_t size,  int flags){
-       struct xattr_DOSATTRIB dosattrib;
-        enum ndr_err_code ndr_err;
-        DATA_BLOB blob;
-        unsigned int dosmode=0;
-        struct gpfs_winattr attrs;
-        int ret = 0;
-       struct gpfs_config_data *config;
-
-       SMB_VFS_HANDLE_GET_DATA(handle, config,
-                               struct gpfs_config_data,
-                               return -1);
+       uint32_t dosmode = 0;
 
-       if (!config->winattr) {
-               DEBUG(10, ("gpfs_set_xattr:name is %s -> next\n",name));
-               return SMB_VFS_NEXT_SETXATTR(handle,path,name,value,size,flags);
+       if (winattrs & GPFS_WINATTR_ARCHIVE){
+               dosmode |= FILE_ATTRIBUTE_ARCHIVE;
+       }
+       if (winattrs & GPFS_WINATTR_HIDDEN){
+               dosmode |= FILE_ATTRIBUTE_HIDDEN;
+       }
+       if (winattrs & GPFS_WINATTR_SYSTEM){
+               dosmode |= FILE_ATTRIBUTE_SYSTEM;
+       }
+       if (winattrs & GPFS_WINATTR_READONLY){
+               dosmode |= FILE_ATTRIBUTE_READONLY;
+       }
+       if (winattrs & GPFS_WINATTR_SPARSE_FILE) {
+               dosmode |= FILE_ATTRIBUTE_SPARSE;
+       }
+       if (winattrs & GPFS_WINATTR_OFFLINE) {
+               dosmode |= FILE_ATTRIBUTE_OFFLINE;
        }
 
-        DEBUG(10, ("gpfs_set_xattr: %s \n",path));
+       return dosmode;
+}
 
-        /* Only handle DOS Attributes */
-        if (strcmp(name,SAMBA_XATTR_DOS_ATTRIB) != 0){
-               DEBUG(5, ("gpfs_set_xattr:name is %s\n",name));
-               return SMB_VFS_NEXT_SETXATTR(handle,path,name,value,size,flags);
-        }
-
-       blob.data = discard_const_p(uint8_t, value);
-       blob.length = size;
-
-       ndr_err = ndr_pull_struct_blob(&blob, talloc_tos(), &dosattrib,
-                       (ndr_pull_flags_fn_t)ndr_pull_xattr_DOSATTRIB);
+static unsigned int vfs_gpfs_dosmode_to_winattrs(uint32_t dosmode)
+{
+       unsigned int winattrs = 0;
 
-       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
-               DEBUG(1, ("gpfs_set_xattr: bad ndr decode "
-                         "from EA on file %s: Error = %s\n",
-                         path, ndr_errstr(ndr_err)));
-               return false;
+       if (dosmode & FILE_ATTRIBUTE_ARCHIVE){
+               winattrs |= GPFS_WINATTR_ARCHIVE;
        }
-
-       if (dosattrib.version != 3) {
-               DEBUG(1, ("gpfs_set_xattr: expected dosattrib version 3, got "
-                         "%d\n", (int)dosattrib.version));
-               return false;
+       if (dosmode & FILE_ATTRIBUTE_HIDDEN){
+               winattrs |= GPFS_WINATTR_HIDDEN;
        }
-       if (!(dosattrib.info.info3.valid_flags & XATTR_DOSINFO_ATTRIB)) {
-               DEBUG(10, ("gpfs_set_xattr: XATTR_DOSINFO_ATTRIB not "
-                          "valid, ignoring\n"));
-               return true;
+       if (dosmode & FILE_ATTRIBUTE_SYSTEM){
+               winattrs |= GPFS_WINATTR_SYSTEM;
+       }
+       if (dosmode & FILE_ATTRIBUTE_READONLY){
+               winattrs |= GPFS_WINATTR_READONLY;
+       }
+       if (dosmode & FILE_ATTRIBUTE_SPARSE) {
+               winattrs |= GPFS_WINATTR_SPARSE_FILE;
+       }
+       if (dosmode & FILE_ATTRIBUTE_OFFLINE) {
+               winattrs |= GPFS_WINATTR_OFFLINE;
        }
 
-       dosmode = dosattrib.info.info3.attrib;
-
-        attrs.winAttrs = 0;
-        /*Just map RD_ONLY, ARCHIVE, SYSTEM HIDDEN and SPARSE. Ignore the others*/
-        if (dosmode & FILE_ATTRIBUTE_ARCHIVE){
-                attrs.winAttrs |= GPFS_WINATTR_ARCHIVE;
-        }
-        if (dosmode & FILE_ATTRIBUTE_HIDDEN){
-                        attrs.winAttrs |= GPFS_WINATTR_HIDDEN;
-                }
-        if (dosmode & FILE_ATTRIBUTE_SYSTEM){
-                        attrs.winAttrs |= GPFS_WINATTR_SYSTEM;
-                }
-        if (dosmode & FILE_ATTRIBUTE_READONLY){
-                        attrs.winAttrs |= GPFS_WINATTR_READONLY;
-        }
-        if (dosmode & FILE_ATTRIBUTE_SPARSE) {
-               attrs.winAttrs |= GPFS_WINATTR_SPARSE_FILE;
-       }
-
-
-       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,
-                                                    size, flags);
-               }
-
-                DEBUG(1, ("gpfs_set_xattr:Set GPFS attributes failed %d\n",ret));
-                return -1;
-        }
+       return winattrs;
+}
 
-        DEBUG(10, ("gpfs_set_xattr:Set attributes: 0x%x\n",attrs.winAttrs));
-        return 0;
+static struct timespec gpfs_timestruc64_to_timespec(struct gpfs_timestruc64 g)
+{
+       return (struct timespec) { .tv_sec = g.tv_sec, .tv_nsec = g.tv_nsec };
 }
 
-static ssize_t gpfs_get_xattr(struct vfs_handle_struct *handle,  const char *path,
-                              const char *name, void *value, size_t size){
-        char *attrstr = value;
-        unsigned int dosmode = 0;
-        struct gpfs_winattr attrs;
-        int ret = 0;
+static NTSTATUS vfs_gpfs_fget_dos_attributes(struct vfs_handle_struct *handle,
+                                            struct files_struct *fsp,
+                                            uint32_t *dosmode)
+{
        struct gpfs_config_data *config;
+       int fd = fsp_get_pathref_fd(fsp);
+       struct sys_proc_fd_path_buf buf;
+       const char *p = NULL;
+       struct gpfs_iattr64 iattr = { };
+       unsigned int litemask = 0;
+       struct timespec ts;
+       int ret;
 
        SMB_VFS_HANDLE_GET_DATA(handle, config,
                                struct gpfs_config_data,
-                               return -1);
+                               return NT_STATUS_INTERNAL_ERROR);
 
        if (!config->winattr) {
-               DEBUG(10, ("gpfs_get_xattr:name is %s -> next\n",name));
-               return SMB_VFS_NEXT_GETXATTR(handle,path,name,value,size);
+               return SMB_VFS_NEXT_FGET_DOS_ATTRIBUTES(handle, fsp, dosmode);
        }
 
-        DEBUG(10, ("gpfs_get_xattr: %s \n",path));
-
-        /* Only handle DOS Attributes */
-        if (strcmp(name,SAMBA_XATTR_DOS_ATTRIB) != 0){
-               DEBUG(5, ("gpfs_get_xattr:name is %s\n",name));
-                return SMB_VFS_NEXT_GETXATTR(handle,path,name,value,size);
-        }
-
-       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);
-               }
-
-               if (errno != EPERM && errno != EACCES) {
-                       dbg_lvl = 1;
+       if (fsp->fsp_flags.is_pathref && !config->pathref_ok.gpfs_fstat_x) {
+               if (fsp->fsp_flags.have_proc_fds) {
+                       p = sys_proc_fd_path(fd, &buf);
                } else {
-                       dbg_lvl = 5;
+                       p = fsp->fsp_name->base_name;
                }
-               DEBUG(dbg_lvl, ("gpfs_get_xattr: Get GPFS attributes failed: "
-                             "%d (%s)\n", ret, strerror(errno)));
-                return -1;
-        }
-
-        DEBUG(10, ("gpfs_get_xattr:Got attributes: 0x%x\n",attrs.winAttrs));
-
-        /*Just map RD_ONLY, ARCHIVE, SYSTEM, HIDDEN and SPARSE. Ignore the others*/
-        if (attrs.winAttrs & GPFS_WINATTR_ARCHIVE){
-                dosmode |= FILE_ATTRIBUTE_ARCHIVE;
-        }
-        if (attrs.winAttrs & GPFS_WINATTR_HIDDEN){
-                dosmode |= FILE_ATTRIBUTE_HIDDEN;
-        }
-        if (attrs.winAttrs & GPFS_WINATTR_SYSTEM){
-                dosmode |= FILE_ATTRIBUTE_SYSTEM;
-        }
-        if (attrs.winAttrs & GPFS_WINATTR_READONLY){
-                dosmode |= FILE_ATTRIBUTE_READONLY;
-        }
-        if (attrs.winAttrs & GPFS_WINATTR_SPARSE_FILE) {
-               dosmode |= FILE_ATTRIBUTE_SPARSE;
        }
 
-        snprintf(attrstr, size, "0x%2.2x",
-                (unsigned int)(dosmode & SAMBA_ATTRIBUTES_MASK));
-        DEBUG(10, ("gpfs_get_xattr: returning %s\n",attrstr));
-        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;
+       if (p != NULL) {
+               ret = gpfswrap_stat_x(p, &litemask, &iattr, sizeof(iattr));
+       } else {
+               ret = gpfswrap_fstat_x(fd, &litemask, &iattr, sizeof(iattr));
        }
-
-       fd = open(dir_name, O_RDONLY, 0);
-       TALLOC_FREE(dir_name);
-       if (fd == -1) {
-               return -1;
+       if (ret == -1 && errno == ENOSYS) {
+               return SMB_VFS_NEXT_FGET_DOS_ATTRIBUTES(handle, fsp, dosmode);
        }
 
-       set_effective_capability(DAC_OVERRIDE_CAPABILITY);
-       ret = fstatat(fd, rel_name, &st, flag);
-       drop_effective_capability(DAC_OVERRIDE_CAPABILITY);
-
-       close(fd);
+       if (ret == -1 && errno == EACCES) {
+               int saved_errno = 0;
 
-       if (ret == 0) {
-               init_stat_ex_from_stat(
-                       &smb_fname->st, &st,
-                       lp_fake_directory_create_times(SNUM(handle->conn)));
-       }
+               /*
+                * According to MS-FSA 2.1.5.1.2.1 "Algorithm to Check Access to
+                * an Existing File" FILE_LIST_DIRECTORY on a directory implies
+                * FILE_READ_ATTRIBUTES for directory entries. Being able to
+                * open a file implies FILE_LIST_DIRECTORY.
+                */
 
-       return ret;
-}
-#endif
+               set_effective_capability(DAC_OVERRIDE_CAPABILITY);
 
-static int vfs_gpfs_stat(struct vfs_handle_struct *handle,
-                        struct smb_filename *smb_fname)
-{
-       struct gpfs_winattr attrs;
-       char *fname = NULL;
-       NTSTATUS status;
-       int ret;
-       struct gpfs_config_data *config;
+               if (p != NULL) {
+                       ret = gpfswrap_stat_x(p,
+                                             &litemask,
+                                             &iattr,
+                                             sizeof(iattr));
+               } else {
+                       ret = gpfswrap_fstat_x(fd,
+                                              &litemask,
+                                              &iattr,
+                                              sizeof(iattr));
+               }
+               if (ret == -1) {
+                       saved_errno = errno;
+               }
 
-       SMB_VFS_HANDLE_GET_DATA(handle, config,
-                               struct gpfs_config_data,
-                               return -1);
+               drop_effective_capability(DAC_OVERRIDE_CAPABILITY);
 
-       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);
+               if (saved_errno != 0) {
+                       errno = saved_errno;
+               }
        }
-#endif
+
        if (ret == -1) {
-               return -1;
+               DBG_WARNING("Getting winattrs failed for %s: %s\n",
+                           fsp->fsp_name->base_name, strerror(errno));
+               return map_nt_error_from_unix(errno);
        }
 
-       if (!config->winattr) {
-               return 0;
-       }
+       ts = gpfs_timestruc64_to_timespec(iattr.ia_createtime);
 
-       status = get_full_smb_filename(talloc_tos(), smb_fname, &fname);
-       if (!NT_STATUS_IS_OK(status)) {
-               errno = map_errno_from_nt_status(status);
-               return -1;
-       }
-       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;
-       }
-       return 0;
+       *dosmode |= vfs_gpfs_winattrs_to_dosmode(iattr.ia_winflags);
+       update_stat_ex_create_time(&fsp->fsp_name->st, ts);
+
+       return NT_STATUS_OK;
 }
 
-static int vfs_gpfs_fstat(struct vfs_handle_struct *handle,
-                         struct files_struct *fsp, SMB_STRUCT_STAT *sbuf)
+static NTSTATUS vfs_gpfs_fset_dos_attributes(struct vfs_handle_struct *handle,
+                                            struct files_struct *fsp,
+                                            uint32_t dosmode)
 {
-       struct gpfs_winattr attrs;
-       int ret;
        struct gpfs_config_data *config;
+       struct gpfs_winattr attrs = { };
+       int ret;
 
        SMB_VFS_HANDLE_GET_DATA(handle, config,
                                struct gpfs_config_data,
-                               return -1);
+                               return NT_STATUS_INTERNAL_ERROR);
 
-       ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
-       if (ret == -1) {
-               return -1;
-       }
-       if ((fsp->fh == NULL) || (fsp->fh->fd == -1)) {
-               return 0;
-       }
        if (!config->winattr) {
-               return 0;
-       }
-
-       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;
-               sbuf->st_ex_btime.tv_nsec = attrs.creationTime.tv_nsec;
+               return SMB_VFS_NEXT_FSET_DOS_ATTRIBUTES(handle, fsp, dosmode);
        }
-       return 0;
-}
-
-static int vfs_gpfs_lstat(struct vfs_handle_struct *handle,
-                         struct smb_filename *smb_fname)
-{
-       struct gpfs_winattr attrs;
-       char *path = NULL;
-       NTSTATUS status;
-       int ret;
-       struct gpfs_config_data *config;
 
-       SMB_VFS_HANDLE_GET_DATA(handle, config,
-                               struct gpfs_config_data,
-                               return -1);
+       attrs.winAttrs = vfs_gpfs_dosmode_to_winattrs(dosmode);
 
-       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);
+       if (!fsp->fsp_flags.is_pathref) {
+               ret = gpfswrap_set_winattrs(fsp_get_io_fd(fsp),
+                                           GPFS_WINATTR_SET_ATTRS, &attrs);
+               if (ret == -1) {
+                       DBG_WARNING("Setting winattrs failed for %s: %s\n",
+                                   fsp_str_dbg(fsp), strerror(errno));
+                       return map_nt_error_from_unix(errno);
+               }
+               return NT_STATUS_OK;
+       }
+
+       if (fsp->fsp_flags.have_proc_fds) {
+               int fd = fsp_get_pathref_fd(fsp);
+               struct sys_proc_fd_path_buf buf;
+
+               ret = gpfswrap_set_winattrs_path(sys_proc_fd_path(fd, &buf),
+                                                GPFS_WINATTR_SET_ATTRS,
+                                                &attrs);
+               if (ret == -1) {
+                       DBG_WARNING("Setting winattrs failed for "
+                                   "[%s][%s]: %s\n",
+                                   buf.buf,
+                                   fsp_str_dbg(fsp),
+                                   strerror(errno));
+                       return map_nt_error_from_unix(errno);
+               }
+               return NT_STATUS_OK;
        }
-#endif
 
+       /*
+        * This is no longer a handle based call.
+        */
+       ret = gpfswrap_set_winattrs_path(fsp->fsp_name->base_name,
+                                        GPFS_WINATTR_SET_ATTRS,
+                                        &attrs);
        if (ret == -1) {
-               return -1;
+               DBG_WARNING("Setting winattrs failed for [%s]: %s\n",
+                           fsp_str_dbg(fsp), strerror(errno));
+               return map_nt_error_from_unix(errno);
        }
-       if (!config->winattr) {
+
+       return NT_STATUS_OK;
+}
+
+static int timespec_to_gpfs_time(
+       struct timespec ts, gpfs_timestruc_t *gt, int idx, int *flags)
+{
+       if (is_omit_timespec(&ts)) {
                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);
+       if (ts.tv_sec < 0 || ts.tv_sec > UINT32_MAX) {
+               DBG_NOTICE("GPFS uses 32-bit unsigned timestamps "
+                          "and cannot handle %jd.\n",
+                          (intmax_t)ts.tv_sec);
+               errno = ERANGE;
                return -1;
        }
-       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;
-       }
-       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));
-       }
+       *flags |= 1 << idx;
+       gt[idx].tv_sec = ts.tv_sec;
+       gt[idx].tv_nsec = ts.tv_nsec;
+       DBG_DEBUG("Setting GPFS time %d, flags 0x%x\n", idx, *flags);
+
+       return 0;
 }
 
-static int smbd_gpfs_set_times_path(char *path, struct smb_file_time *ft)
+static int smbd_gpfs_set_times(struct files_struct *fsp,
+                              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);
+       rc = timespec_to_gpfs_time(ft->atime, gpfs_times, 0, &flags);
+       if (rc != 0) {
+               return rc;
+       }
+
+       rc = timespec_to_gpfs_time(ft->mtime, gpfs_times, 1, &flags);
+       if (rc != 0) {
+               return rc;
+       }
+
        /* No good mapping from LastChangeTime to ctime, not storing */
-       timespec_to_gpfs_time(ft->create_time, gpfs_times, 3, &flags);
+       rc = timespec_to_gpfs_time(ft->create_time, gpfs_times, 3, &flags);
+       if (rc != 0) {
+               return rc;
+       }
 
        if (!flags) {
-               DEBUG(10, ("nothing to do, return to avoid EINVAL\n"));
+               DBG_DEBUG("nothing to do, return to avoid EINVAL\n");
                return 0;
        }
 
-       rc = gpfswrap_set_times_path(path, flags, gpfs_times);
+       if (!fsp->fsp_flags.is_pathref) {
+               rc = gpfswrap_set_times(fsp_get_io_fd(fsp), flags, gpfs_times);
+               if (rc != 0) {
+                       DBG_WARNING("gpfs_set_times(%s) failed: %s\n",
+                                   fsp_str_dbg(fsp), strerror(errno));
+               }
+               return rc;
+       }
+
 
-       if (rc != 0 && errno != ENOSYS) {
-               DEBUG(1,("gpfs_set_times() returned with error %s\n",
-                       strerror(errno)));
+       if (fsp->fsp_flags.have_proc_fds) {
+               int fd = fsp_get_pathref_fd(fsp);
+               struct sys_proc_fd_path_buf buf;
+
+               rc = gpfswrap_set_times_path(sys_proc_fd_path(fd, &buf),
+                                            flags,
+                                            gpfs_times);
+               if (rc != 0) {
+                       DBG_WARNING("gpfs_set_times_path(%s,%s) failed: %s\n",
+                                   fsp_str_dbg(fsp),
+                                   buf.buf,
+                                   strerror(errno));
+               }
+               return rc;
        }
 
+       /*
+        * This is no longer a handle based call.
+        */
+
+       rc = gpfswrap_set_times_path(fsp->fsp_name->base_name,
+                                    flags,
+                                    gpfs_times);
+       if (rc != 0) {
+               DBG_WARNING("gpfs_set_times_path(%s) failed: %s\n",
+                           fsp_str_dbg(fsp), 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)
+static int vfs_gpfs_fntimes(struct vfs_handle_struct *handle,
+               files_struct *fsp,
+               struct smb_file_time *ft)
 {
 
-        struct gpfs_winattr attrs;
-        int ret;
-        char *path = NULL;
-        NTSTATUS status;
+       struct gpfs_winattr attrs;
+       int ret;
        struct gpfs_config_data *config;
 
-       SMB_VFS_HANDLE_GET_DATA(handle, config,
+       SMB_VFS_HANDLE_GET_DATA(handle,
+                               config,
                                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;
-               }
+               return smbd_gpfs_set_times(fsp, ft);
        }
 
-       DEBUG(10,("gpfs_set_times() not available or disabled, "
-                 "use ntimes and winattr\n"));
+       DBG_DEBUG("gpfs_set_times() not available or disabled, "
+                 "use ntimes and winattr\n");
 
-        ret = SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
-        if(ret == -1){
+       ret = SMB_VFS_NEXT_FNTIMES(handle, fsp, ft);
+       if (ret == -1) {
                /* don't complain if access was denied */
                if (errno != EPERM && errno != EACCES) {
-                       DEBUG(1,("vfs_gpfs_ntimes: SMB_VFS_NEXT_NTIMES failed:"
-                                "%s", strerror(errno)));
+                       DBG_WARNING("SMB_VFS_NEXT_FNTIMES failed: %s\n",
+                                   strerror(errno));
                }
-                return -1;
-        }
+               return -1;
+       }
 
-        if(null_timespec(ft->create_time)){
-                DEBUG(10,("vfs_gpfs_ntimes:Create Time is NULL\n"));
-                return 0;
-        }
+       if (is_omit_timespec(&ft->create_time)) {
+               DBG_DEBUG("Create Time is NULL\n");
+               return 0;
+       }
 
        if (!config->winattr) {
                return 0;
        }
 
-        attrs.winAttrs = 0;
-        attrs.creationTime.tv_sec = ft->create_time.tv_sec;
-        attrs.creationTime.tv_nsec = ft->create_time.tv_nsec;
+       attrs.winAttrs = 0;
+       attrs.creationTime.tv_sec = ft->create_time.tv_sec;
+       attrs.creationTime.tv_nsec = ft->create_time.tv_nsec;
 
-       ret = gpfswrap_set_winattrs_path(discard_const_p(char, path),
+       if (!fsp->fsp_flags.is_pathref) {
+               ret = gpfswrap_set_winattrs(fsp_get_io_fd(fsp),
+                                           GPFS_WINATTR_SET_CREATION_TIME,
+                                           &attrs);
+               if (ret == -1 && errno != ENOSYS) {
+                       DBG_WARNING("Set GPFS ntimes failed %d\n", ret);
+                       return -1;
+               }
+               return ret;
+       }
+
+       if (fsp->fsp_flags.have_proc_fds) {
+               int fd = fsp_get_pathref_fd(fsp);
+               struct sys_proc_fd_path_buf buf;
+
+               ret = gpfswrap_set_winattrs_path(
+                       sys_proc_fd_path(fd, &buf),
+                       GPFS_WINATTR_SET_CREATION_TIME,
+                       &attrs);
+               if (ret == -1 && errno != ENOSYS) {
+                       DBG_WARNING("Set GPFS ntimes failed %d\n", ret);
+                       return -1;
+               }
+               return ret;
+       }
+
+       /*
+        * This is no longer a handle based call.
+        */
+       ret = gpfswrap_set_winattrs_path(fsp->fsp_name->base_name,
                                         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;
-        }
-        return 0;
+       if (ret == -1 && errno != ENOSYS) {
+               DBG_WARNING("Set GPFS ntimes failed %d\n", ret);
+               return -1;
+       }
 
+       return 0;
 }
 
 static int vfs_gpfs_fallocate(struct vfs_handle_struct *handle,
-                      struct files_struct *fsp, uint32_t mode,
-                      off_t offset, off_t len)
+                             struct files_struct *fsp, uint32_t mode,
+                             off_t offset, off_t len)
 {
-       int ret;
-       struct gpfs_config_data *config;
-
-       SMB_VFS_HANDLE_GET_DATA(handle, config,
-                               struct gpfs_config_data,
-                               return -1);
-
-       if (!config->prealloc) {
-               /* you should better not run fallocate() on GPFS at all */
-               errno = ENOTSUP;
-               return -1;
-       }
-
-       if (mode != 0) {
-               DEBUG(10, ("unmapped fallocate flags: %lx\n",
-                     (unsigned long)mode));
+       if (mode == (VFS_FALLOCATE_FL_PUNCH_HOLE|VFS_FALLOCATE_FL_KEEP_SIZE) &&
+           !fsp->fsp_flags.is_sparse &&
+           lp_strict_allocate(SNUM(fsp->conn))) {
+               /*
+                * This is from a ZERO_DATA request on a non-sparse
+                * file. GPFS does not support FL_KEEP_SIZE and thus
+                * cannot fill the whole again in the subsequent
+                * fallocate(FL_KEEP_SIZE). Deny this FL_PUNCH_HOLE
+                * call to not end up with a hole in a non-sparse
+                * file.
+                */
                errno = ENOTSUP;
                return -1;
        }
 
-       ret = gpfswrap_prealloc(fsp->fh->fd, offset, len);
-
-       if (ret == -1 && errno != ENOSYS) {
-               DEBUG(0, ("GPFS prealloc failed: %s\n", strerror(errno)));
-       } else if (ret == -1 && errno == ENOSYS) {
-               DEBUG(10, ("GPFS prealloc not supported.\n"));
-       } else {
-               DEBUG(10, ("GPFS prealloc succeeded.\n"));
-       }
-
-       return ret;
+       return SMB_VFS_NEXT_FALLOCATE(handle, fsp, mode, offset, len);
 }
 
 static int vfs_gpfs_ftruncate(vfs_handle_struct *handle, files_struct *fsp,
@@ -1893,7 +1801,7 @@ static int vfs_gpfs_ftruncate(vfs_handle_struct *handle, files_struct *fsp,
                return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, len);
        }
 
-       result = gpfswrap_ftruncate(fsp->fh->fd, len);
+       result = gpfswrap_ftruncate(fsp_get_io_fd(fsp), len);
        if ((result == -1) && (errno == ENOSYS)) {
                return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, len);
        }
@@ -1901,70 +1809,173 @@ static int vfs_gpfs_ftruncate(vfs_handle_struct *handle, files_struct *fsp,
 }
 
 static bool vfs_gpfs_is_offline(struct vfs_handle_struct *handle,
-                               const struct smb_filename *fname,
+                               struct files_struct *fsp,
                                SMB_STRUCT_STAT *sbuf)
 {
        struct gpfs_winattr attrs;
-       char *path = NULL;
-       NTSTATUS status;
        struct gpfs_config_data *config;
        int ret;
 
        SMB_VFS_HANDLE_GET_DATA(handle, config,
                                struct gpfs_config_data,
-                               return -1);
+                               return false);
 
        if (!config->winattr) {
-               return SMB_VFS_NEXT_IS_OFFLINE(handle, fname, sbuf);
-       }
-
-       status = get_full_smb_filename(talloc_tos(), fname, &path);
-       if (!NT_STATUS_IS_OK(status)) {
-               errno = map_errno_from_nt_status(status);
-               return -1;
+               return false;
        }
 
-       ret = gpfswrap_get_winattrs_path(path, &attrs);
+       ret = gpfswrap_get_winattrs(fsp_get_pathref_fd(fsp), &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);
+               DBG_DEBUG("%s is offline\n", fsp_str_dbg(fsp));
                return true;
        }
-       DEBUG(10, ("%s is online\n", path));
-       TALLOC_FREE(path);
-       return SMB_VFS_NEXT_IS_OFFLINE(handle, fname, sbuf);
+
+       DBG_DEBUG("%s is online\n", fsp_str_dbg(fsp));
+       return false;
+}
+
+static bool vfs_gpfs_fsp_is_offline(struct vfs_handle_struct *handle,
+                                   struct files_struct *fsp)
+{
+       struct gpfs_fsp_extension *ext;
+
+       ext = VFS_FETCH_FSP_EXTENSION(handle, fsp);
+       if (ext == NULL) {
+               /*
+                * Something bad happened, always ask.
+                */
+               return vfs_gpfs_is_offline(handle, fsp,
+                                          &fsp->fsp_name->st);
+       }
+
+       if (ext->offline) {
+               /*
+                * As long as it's offline, ask.
+                */
+               ext->offline = vfs_gpfs_is_offline(handle, fsp,
+                                                  &fsp->fsp_name->st);
+       }
+
+       return ext->offline;
 }
 
 static bool vfs_gpfs_aio_force(struct vfs_handle_struct *handle,
                               struct files_struct *fsp)
 {
-       return vfs_gpfs_is_offline(handle, fsp->fsp_name, &fsp->fsp_name->st);
+       return vfs_gpfs_fsp_is_offline(handle, fsp);
 }
 
 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 (SMB_VFS_IS_OFFLINE(handle->conn, fsp->fsp_name, &fsp->fsp_name->st))
-       {
+       if (vfs_gpfs_fsp_is_offline(handle, fsp)) {
                errno = ENOSYS;
                return -1;
        }
        return SMB_VFS_NEXT_SENDFILE(handle, tofd, fsp, hdr, offset, n);
 }
 
+#ifdef O_PATH
+static int vfs_gpfs_check_pathref_fstat_x(struct gpfs_config_data *config,
+                                         struct connection_struct *conn)
+{
+       struct gpfs_iattr64 iattr = {0};
+       unsigned int litemask = 0;
+       int saved_errno;
+       int fd;
+       int ret;
+
+       fd = open(conn->connectpath, O_PATH);
+       if (fd == -1) {
+               DBG_ERR("openat() of share with O_PATH failed: %s\n",
+                       strerror(errno));
+               return -1;
+       }
+
+       ret = gpfswrap_fstat_x(fd, &litemask, &iattr, sizeof(iattr));
+       if (ret == 0) {
+               close(fd);
+               config->pathref_ok.gpfs_fstat_x = true;
+               return 0;
+       }
+
+       saved_errno = errno;
+       ret = close(fd);
+       if (ret != 0) {
+               DBG_ERR("close failed: %s\n", strerror(errno));
+               return -1;
+       }
+
+       if (saved_errno != EBADF) {
+               DBG_ERR("gpfswrap_fstat_x() of O_PATH handle failed: %s\n",
+                       strerror(saved_errno));
+               return -1;
+       }
+
+       return 0;
+}
+#endif
+
+static int vfs_gpfs_check_pathref(struct gpfs_config_data *config,
+                                 struct connection_struct *conn)
+{
+#ifndef O_PATH
+       /*
+        * This code path leaves all struct gpfs_config_data.pathref_ok members
+        * initialized to false.
+        */
+       return 0;
+#else
+       int ret;
+
+       ret = vfs_gpfs_check_pathref_fstat_x(config, conn);
+       if (ret != 0) {
+               return -1;
+       }
+
+       return 0;
+#endif
+}
+
 static int vfs_gpfs_connect(struct vfs_handle_struct *handle,
                            const char *service, const char *user)
 {
        struct gpfs_config_data *config;
        int ret;
+       bool check_fstype;
+
+       ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
+       if (ret < 0) {
+               return ret;
+       }
+
+       if (IS_IPC(handle->conn)) {
+               return 0;
+       }
+
+       ret = gpfswrap_init();
+       if (ret < 0) {
+               DBG_ERR("Could not load GPFS library.\n");
+               return ret;
+       }
+
+       ret = gpfswrap_lib_init(0);
+       if (ret < 0) {
+               DBG_ERR("Could not open GPFS device file: %s\n",
+                       strerror(errno));
+               return ret;
+       }
 
-       gpfswrap_lib_init(0);
+       ret = gpfswrap_register_cifs_export();
+       if (ret < 0) {
+               DBG_ERR("Failed to register with GPFS: %s\n", strerror(errno));
+               return ret;
+       }
 
        config = talloc_zero(handle->conn, struct gpfs_config_data);
        if (!config) {
@@ -1973,7 +1984,34 @@ static int vfs_gpfs_connect(struct vfs_handle_struct *handle,
                return -1;
        }
 
-       ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
+       check_fstype = lp_parm_bool(SNUM(handle->conn), "gpfs",
+                                   "check_fstype", true);
+
+       if (check_fstype) {
+               const char *connectpath = handle->conn->connectpath;
+               struct statfs buf = { 0 };
+
+               ret = statfs(connectpath, &buf);
+               if (ret != 0) {
+                       DBG_ERR("statfs failed for share %s at path %s: %s\n",
+                               service, connectpath, strerror(errno));
+                       TALLOC_FREE(config);
+                       return ret;
+               }
+
+               if (buf.f_type != GPFS_SUPER_MAGIC) {
+                       DBG_ERR("SMB share %s, path %s not in GPFS file system."
+                               " statfs magic: 0x%jx\n",
+                               service,
+                               connectpath,
+                               (uintmax_t)buf.f_type);
+                       errno = EINVAL;
+                       TALLOC_FREE(config);
+                       return -1;
+               }
+       }
+
+       ret = smbacl4_get_vfs_params(handle->conn, &config->nfs4_params);
        if (ret < 0) {
                TALLOC_FREE(config);
                return ret;
@@ -2003,9 +2041,6 @@ static int vfs_gpfs_connect(struct vfs_handle_struct *handle,
        config->dfreequota = lp_parm_bool(SNUM(handle->conn), "gpfs",
                                          "dfreequota", false);
 
-       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",
@@ -2013,6 +2048,14 @@ static int vfs_gpfs_connect(struct vfs_handle_struct *handle,
        config->recalls = lp_parm_bool(SNUM(handle->conn), "gpfs",
                                       "recalls", true);
 
+       ret = vfs_gpfs_check_pathref(config, handle->conn);
+       if (ret != 0) {
+               DBG_ERR("vfs_gpfs_check_pathref() on [%s] failed\n",
+                       handle->conn->connectpath);
+               TALLOC_FREE(config);
+               return -1;
+       }
+
        SMB_VFS_HANDLE_SET_DATA(handle, config,
                                NULL, struct gpfs_config_data,
                                return -1);
@@ -2042,51 +2085,13 @@ static int vfs_gpfs_connect(struct vfs_handle_struct *handle,
                }
        }
 
-       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;
-       }
+       /*
+        * Unless we have an async implementation of get_dos_attributes turn
+        * this off.
+        */
+       lp_do_parameter(SNUM(handle->conn), "smbd async dosmode", "false");
 
-       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;
+       return 0;
 }
 
 static int get_gpfs_quota(const char *pathname, int type, int id,
@@ -2094,9 +2099,7 @@ static int get_gpfs_quota(const char *pathname, int type, int id,
 {
        int ret;
 
-       ZERO_STRUCTP(qi);
-       ret = gpfswrap_quotactl(discard_const_p(char, pathname),
-                               GPFS_QCMD(Q_GETQUOTA, type), id, qi);
+       ret = gpfswrap_quotactl(pathname, GPFS_QCMD(Q_GETQUOTA, type), id, qi);
 
        if (ret) {
                if (errno == GPFS_E_NO_QUOTA_INST) {
@@ -2116,58 +2119,6 @@ static int get_gpfs_quota(const char *pathname, int type, int id,
        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,
-                              struct gpfs_quotaInfo *qi_group,
-                              struct gpfs_quotaInfo *qi_fset)
-{
-       int err;
-       char *dir_path;
-       bool b;
-
-       /*
-        * 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 path %s, dir %s, errno %d.\n",
-                         path, dir_path, errno));
-               return err;
-       }
-
-       err = get_gpfs_quota(path, GPFS_USRQUOTA, uid, qi_user);
-       if (err) {
-               return err;
-       }
-
-       err = get_gpfs_quota(path, GPFS_GRPQUOTA, gid, qi_group);
-       if (err) {
-               return err;
-       }
-
-       err = get_gpfs_quota(path, GPFS_FILESETQUOTA, *fset_id, qi_fset);
-       if (err) {
-               return err;
-       }
-
-       return 0;
-}
-
 static void vfs_gpfs_disk_free_quota(struct gpfs_quotaInfo qi, time_t cur_time,
                                     uint64_t *dfree, uint64_t *dsize)
 {
@@ -2207,27 +2158,29 @@ 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,
-                                  uint64_t *bsize,
-                                  uint64_t *dfree, uint64_t *dsize)
+static uint64_t vfs_gpfs_disk_free(vfs_handle_struct *handle,
+                               const struct smb_filename *smb_fname,
+                               uint64_t *bsize,
+                               uint64_t *dfree,
+                               uint64_t *dsize)
 {
        struct security_unix_token *utok;
-       struct gpfs_quotaInfo qi_user, qi_group, qi_fset;
+       struct gpfs_quotaInfo qi_user = { 0 }, qi_group = { 0 };
        struct gpfs_config_data *config;
-       int err, fset_id;
+       int err;
        time_t cur_time;
 
        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,
+               return SMB_VFS_NEXT_DISK_FREE(handle, smb_fname,
                                              bsize, dfree, dsize);
        }
 
-       err = sys_fsusage(path, dfree, dsize);
+       err = sys_fsusage(smb_fname->base_name, dfree, dsize);
        if (err) {
                DEBUG (0, ("Could not get fs usage, errno %d\n", errno));
-               return SMB_VFS_NEXT_DISK_FREE(handle, path,
+               return SMB_VFS_NEXT_DISK_FREE(handle, smb_fname,
                                              bsize, dfree, dsize);
        }
 
@@ -2238,10 +2191,34 @@ static uint64_t vfs_gpfs_disk_free(vfs_handle_struct *handle, const char *path,
                   (unsigned long long)*dfree, (unsigned long long)*dsize));
 
        utok = handle->conn->session_info->unix_token;
-       err = vfs_gpfs_get_quotas(path, utok->uid, utok->gid, &fset_id,
-                                 &qi_user, &qi_group, &qi_fset);
+
+       err = get_gpfs_quota(smb_fname->base_name,
+                       GPFS_USRQUOTA, utok->uid, &qi_user);
        if (err) {
-               return SMB_VFS_NEXT_DISK_FREE(handle, path,
+               return SMB_VFS_NEXT_DISK_FREE(handle, smb_fname,
+                                             bsize, dfree, dsize);
+       }
+
+       /*
+        * If new files created under this folder get this folder's
+        * GID, then available space is governed by the quota of the
+        * folder's GID, not the primary group of the creating user.
+        */
+       if (VALID_STAT(smb_fname->st) &&
+           S_ISDIR(smb_fname->st.st_ex_mode) &&
+           smb_fname->st.st_ex_mode & S_ISGID) {
+               become_root();
+               err = get_gpfs_quota(smb_fname->base_name, GPFS_GRPQUOTA,
+                                    smb_fname->st.st_ex_gid, &qi_group);
+               unbecome_root();
+
+       } else {
+               err = get_gpfs_quota(smb_fname->base_name, GPFS_GRPQUOTA,
+                                    utok->gid, &qi_group);
+       }
+
+       if (err) {
+               return SMB_VFS_NEXT_DISK_FREE(handle, smb_fname,
                                              bsize, dfree, dsize);
        }
 
@@ -2251,13 +2228,34 @@ static uint64_t vfs_gpfs_disk_free(vfs_handle_struct *handle, const char *path,
        vfs_gpfs_disk_free_quota(qi_user, cur_time, dfree, dsize);
        vfs_gpfs_disk_free_quota(qi_group, cur_time, dfree, dsize);
 
-       /* Id 0 indicates the default quota, not an actual quota */
-       if (fset_id != 0) {
-               vfs_gpfs_disk_free_quota(qi_fset, cur_time, dfree, dsize);
-       }
+       return *dfree / 2;
+}
 
-       disk_norm(bsize, dfree, dsize);
-       return *dfree;
+static int vfs_gpfs_get_quota(vfs_handle_struct *handle,
+                               const struct smb_filename *smb_fname,
+                               enum SMB_QUOTA_TYPE qtype,
+                               unid_t id,
+                               SMB_DISK_QUOTA *dq)
+{
+       switch(qtype) {
+               /*
+                * User/group quota are being used for disk-free
+                * determination, which in this module is done directly
+                * by the disk-free function. It's important that this
+                * module does not return wrong quota values by mistake,
+                * which would modify the correct values set by disk-free.
+                * User/group quota are also being used for processing
+                * NT_TRANSACT_GET_USER_QUOTA in smb1 protocol, which is
+                * currently not supported by this module.
+                */
+               case SMB_USER_QUOTA_TYPE:
+               case SMB_GROUP_QUOTA_TYPE:
+                       errno = ENOSYS;
+                       return -1;
+               default:
+                       return SMB_VFS_NEXT_GET_QUOTA(handle, smb_fname,
+                                       qtype, id, dq);
+       }
 }
 
 static uint32_t vfs_gpfs_capabilities(struct vfs_handle_struct *handle,
@@ -2278,30 +2276,52 @@ static uint32_t vfs_gpfs_capabilities(struct vfs_handle_struct *handle,
        return next;
 }
 
-static int vfs_gpfs_open(struct vfs_handle_struct *handle,
-                        struct smb_filename *smb_fname, files_struct *fsp,
-                        int flags, mode_t mode)
+static int vfs_gpfs_openat(struct vfs_handle_struct *handle,
+                          const struct files_struct *dirfsp,
+                          const struct smb_filename *smb_fname,
+                          files_struct *fsp,
+                          const struct vfs_open_how *_how)
 {
-       struct gpfs_config_data *config;
+       struct vfs_open_how how = *_how;
+       struct gpfs_config_data *config = NULL;
+       struct gpfs_fsp_extension *ext = NULL;
+       int ret;
 
        SMB_VFS_HANDLE_GET_DATA(handle, config,
                                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->hsm && !config->recalls &&
+           !fsp->fsp_flags.is_pathref &&
+           vfs_gpfs_fsp_is_offline(handle, fsp))
+       {
+               DBG_DEBUG("Refusing access to offline file %s\n",
+                         fsp_str_dbg(fsp));
+               errno = EACCES;
+               return -1;
        }
 
        if (config->syncio) {
-               flags |= O_SYNC;
+               how.flags |= O_SYNC;
+       }
+
+       ext = VFS_ADD_FSP_EXTENSION(handle, fsp, struct gpfs_fsp_extension,
+                                   NULL);
+       if (ext == NULL) {
+               errno = ENOMEM;
+               return -1;
+       }
+
+       /*
+        * Assume the file is offline until gpfs tells us it's online.
+        */
+       *ext = (struct gpfs_fsp_extension) { .offline = true };
+
+       ret = SMB_VFS_NEXT_OPENAT(handle, dirfsp, smb_fname, fsp, &how);
+       if (ret == -1) {
+               VFS_REMOVE_FSP_EXTENSION(handle, fsp);
        }
-       return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
+       return ret;
 }
 
 static ssize_t vfs_gpfs_pread(vfs_handle_struct *handle, files_struct *fsp,
@@ -2310,8 +2330,7 @@ static ssize_t vfs_gpfs_pread(vfs_handle_struct *handle, files_struct *fsp,
        ssize_t ret;
        bool was_offline;
 
-       was_offline = SMB_VFS_IS_OFFLINE(handle->conn, fsp->fsp_name,
-                                        &fsp->fsp_name->st);
+       was_offline = vfs_gpfs_fsp_is_offline(handle, fsp);
 
        ret = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
 
@@ -2327,8 +2346,8 @@ static ssize_t vfs_gpfs_pread(vfs_handle_struct *handle, files_struct *fsp,
 struct vfs_gpfs_pread_state {
        struct files_struct *fsp;
        ssize_t ret;
-       int err;
        bool was_offline;
+       struct vfs_aio_state vfs_aio_state;
 };
 
 static void vfs_gpfs_pread_done(struct tevent_req *subreq);
@@ -2347,8 +2366,7 @@ static struct tevent_req *vfs_gpfs_pread_send(struct vfs_handle_struct *handle,
        if (req == NULL) {
                return NULL;
        }
-       state->was_offline = SMB_VFS_IS_OFFLINE(handle->conn, fsp->fsp_name,
-                                               &fsp->fsp_name->st);
+       state->was_offline = vfs_gpfs_fsp_is_offline(handle, fsp);
        state->fsp = fsp;
        subreq = SMB_VFS_NEXT_PREAD_SEND(state, ev, handle, fsp, data,
                                         n, offset);
@@ -2366,21 +2384,22 @@ static void vfs_gpfs_pread_done(struct tevent_req *subreq)
        struct vfs_gpfs_pread_state *state = tevent_req_data(
                req, struct vfs_gpfs_pread_state);
 
-       state->ret = SMB_VFS_PREAD_RECV(subreq, &state->err);
+       state->ret = SMB_VFS_PREAD_RECV(subreq, &state->vfs_aio_state);
        TALLOC_FREE(subreq);
        tevent_req_done(req);
 }
 
-static ssize_t vfs_gpfs_pread_recv(struct tevent_req *req, int *err)
+static ssize_t vfs_gpfs_pread_recv(struct tevent_req *req,
+                                  struct vfs_aio_state *vfs_aio_state)
 {
        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)) {
+       if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
                return -1;
        }
-       *err = state->err;
+       *vfs_aio_state = state->vfs_aio_state;
 
        if ((state->ret != -1) && state->was_offline) {
                DEBUG(10, ("sending notify\n"));
@@ -2398,8 +2417,7 @@ static ssize_t vfs_gpfs_pwrite(vfs_handle_struct *handle, files_struct *fsp,
        ssize_t ret;
        bool was_offline;
 
-       was_offline = SMB_VFS_IS_OFFLINE(handle->conn, fsp->fsp_name,
-                                        &fsp->fsp_name->st);
+       was_offline = vfs_gpfs_fsp_is_offline(handle, fsp);
 
        ret = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
 
@@ -2415,8 +2433,8 @@ static ssize_t vfs_gpfs_pwrite(vfs_handle_struct *handle, files_struct *fsp,
 struct vfs_gpfs_pwrite_state {
        struct files_struct *fsp;
        ssize_t ret;
-       int err;
        bool was_offline;
+       struct vfs_aio_state vfs_aio_state;
 };
 
 static void vfs_gpfs_pwrite_done(struct tevent_req *subreq);
@@ -2436,8 +2454,7 @@ static struct tevent_req *vfs_gpfs_pwrite_send(
        if (req == NULL) {
                return NULL;
        }
-       state->was_offline = SMB_VFS_IS_OFFLINE(handle->conn, fsp->fsp_name,
-                                               &fsp->fsp_name->st);
+       state->was_offline = vfs_gpfs_fsp_is_offline(handle, fsp);
        state->fsp = fsp;
        subreq = SMB_VFS_NEXT_PWRITE_SEND(state, ev, handle, fsp, data,
                                         n, offset);
@@ -2455,21 +2472,22 @@ static void vfs_gpfs_pwrite_done(struct tevent_req *subreq)
        struct vfs_gpfs_pwrite_state *state = tevent_req_data(
                req, struct vfs_gpfs_pwrite_state);
 
-       state->ret = SMB_VFS_PWRITE_RECV(subreq, &state->err);
+       state->ret = SMB_VFS_PWRITE_RECV(subreq, &state->vfs_aio_state);
        TALLOC_FREE(subreq);
        tevent_req_done(req);
 }
 
-static ssize_t vfs_gpfs_pwrite_recv(struct tevent_req *req, int *err)
+static ssize_t vfs_gpfs_pwrite_recv(struct tevent_req *req,
+                                   struct vfs_aio_state *vfs_aio_state)
 {
        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)) {
+       if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
                return -1;
        }
-       *err = state->err;
+       *vfs_aio_state = state->vfs_aio_state;
 
        if ((state->ret != -1) && state->was_offline) {
                DEBUG(10, ("sending notify\n"));
@@ -2485,34 +2503,32 @@ static ssize_t vfs_gpfs_pwrite_recv(struct tevent_req *req, int *err)
 static struct vfs_fn_pointers vfs_gpfs_fns = {
        .connect_fn = vfs_gpfs_connect,
        .disk_free_fn = vfs_gpfs_disk_free,
+       .get_quota_fn = vfs_gpfs_get_quota,
        .fs_capabilities_fn = vfs_gpfs_capabilities,
-       .kernel_flock_fn = vfs_gpfs_kernel_flock,
+       .filesystem_sharemode_fn = vfs_gpfs_filesystem_sharemode,
        .linux_setlease_fn = vfs_gpfs_setlease,
-       .get_real_filename_fn = vfs_gpfs_get_real_filename,
+       .get_real_filename_at_fn = vfs_gpfs_get_real_filename_at,
+       .get_dos_attributes_send_fn = vfs_not_implemented_get_dos_attributes_send,
+       .get_dos_attributes_recv_fn = vfs_not_implemented_get_dos_attributes_recv,
+       .fget_dos_attributes_fn = vfs_gpfs_fget_dos_attributes,
+       .fset_dos_attributes_fn = vfs_gpfs_fset_dos_attributes,
        .fget_nt_acl_fn = gpfsacl_fget_nt_acl,
-       .get_nt_acl_fn = gpfsacl_get_nt_acl,
        .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,
-       .chmod_fn = vfs_gpfs_chmod,
+       .sys_acl_delete_def_fd_fn = gpfsacl_sys_acl_delete_def_fd,
        .fchmod_fn = vfs_gpfs_fchmod,
        .close_fn = vfs_gpfs_close,
-       .setxattr_fn = gpfs_set_xattr,
-       .getxattr_fn = gpfs_get_xattr,
-       .stat_fn = vfs_gpfs_stat,
-       .fstat_fn = vfs_gpfs_fstat,
-       .lstat_fn = vfs_gpfs_lstat,
-       .ntimes_fn = vfs_gpfs_ntimes,
-       .is_offline_fn = vfs_gpfs_is_offline,
+       .stat_fn = nfs4_acl_stat,
+       .fstat_fn = nfs4_acl_fstat,
+       .lstat_fn = nfs4_acl_lstat,
+       .fstatat_fn = nfs4_acl_fstatat,
+       .fntimes_fn = vfs_gpfs_fntimes,
        .aio_force_fn = vfs_gpfs_aio_force,
        .sendfile_fn = vfs_gpfs_sendfile,
        .fallocate_fn = vfs_gpfs_fallocate,
-       .open_fn = vfs_gpfs_open,
+       .openat_fn = vfs_gpfs_openat,
        .pread_fn = vfs_gpfs_pread,
        .pread_send_fn = vfs_gpfs_pread_send,
        .pread_recv_fn = vfs_gpfs_pread_recv,
@@ -2522,16 +2538,9 @@ static struct vfs_fn_pointers vfs_gpfs_fns = {
        .ftruncate_fn = vfs_gpfs_ftruncate
 };
 
-NTSTATUS vfs_gpfs_init(void);
-NTSTATUS vfs_gpfs_init(void)
+static_decl_vfs;
+NTSTATUS vfs_gpfs_init(TALLOC_CTX *ctx)
 {
-       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);
 }