s3:vfs_gpfs use non_posix_sys_acl_blob_get_*_helper
[obnox/samba/samba-obnox.git] / source3 / modules / vfs_gpfs.c
index e8d8bd38aeb02d018460ce2f405b064b27a3c106..d098bdee04c4a5e4e9fb1c0971a4109f2bbc14dc 100644 (file)
@@ -25,6 +25,7 @@
 #include "smbd/smbd.h"
 #include "librpc/gen_ndr/ndr_xattr.h"
 #include "include/smbprofile.h"
+#include "modules/non_posix_acls.h"
 
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_VFS
 #include <gpfs_gpl.h>
 #include "nfs4_acls.h"
 #include "vfs_gpfs.h"
+#include "system/filesys.h"
+#include "auth.h"
+#include "lib/util/tevent_unix.h"
 
 struct gpfs_config_data {
        bool sharemodes;
        bool leases;
        bool hsm;
+       bool syncio;
+       bool winattr;
+       bool ftruncate;
+       bool getrealfilename;
+       bool dfreequota;
+       bool prealloc;
+       bool acl;
 };
 
 
-static int vfs_gpfs_kernel_flock(vfs_handle_struct *handle, files_struct *fsp, 
+static int vfs_gpfs_kernel_flock(vfs_handle_struct *handle, files_struct *fsp,
                                 uint32 share_mode, uint32 access_mask)
 {
 
        struct gpfs_config_data *config;
+       int ret = 0;
 
        SMB_VFS_HANDLE_GET_DATA(handle, config,
                                struct gpfs_config_data,
                                return -1);
 
+       if(!config->sharemodes) {
+               return 0;
+       }
+
        START_PROFILE(syscall_kernel_flock);
 
        kernel_flock(fsp->fh->fd, share_mode, access_mask);
 
-       if (config->sharemodes
-               && !set_gpfs_sharemode(fsp, access_mask, fsp->share_access)) {
-               return -1;
+       if (!set_gpfs_sharemode(fsp, access_mask, fsp->share_access)) {
+               ret = -1;
        }
 
        END_PROFILE(syscall_kernel_flock);
 
-       return 0;
+       return ret;
 }
 
 static int vfs_gpfs_close(vfs_handle_struct *handle, files_struct *fsp)
@@ -90,21 +105,15 @@ static int vfs_gpfs_setlease(vfs_handle_struct *handle, files_struct *fsp,
                                struct gpfs_config_data,
                                return -1);
 
-       START_PROFILE(syscall_linux_setlease);
-
        if (linux_set_lease_sighandler(fsp->fh->fd) == -1)
                return -1;
 
+       START_PROFILE(syscall_linux_setlease);
+
        if (config->leases) {
                ret = set_gpfs_lease(fsp->fh->fd,leasetype);
        }
 
-       if (ret < 0) {
-               /* This must have come from GPFS not being available */
-               /* or some other error, hence call the default */
-               ret = linux_setlease(fsp->fh->fd, leasetype);
-       }
-
        END_PROFILE(syscall_linux_setlease);
 
        return ret;
@@ -121,6 +130,16 @@ static int vfs_gpfs_get_real_filename(struct vfs_handle_struct *handle,
        char real_pathname[PATH_MAX+1];
        int buflen;
        bool mangled;
+       struct gpfs_config_data *config;
+
+       SMB_VFS_HANDLE_GET_DATA(handle, config,
+                               struct gpfs_config_data,
+                               return -1);
+
+       if (!config->getrealfilename) {
+               return SMB_VFS_NEXT_GET_REAL_FILENAME(handle, path, name,
+                                                     mem_ctx, found_name);
+       }
 
        mangled = mangle_is_mangled(name, handle->conn->params);
        if (mangled) {
@@ -183,7 +202,7 @@ static int vfs_gpfs_get_real_filename(struct vfs_handle_struct *handle,
 
 static void gpfs_dumpacl(int level, struct gpfs_acl *gacl)
 {
-       int     i;
+       gpfs_aclCount_t i;
        if (gacl==NULL)
        {
                DEBUG(0, ("gpfs acl is NULL\n"));
@@ -201,6 +220,79 @@ static void gpfs_dumpacl(int level, struct gpfs_acl *gacl)
        }
 }
 
+/*
+ * get the ACL from GPFS, allocated on the specified mem_ctx
+ * internally retries when initial buffer was too small
+ *
+ * caller needs to cast result to either
+ * raw = yes: struct gpfs_opaque_acl
+ * raw = no: struct gpfs_acl
+ *
+ */
+static void *vfs_gpfs_getacl(TALLOC_CTX *mem_ctx,
+                        const char *fname,
+                        const bool raw,
+                        const gpfs_aclType_t type)
+{
+
+       void *aclbuf;
+       size_t size = 512;
+       int ret, flags;
+       unsigned int *len;
+       size_t struct_size;
+
+again:
+
+       aclbuf = talloc_zero_size(mem_ctx, size);
+       if (aclbuf == NULL) {
+               errno = ENOMEM;
+               return NULL;
+       }
+
+       if (raw) {
+               struct gpfs_opaque_acl *buf = (struct gpfs_opaque_acl *) aclbuf;
+               buf->acl_type = type;
+               flags = GPFS_GETACL_NATIVE;
+               len = (unsigned int *) &(buf->acl_buffer_len);
+               struct_size = sizeof(struct gpfs_opaque_acl);
+       } else {
+               struct gpfs_acl *buf = (struct gpfs_acl *) aclbuf;
+               buf->acl_type = type;
+               flags = GPFS_GETACL_STRUCT;
+               len = &(buf->acl_len);
+               struct_size = sizeof(struct gpfs_acl);
+       }
+
+       /* set the length of the buffer as input value */
+       *len = size;
+
+       errno = 0;
+       ret = smbd_gpfs_getacl((char *)fname, flags, aclbuf);
+       if ((ret != 0) && (errno == ENOSPC)) {
+               /*
+                * get the size needed to accommodate the complete buffer
+                *
+                * the value returned only applies to the ACL blob in the
+                * struct so make sure to also have headroom for the first
+                * struct members by adding room for the complete struct
+                * (might be a few bytes too much then)
+                */
+               size = *len + struct_size;
+               talloc_free(aclbuf);
+               DEBUG(10, ("Increasing ACL buffer size to %zu\n", size));
+               goto again;
+       }
+
+       if (ret != 0) {
+               DEBUG(5, ("smbd_gpfs_getacl failed with %s\n",
+                         strerror(errno)));
+               talloc_free(aclbuf);
+               return NULL;
+       }
+
+       return aclbuf;
+}
+
 static struct gpfs_acl *gpfs_getacl_alloc(const char *fname, gpfs_aclType_t type)
 {
        struct gpfs_acl *acl;
@@ -219,11 +311,12 @@ static struct gpfs_acl *gpfs_getacl_alloc(const char *fname, gpfs_aclType_t type
        acl->acl_version = 0;
        acl->acl_type = type;
 
-       ret = smbd_gpfs_getacl((char *)fname, GPFS_GETACL_STRUCT | GPFS_ACL_SAMBA, acl);
+       ret = smbd_gpfs_getacl((char *)fname, GPFS_GETACL_STRUCT, acl);
        if ((ret != 0) && (errno == ENOSPC)) {
                struct gpfs_acl *new_acl = (struct gpfs_acl *)TALLOC_SIZE(
                        mem_ctx, acl->acl_len + sizeof(struct gpfs_acl));
                if (new_acl == NULL) {
+                       talloc_free(acl);
                        errno = ENOMEM;
                        return NULL;
                }
@@ -232,13 +325,14 @@ static struct gpfs_acl *gpfs_getacl_alloc(const char *fname, gpfs_aclType_t type
                new_acl->acl_level = acl->acl_level;
                new_acl->acl_version = acl->acl_version;
                new_acl->acl_type = acl->acl_type;
+               talloc_free(acl);
                acl = new_acl;
 
-               ret = smbd_gpfs_getacl((char *)fname, GPFS_GETACL_STRUCT | GPFS_ACL_SAMBA, acl);
+               ret = smbd_gpfs_getacl((char *)fname, GPFS_GETACL_STRUCT, acl);
        }
-       if (ret != 0)
-       {
+       if (ret != 0) {
                DEBUG(8, ("smbd_gpfs_getacl failed with %s\n",strerror(errno)));
+               talloc_free(acl);
                return NULL;
        }
 
@@ -253,7 +347,7 @@ static struct gpfs_acl *gpfs_getacl_alloc(const char *fname, gpfs_aclType_t type
  */
 static int gpfs_get_nfs4_acl(const char *fname, SMB4ACL_T **ppacl)
 {
-       int i;
+       gpfs_aclCount_t i;
        struct gpfs_acl *gacl = NULL;
        DEBUG(10, ("gpfs_get_nfs4_acl invoked for %s\n", fname));
 
@@ -268,6 +362,7 @@ static int gpfs_get_nfs4_acl(const char *fname, SMB4ACL_T **ppacl)
        if (gacl->acl_type != GPFS_ACL_TYPE_NFS4) {
                DEBUG(10, ("Got non-nfsv4 acl\n"));
                /* Retry with POSIX ACLs check */
+               talloc_free(gacl);
                return 1;
        }
 
@@ -333,20 +428,32 @@ static int gpfs_get_nfs4_acl(const char *fname, SMB4ACL_T **ppacl)
 
 static NTSTATUS gpfsacl_fget_nt_acl(vfs_handle_struct *handle,
        files_struct *fsp, uint32 security_info,
+       TALLOC_CTX *mem_ctx,
        struct security_descriptor **ppdesc)
 {
        SMB4ACL_T *pacl = NULL;
        int     result;
+       struct gpfs_config_data *config;
 
        *ppdesc = NULL;
+
+       SMB_VFS_HANDLE_GET_DATA(handle, config,
+                               struct gpfs_config_data,
+                               return NT_STATUS_INTERNAL_ERROR);
+
+       if (!config->acl) {
+               return SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
+                                               mem_ctx, ppdesc);
+       }
+
        result = gpfs_get_nfs4_acl(fsp->fsp_name->base_name, &pacl);
 
        if (result == 0)
-               return smb_fget_nt_acl_nfs4(fsp, security_info, ppdesc, pacl);
+               return smb_fget_nt_acl_nfs4(fsp, security_info, mem_ctx, ppdesc, pacl);
 
        if (result > 0) {
                DEBUG(10, ("retrying with posix acl...\n"));
-               return posix_fget_nt_acl(fsp, security_info, ppdesc);
+               return posix_fget_nt_acl(fsp, security_info, mem_ctx, ppdesc);
        }
 
        /* GPFS ACL was not read, something wrong happened, error code is set in errno */
@@ -355,20 +462,34 @@ static NTSTATUS gpfsacl_fget_nt_acl(vfs_handle_struct *handle,
 
 static NTSTATUS gpfsacl_get_nt_acl(vfs_handle_struct *handle,
        const char *name,
-       uint32 security_info, struct security_descriptor **ppdesc)
+       uint32 security_info,
+       TALLOC_CTX *mem_ctx, struct security_descriptor **ppdesc)
 {
        SMB4ACL_T *pacl = NULL;
        int     result;
+       struct gpfs_config_data *config;
 
        *ppdesc = NULL;
+
+       SMB_VFS_HANDLE_GET_DATA(handle, config,
+                               struct gpfs_config_data,
+                               return NT_STATUS_INTERNAL_ERROR);
+
+       if (!config->acl) {
+               return SMB_VFS_NEXT_GET_NT_ACL(handle, name, security_info,
+                                              mem_ctx, ppdesc);
+       }
+
        result = gpfs_get_nfs4_acl(name, &pacl);
 
        if (result == 0)
-               return smb_get_nt_acl_nfs4(handle->conn, name, security_info, ppdesc, pacl);
+               return smb_get_nt_acl_nfs4(handle->conn, name, security_info,
+                                          mem_ctx, ppdesc, pacl);
 
        if (result > 0) {
                DEBUG(10, ("retrying with posix acl...\n"));
-               return posix_get_nt_acl(handle->conn, name, security_info, ppdesc);
+               return posix_get_nt_acl(handle->conn, name, security_info,
+                                       mem_ctx, ppdesc);
        }
 
        /* GPFS ACL was not read, something wrong happened, error code is set in errno */
@@ -383,8 +504,8 @@ static bool gpfsacl_process_smbacl(files_struct *fsp, SMB4ACL_T *smbacl)
        struct gpfs_acl *gacl;
        TALLOC_CTX *mem_ctx  = talloc_tos();
 
-       gacl_len = sizeof(struct gpfs_acl) +
-               (smb_get_naces(smbacl)-1)*sizeof(gpfs_ace_v4_t);
+       gacl_len = offsetof(gpfs_acl_t, ace_v4) + smb_get_naces(smbacl) *
+               sizeof(gpfs_ace_v4_t);
 
        gacl = (struct gpfs_acl *)TALLOC_SIZE(mem_ctx, gacl_len);
        if (gacl == NULL) {
@@ -501,21 +622,38 @@ static NTSTATUS gpfsacl_set_nt_acl_internal(files_struct *fsp, uint32 security_i
 
 static NTSTATUS gpfsacl_fset_nt_acl(vfs_handle_struct *handle, files_struct *fsp, uint32 security_info_sent, const struct security_descriptor *psd)
 {
+       struct gpfs_config_data *config;
+
+       SMB_VFS_HANDLE_GET_DATA(handle, config,
+                               struct gpfs_config_data,
+                               return NT_STATUS_INTERNAL_ERROR);
+
+       if (!config->acl) {
+               return SMB_VFS_NEXT_FSET_NT_ACL(handle, fsp, security_info_sent, psd);
+       }
+
        return gpfsacl_set_nt_acl_internal(fsp, security_info_sent, psd);
 }
 
-static SMB_ACL_T gpfs2smb_acl(const struct gpfs_acl *pacl)
+static SMB_ACL_T gpfs2smb_acl(const struct gpfs_acl *pacl, TALLOC_CTX *mem_ctx)
 {
        SMB_ACL_T result;
-       int i;
+       gpfs_aclCount_t i;
 
-       result = sys_acl_init(pacl->acl_nace);
+       result = sys_acl_init(mem_ctx);
        if (result == NULL) {
                errno = ENOMEM;
                return NULL;
        }
 
        result->count = pacl->acl_nace;
+       result->acl = talloc_realloc(result, result->acl, struct smb_acl_entry,
+                                    result->count);
+       if (result->acl == NULL) {
+               TALLOC_FREE(result);
+               errno = ENOMEM;
+               return NULL;
+       }
 
        for (i=0; i<pacl->acl_nace; i++) {
                struct smb_acl_entry *ace = &result->acl[i];
@@ -528,14 +666,14 @@ static SMB_ACL_T gpfs2smb_acl(const struct gpfs_acl *pacl)
                switch (g_ace->ace_type) {
                case GPFS_ACL_USER:
                        ace->a_type = SMB_ACL_USER;
-                       ace->uid = (uid_t)g_ace->ace_who;
+                       ace->info.user.uid = (uid_t)g_ace->ace_who;
                        break;
                case GPFS_ACL_USER_OBJ:
                        ace->a_type = SMB_ACL_USER_OBJ;
                        break;
                case GPFS_ACL_GROUP:
                        ace->a_type = SMB_ACL_GROUP;
-                       ace->gid = (gid_t)g_ace->ace_who;
+                       ace->info.group.gid = (gid_t)g_ace->ace_who;
                        break;
                case GPFS_ACL_GROUP_OBJ:
                        ace->a_type = SMB_ACL_GROUP_OBJ;
@@ -549,8 +687,8 @@ static SMB_ACL_T gpfs2smb_acl(const struct gpfs_acl *pacl)
                default:
                        DEBUG(10, ("Got invalid ace_type: %d\n",
                                   g_ace->ace_type));
+                       TALLOC_FREE(result);
                        errno = EINVAL;
-                       SAFE_FREE(result);
                        return NULL;
                }
 
@@ -569,7 +707,8 @@ static SMB_ACL_T gpfs2smb_acl(const struct gpfs_acl *pacl)
        return result;
 }
 
-static SMB_ACL_T gpfsacl_get_posix_acl(const char *path, gpfs_aclType_t type)
+static SMB_ACL_T gpfsacl_get_posix_acl(const char *path, gpfs_aclType_t type,
+                                      TALLOC_CTX *mem_ctx)
 {
        struct gpfs_acl *pacl;
        SMB_ACL_T result = NULL;
@@ -596,24 +735,38 @@ static SMB_ACL_T gpfsacl_get_posix_acl(const char *path, gpfs_aclType_t type)
                   pacl->acl_len, pacl->acl_level, pacl->acl_version,
                   pacl->acl_nace));
 
-       result = gpfs2smb_acl(pacl);
-       if (result == NULL) {
-               goto done;
+       result = gpfs2smb_acl(pacl, mem_ctx);
+       if (result != NULL) {
+               errno = 0;
        }
 
  done:
 
+       if (pacl != NULL) {
+               talloc_free(pacl);
+       }
        if (errno != 0) {
-               SAFE_FREE(result);
+               TALLOC_FREE(result);
        }
        return result;  
 }
 
 static SMB_ACL_T gpfsacl_sys_acl_get_file(vfs_handle_struct *handle,
                                          const char *path_p,
-                                         SMB_ACL_TYPE_T type)
+                                         SMB_ACL_TYPE_T type,
+                                         TALLOC_CTX *mem_ctx)
 {
        gpfs_aclType_t gpfs_type;
+       struct gpfs_config_data *config;
+
+       SMB_VFS_HANDLE_GET_DATA(handle, config,
+                               struct gpfs_config_data,
+                               return NULL);
+
+       if (!config->acl) {
+               return SMB_VFS_NEXT_SYS_ACL_GET_FILE(handle, path_p,
+                                                    type, mem_ctx);
+       }
 
        switch(type) {
        case SMB_ACL_TYPE_ACCESS:
@@ -627,14 +780,164 @@ static SMB_ACL_T gpfsacl_sys_acl_get_file(vfs_handle_struct *handle,
                smb_panic("exiting");
        }
 
-       return gpfsacl_get_posix_acl(path_p, gpfs_type);
+       return gpfsacl_get_posix_acl(path_p, gpfs_type, mem_ctx);
 }
 
 static SMB_ACL_T gpfsacl_sys_acl_get_fd(vfs_handle_struct *handle,
-                                       files_struct *fsp)
+                                       files_struct *fsp,
+                                       TALLOC_CTX *mem_ctx)
 {
+       struct gpfs_config_data *config;
+
+       SMB_VFS_HANDLE_GET_DATA(handle, config,
+                               struct gpfs_config_data,
+                               return NULL);
+
+       if (!config->acl) {
+               return SMB_VFS_NEXT_SYS_ACL_GET_FD(handle, fsp, mem_ctx);
+       }
+
        return gpfsacl_get_posix_acl(fsp->fsp_name->base_name,
-                                    GPFS_ACL_TYPE_ACCESS);
+                                    GPFS_ACL_TYPE_ACCESS, mem_ctx);
+}
+
+static int gpfsacl_sys_acl_blob_get_file(vfs_handle_struct *handle,
+                                     const char *path_p,
+                                     TALLOC_CTX *mem_ctx,
+                                     char **blob_description,
+                                     DATA_BLOB *blob)
+{
+       struct gpfs_config_data *config;
+       struct gpfs_opaque_acl *acl = NULL;
+       DATA_BLOB aclblob;
+       int result;
+
+       SMB_VFS_HANDLE_GET_DATA(handle, config,
+                               struct gpfs_config_data,
+                               return -1);
+
+       if (!config->acl) {
+               return SMB_VFS_NEXT_SYS_ACL_BLOB_GET_FILE(handle, path_p,
+                                                         mem_ctx,
+                                                         blob_description,
+                                                         blob);
+       }
+
+       errno = 0;
+       acl = (struct gpfs_opaque_acl *)
+                       vfs_gpfs_getacl(mem_ctx,
+                                       path_p,
+                                       true,
+                                       GPFS_ACL_TYPE_NFS4);
+
+       if (errno) {
+               DEBUG(5, ("vfs_gpfs_getacl finished with errno %d: %s\n",
+                                       errno, strerror(errno)));
+
+               /* EINVAL means POSIX ACL, bail out on other cases */
+               if (errno != EINVAL) {
+                       return -1;
+               }
+       }
+
+       if (acl != NULL) {
+               /*
+                * file has NFSv4 ACL
+                *
+                * we only need the actual ACL blob here
+                * acl_version will always be NFS4 because we asked
+                * for NFS4
+                * acl_type is only used for POSIX ACLs
+                */
+               aclblob.data = (uint8_t*) acl->acl_var_data;
+               aclblob.length = acl->acl_buffer_len;
+
+               *blob_description = talloc_strdup(mem_ctx, "gpfs_nfs4_acl");
+               if (!*blob_description) {
+                       talloc_free(acl);
+                       errno = ENOMEM;
+                       return -1;
+               }
+
+               result = non_posix_sys_acl_blob_get_file_helper(handle, path_p,
+                                                               aclblob,
+                                                               mem_ctx, blob);
+
+               talloc_free(acl);
+               return result;
+       }
+
+       /* fall back to POSIX ACL */
+       return posix_sys_acl_blob_get_file(handle, path_p, mem_ctx,
+                                          blob_description, blob);
+}
+
+static int gpfsacl_sys_acl_blob_get_fd(vfs_handle_struct *handle,
+                                     files_struct *fsp,
+                                     TALLOC_CTX *mem_ctx,
+                                     char **blob_description,
+                                     DATA_BLOB *blob)
+{
+       struct gpfs_config_data *config;
+       struct gpfs_opaque_acl *acl = NULL;
+       DATA_BLOB aclblob;
+       int result;
+
+       SMB_VFS_HANDLE_GET_DATA(handle, config,
+                               struct gpfs_config_data,
+                               return -1);
+
+       if (!config->acl) {
+               return SMB_VFS_NEXT_SYS_ACL_BLOB_GET_FD(handle, fsp, mem_ctx,
+                                                       blob_description, blob);
+       }
+
+       errno = 0;
+       acl = (struct gpfs_opaque_acl *) vfs_gpfs_getacl(mem_ctx,
+                                               fsp->fsp_name->base_name,
+                                               true,
+                                               GPFS_ACL_TYPE_NFS4);
+
+       if (errno) {
+               DEBUG(5, ("vfs_gpfs_getacl finished with errno %d: %s\n",
+                                       errno, strerror(errno)));
+
+               /* EINVAL means POSIX ACL, bail out on other cases */
+               if (errno != EINVAL) {
+                       return -1;
+               }
+       }
+
+       if (acl != NULL) {
+               /*
+                * file has NFSv4 ACL
+                *
+                * we only need the actual ACL blob here
+                * acl_version will always be NFS4 because we asked
+                * for NFS4
+                * acl_type is only used for POSIX ACLs
+                */
+               aclblob.data = (uint8_t*) acl->acl_var_data;
+               aclblob.length = acl->acl_buffer_len;
+
+               *blob_description = talloc_strdup(mem_ctx, "gpfs_nfs4_acl");
+               if (!*blob_description) {
+                       talloc_free(acl);
+                       errno = ENOMEM;
+                       return -1;
+               }
+
+               result = non_posix_sys_acl_blob_get_fd_helper(handle, fsp,
+                                                             aclblob, mem_ctx,
+                                                             blob);
+
+               talloc_free(acl);
+               return result;
+       }
+
+       /* fall back to POSIX ACL */
+       return posix_sys_acl_blob_get_fd(handle, fsp, mem_ctx,
+                                        blob_description, blob);
 }
 
 static struct gpfs_acl *smb2gpfs_acl(const SMB_ACL_T pacl,
@@ -643,16 +946,11 @@ static struct gpfs_acl *smb2gpfs_acl(const SMB_ACL_T pacl,
        gpfs_aclLen_t len;
        struct gpfs_acl *result;
        int i;
-       union gpfs_ace_union
-       {
-               gpfs_ace_v1_t  ace_v1[1]; /* when GPFS_ACL_VERSION_POSIX */
-               gpfs_ace_v4_t  ace_v4[1]; /* when GPFS_ACL_VERSION_NFS4  */
-       };
 
        DEBUG(10, ("smb2gpfs_acl: Got ACL with %d entries\n", pacl->count));
 
-       len = sizeof(struct gpfs_acl) - sizeof(union gpfs_ace_union) +
-               (pacl->count)*sizeof(gpfs_ace_v1_t);
+       len = offsetof(gpfs_acl_t, ace_v1) + (pacl->count) *
+               sizeof(gpfs_ace_v1_t);
 
        result = (struct gpfs_acl *)SMB_MALLOC(len);
        if (result == NULL) {
@@ -679,7 +977,7 @@ static struct gpfs_acl *smb2gpfs_acl(const SMB_ACL_T pacl,
                switch(ace->a_type) {
                case SMB_ACL_USER:
                        g_ace->ace_type = GPFS_ACL_USER;
-                       g_ace->ace_who = (gpfs_uid_t)ace->uid;
+                       g_ace->ace_who = (gpfs_uid_t)ace->info.user.uid;
                        break;
                case SMB_ACL_USER_OBJ:
                        g_ace->ace_type = GPFS_ACL_USER_OBJ;
@@ -688,7 +986,7 @@ static struct gpfs_acl *smb2gpfs_acl(const SMB_ACL_T pacl,
                        break;
                case SMB_ACL_GROUP:
                        g_ace->ace_type = GPFS_ACL_GROUP;
-                       g_ace->ace_who = (gpfs_uid_t)ace->gid;
+                       g_ace->ace_who = (gpfs_uid_t)ace->info.group.gid;
                        break;
                case SMB_ACL_GROUP_OBJ:
                        g_ace->ace_type = GPFS_ACL_GROUP_OBJ;
@@ -731,6 +1029,15 @@ static int gpfsacl_sys_acl_set_file(vfs_handle_struct *handle,
 {
        struct gpfs_acl *gpfs_acl;
        int result;
+       struct gpfs_config_data *config;
+
+       SMB_VFS_HANDLE_GET_DATA(handle, config,
+                               struct gpfs_config_data,
+                               return -1);
+
+       if (!config->acl) {
+               return SMB_VFS_NEXT_SYS_ACL_SET_FILE(handle, name, type, theacl);
+       }
 
        gpfs_acl = smb2gpfs_acl(theacl, type);
        if (gpfs_acl == NULL) {
@@ -747,6 +1054,16 @@ static int gpfsacl_sys_acl_set_fd(vfs_handle_struct *handle,
                                  files_struct *fsp,
                                  SMB_ACL_T theacl)
 {
+       struct gpfs_config_data *config;
+
+       SMB_VFS_HANDLE_GET_DATA(handle, config,
+                               struct gpfs_config_data,
+                               return -1);
+
+       if (!config->acl) {
+               return SMB_VFS_NEXT_SYS_ACL_SET_FD(handle, fsp, theacl);
+       }
+
        return gpfsacl_sys_acl_set_file(handle, fsp->fsp_name->base_name,
                                        SMB_ACL_TYPE_ACCESS, theacl);
 }
@@ -754,6 +1071,16 @@ static int gpfsacl_sys_acl_set_fd(vfs_handle_struct *handle,
 static int gpfsacl_sys_acl_delete_def_file(vfs_handle_struct *handle,
                                           const char *path)
 {
+       struct gpfs_config_data *config;
+
+       SMB_VFS_HANDLE_GET_DATA(handle, config,
+                               struct gpfs_config_data,
+                               return -1);
+
+       if (!config->acl) {
+               return SMB_VFS_NEXT_SYS_ACL_DELETE_DEF_FILE(handle, path);
+       }
+
        errno = ENOTSUP;
        return -1;
 }
@@ -944,12 +1271,22 @@ static int gpfs_set_xattr(struct vfs_handle_struct *handle,  const char *path,
         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);
+
+       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);
+       }
 
         DEBUG(10, ("gpfs_set_xattr: %s \n",path));
 
         /* Only handle DOS Attributes */
         if (strcmp(name,SAMBA_XATTR_DOS_ATTRIB) != 0){
-               DEBUG(1, ("gpfs_set_xattr:name is %s\n",name));
+               DEBUG(5, ("gpfs_set_xattr:name is %s\n",name));
                return SMB_VFS_NEXT_SETXATTR(handle,path,name,value,size,flags);
         }
 
@@ -998,7 +1335,7 @@ static int gpfs_set_xattr(struct vfs_handle_struct *handle,  const char *path,
        }
 
 
-        ret = set_gpfs_winattrs(CONST_DISCARD(char *, path),
+        ret = set_gpfs_winattrs(discard_const_p(char, path),
                                GPFS_WINATTR_SET_ATTRS, &attrs);
         if ( ret == -1){
                if (errno == ENOSYS) {
@@ -1020,16 +1357,26 @@ static ssize_t gpfs_get_xattr(struct vfs_handle_struct *handle,  const char *pat
         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);
+
+       if (!config->winattr) {
+               DEBUG(10, ("gpfs_get_xattr:name is %s -> next\n",name));
+               return SMB_VFS_NEXT_GETXATTR(handle,path,name,value,size);
+       }
 
         DEBUG(10, ("gpfs_get_xattr: %s \n",path));
 
         /* Only handle DOS Attributes */
         if (strcmp(name,SAMBA_XATTR_DOS_ATTRIB) != 0){
-                DEBUG(1, ("gpfs_get_xattr:name is %s\n",name));
+               DEBUG(5, ("gpfs_get_xattr:name is %s\n",name));
                 return SMB_VFS_NEXT_GETXATTR(handle,path,name,value,size);
         }
 
-        ret = get_gpfs_winattrs(CONST_DISCARD(char *, path), &attrs);
+        ret = get_gpfs_winattrs(discard_const_p(char, path), &attrs);
         if ( ret == -1){
                if (errno == ENOSYS) {
                        return SMB_VFS_NEXT_GETXATTR(handle, path, name, value,
@@ -1073,19 +1420,30 @@ static int vfs_gpfs_stat(struct vfs_handle_struct *handle,
        char *fname = NULL;
        NTSTATUS status;
        int ret;
+       struct gpfs_config_data *config;
+
+       SMB_VFS_HANDLE_GET_DATA(handle, config,
+                               struct gpfs_config_data,
+                               return -1);
 
        ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
        if (ret == -1) {
                return -1;
        }
+
+       if (!config->winattr) {
+               return 0;
+       }
+
        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 = get_gpfs_winattrs(CONST_DISCARD(char *, fname), &attrs);
+       ret = get_gpfs_winattrs(discard_const_p(char, fname), &attrs);
        TALLOC_FREE(fname);
        if (ret == 0) {
+               smb_fname->st.st_ex_calculated_birthtime = false;
                smb_fname->st.st_ex_btime.tv_sec = attrs.creationTime.tv_sec;
                smb_fname->st.st_ex_btime.tv_nsec = attrs.creationTime.tv_nsec;
                smb_fname->st.vfs_private = attrs.winAttrs;
@@ -1098,6 +1456,11 @@ static int vfs_gpfs_fstat(struct vfs_handle_struct *handle,
 {
        struct gpfs_winattr attrs;
        int ret;
+       struct gpfs_config_data *config;
+
+       SMB_VFS_HANDLE_GET_DATA(handle, config,
+                               struct gpfs_config_data,
+                               return -1);
 
        ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
        if (ret == -1) {
@@ -1106,8 +1469,13 @@ static int vfs_gpfs_fstat(struct vfs_handle_struct *handle,
        if ((fsp->fh == NULL) || (fsp->fh->fd == -1)) {
                return 0;
        }
+       if (!config->winattr) {
+               return 0;
+       }
+
        ret = smbd_fget_gpfs_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;
        }
@@ -1121,19 +1489,29 @@ static int vfs_gpfs_lstat(struct vfs_handle_struct *handle,
        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);
 
        ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
        if (ret == -1) {
                return -1;
        }
+       if (!config->winattr) {
+               return 0;
+       }
+
        status = get_full_smb_filename(talloc_tos(), smb_fname, &path);
        if (!NT_STATUS_IS_OK(status)) {
                errno = map_errno_from_nt_status(status);
                return -1;
        }
-       ret = get_gpfs_winattrs(CONST_DISCARD(char *, path), &attrs);
+       ret = get_gpfs_winattrs(discard_const_p(char, path), &attrs);
        TALLOC_FREE(path);
        if (ret == 0) {
+               smb_fname->st.st_ex_calculated_birthtime = false;
                smb_fname->st.st_ex_btime.tv_sec = attrs.creationTime.tv_sec;
                smb_fname->st.st_ex_btime.tv_nsec = attrs.creationTime.tv_nsec;
                smb_fname->st.vfs_private = attrs.winAttrs;
@@ -1150,10 +1528,19 @@ static int vfs_gpfs_ntimes(struct vfs_handle_struct *handle,
         int ret;
         char *path = NULL;
         NTSTATUS status;
+       struct gpfs_config_data *config;
+
+       SMB_VFS_HANDLE_GET_DATA(handle, config,
+                               struct gpfs_config_data,
+                               return -1);
 
         ret = SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
         if(ret == -1){
-                DEBUG(1,("vfs_gpfs_ntimes: SMB_VFS_NEXT_NTIMES failed\n"));
+               /* 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)));
+               }
                 return -1;
         }
 
@@ -1162,6 +1549,10 @@ static int vfs_gpfs_ntimes(struct vfs_handle_struct *handle,
                 return 0;
         }
 
+       if (!config->winattr) {
+               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);
@@ -1172,7 +1563,7 @@ static int vfs_gpfs_ntimes(struct vfs_handle_struct *handle,
         attrs.creationTime.tv_sec = ft->create_time.tv_sec;
         attrs.creationTime.tv_nsec = ft->create_time.tv_nsec;
 
-        ret = set_gpfs_winattrs(CONST_DISCARD(char *, path),
+        ret = set_gpfs_winattrs(discard_const_p(char, path),
                                 GPFS_WINATTR_SET_CREATION_TIME, &attrs);
         if(ret == -1 && errno != ENOSYS){
                 DEBUG(1,("vfs_gpfs_ntimes: set GPFS ntimes failed %d\n",ret));
@@ -1182,10 +1573,55 @@ static int vfs_gpfs_ntimes(struct vfs_handle_struct *handle,
 
 }
 
+static int vfs_gpfs_fallocate(struct vfs_handle_struct *handle,
+                      struct files_struct *fsp, enum vfs_fallocate_mode 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 == VFS_FALLOCATE_KEEP_SIZE) {
+               DEBUG(10, ("Unsupported VFS_FALLOCATE_KEEP_SIZE\n"));
+               errno = ENOTSUP;
+               return -1;
+       }
+
+       ret = smbd_gpfs_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;
+}
+
 static int vfs_gpfs_ftruncate(vfs_handle_struct *handle, files_struct *fsp,
-                               SMB_OFF_T len)
+                               off_t len)
 {
        int result;
+       struct gpfs_config_data *config;
+
+       SMB_VFS_HANDLE_GET_DATA(handle, config,
+                               struct gpfs_config_data,
+                               return -1);
+
+       if (!config->ftruncate) {
+               return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, len);
+       }
 
        result = smbd_gpfs_ftruncate(fsp->fh->fd, len);
        if ((result == -1) && (errno == ENOSYS)) {
@@ -1201,6 +1637,15 @@ static bool vfs_gpfs_is_offline(struct vfs_handle_struct *handle,
        struct gpfs_winattr attrs;
        char *path = NULL;
        NTSTATUS status;
+       struct gpfs_config_data *config;
+
+       SMB_VFS_HANDLE_GET_DATA(handle, config,
+                               struct gpfs_config_data,
+                               return -1);
+
+       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)) {
@@ -1235,14 +1680,26 @@ static bool vfs_gpfs_aio_force(struct vfs_handle_struct *handle,
        return vfs_gpfs_is_offline(handle, fsp->fsp_name, &fsp->fsp_name->st);
 }
 
-int vfs_gpfs_connect(struct vfs_handle_struct *handle, const char *service,
-                       const char *user)
+static ssize_t vfs_gpfs_sendfile(vfs_handle_struct *handle, int tofd,
+                                files_struct *fsp, const DATA_BLOB *hdr,
+                                off_t offset, size_t n)
+{
+       if ((fsp->fsp_name->st.vfs_private & GPFS_WINATTR_OFFLINE) != 0) {
+               errno = ENOSYS;
+               return -1;
+       }
+       return SMB_VFS_NEXT_SENDFILE(handle, tofd, fsp, hdr, offset, n);
+}
+
+static int vfs_gpfs_connect(struct vfs_handle_struct *handle,
+                           const char *service, const char *user)
 {
        struct gpfs_config_data *config;
+       int ret;
 
        smbd_gpfs_lib_init();
 
-       int ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
+       ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
 
        if (ret < 0) {
                return ret;
@@ -1251,7 +1708,8 @@ int vfs_gpfs_connect(struct vfs_handle_struct *handle, const char *service,
        config = talloc_zero(handle->conn, struct gpfs_config_data);
        if (!config) {
                SMB_VFS_NEXT_DISCONNECT(handle);
-               DEBUG(0, ("talloc_zero() failed\n")); return -1;
+               DEBUG(0, ("talloc_zero() failed\n"));
+               return -1;
        }
 
        config->sharemodes = lp_parm_bool(SNUM(handle->conn), "gpfs",
@@ -1263,13 +1721,202 @@ int vfs_gpfs_connect(struct vfs_handle_struct *handle, const char *service,
        config->hsm = lp_parm_bool(SNUM(handle->conn), "gpfs",
                                   "hsm", false);
 
+       config->syncio = lp_parm_bool(SNUM(handle->conn), "gpfs",
+                                     "syncio", false);
+
+       config->winattr = lp_parm_bool(SNUM(handle->conn), "gpfs",
+                                      "winattr", false);
+
+       config->ftruncate = lp_parm_bool(SNUM(handle->conn), "gpfs",
+                                        "ftruncate", true);
+
+       config->getrealfilename = lp_parm_bool(SNUM(handle->conn), "gpfs",
+                                              "getrealfilename", true);
+
+       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);
+
        SMB_VFS_HANDLE_SET_DATA(handle, config,
                                NULL, struct gpfs_config_data,
                                return -1);
 
+       if (config->leases) {
+               /*
+                * GPFS lease code is based on kernel oplock code
+                * so make sure it is turned on
+                */
+               if (!lp_kernel_oplocks(SNUM(handle->conn))) {
+                       DEBUG(5, ("Enabling kernel oplocks for "
+                                 "gpfs:leases to work\n"));
+                       lp_do_parameter(SNUM(handle->conn), "kernel oplocks",
+                                       "true");
+               }
+
+               /*
+                * as the kernel does not properly support Level II oplocks
+                * and GPFS leases code is based on kernel infrastructure, we
+                * need to turn off Level II oplocks if gpfs:leases is enabled
+                */
+               if (lp_level2_oplocks(SNUM(handle->conn))) {
+                       DEBUG(5, ("gpfs:leases are enabled, disabling "
+                                 "Level II oplocks\n"));
+                       lp_do_parameter(SNUM(handle->conn), "level2 oplocks",
+                                       "false");
+               }
+       }
+
        return 0;
 }
 
+static int 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)
+{
+       uint64_t usage, limit;
+
+       /*
+        * The quota reporting is done in units of 1024 byte blocks, but
+        * sys_fsusage uses units of 512 byte blocks, adjust the block number
+        * accordingly. Also filter possibly negative usage counts from gpfs.
+        */
+       usage = qi.blockUsage < 0 ? 0 : (uint64_t)qi.blockUsage * 2;
+       limit = (uint64_t)qi.blockHardLimit * 2;
+
+       /*
+        * When the grace time for the exceeded soft block quota has been
+        * exceeded, the soft block quota becomes an additional hard limit.
+        */
+       if (qi.blockSoftLimit &&
+           qi.blockGraceTime && cur_time > qi.blockGraceTime) {
+               /* report disk as full */
+               *dfree = 0;
+               *dsize = MIN(*dsize, usage);
+       }
+
+       if (!qi.blockHardLimit)
+               return;
+
+       if (usage >= limit) {
+               /* report disk as full */
+               *dfree = 0;
+               *dsize = MIN(*dsize, usage);
+
+       } else {
+               /* limit has not been reached, determine "free space" */
+               *dfree = MIN(*dfree, limit - usage);
+               *dsize = MIN(*dsize, limit);
+       }
+}
+
+static uint64_t vfs_gpfs_disk_free(vfs_handle_struct *handle, const char *path,
+                                  bool small_query, 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_config_data *config;
+       int err, fset_id;
+       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, small_query,
+                                             bsize, dfree, dsize);
+       }
+
+       err = sys_fsusage(path, dfree, dsize);
+       if (err) {
+               DEBUG (0, ("Could not get fs usage, errno %d\n", errno));
+               return SMB_VFS_NEXT_DISK_FREE(handle, path, small_query,
+                                             bsize, dfree, dsize);
+       }
+
+       /* sys_fsusage returns units of 512 bytes */
+       *bsize = 512;
+
+       DEBUG(10, ("fs dfree %llu, dsize %llu\n",
+                  (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);
+       if (err) {
+               return SMB_VFS_NEXT_DISK_FREE(handle, path, small_query,
+                                             bsize, dfree, dsize);
+       }
+
+       cur_time = time(NULL);
+
+       /* Adjust free space and size according to quota limits. */
+       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);
+       }
+
+       disk_norm(small_query, bsize, dfree, dsize);
+       return *dfree;
+}
+
 static uint32_t vfs_gpfs_capabilities(struct vfs_handle_struct *handle,
                                      enum timestamp_set_resolution *p_ts_res)
 {
@@ -1288,33 +1935,244 @@ 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)
+{
+       struct gpfs_config_data *config;
+
+       SMB_VFS_HANDLE_GET_DATA(handle, config,
+                               struct gpfs_config_data,
+                               return -1);
+
+       if (config->syncio) {
+               flags |= O_SYNC;
+       }
+       return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
+}
+
+static ssize_t vfs_gpfs_pread(vfs_handle_struct *handle, files_struct *fsp,
+                             void *data, size_t n, off_t offset)
+{
+       ssize_t ret;
+
+       ret = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
+
+       DEBUG(10, ("vfs_private = %x\n",
+                  (unsigned int)fsp->fsp_name->st.vfs_private));
+
+       if ((ret != -1) &&
+           ((fsp->fsp_name->st.vfs_private & GPFS_WINATTR_OFFLINE) != 0)) {
+               fsp->fsp_name->st.vfs_private &= ~GPFS_WINATTR_OFFLINE;
+               notify_fname(handle->conn, NOTIFY_ACTION_MODIFIED,
+                            FILE_NOTIFY_CHANGE_ATTRIBUTES,
+                            fsp->fsp_name->base_name);
+       }
+
+       return ret;
+}
+
+struct vfs_gpfs_pread_state {
+       struct files_struct *fsp;
+       ssize_t ret;
+       int err;
+};
+
+static void vfs_gpfs_pread_done(struct tevent_req *subreq);
+
+static struct tevent_req *vfs_gpfs_pread_send(struct vfs_handle_struct *handle,
+                                             TALLOC_CTX *mem_ctx,
+                                             struct tevent_context *ev,
+                                             struct files_struct *fsp,
+                                             void *data, size_t n,
+                                             off_t offset)
+{
+       struct tevent_req *req, *subreq;
+       struct vfs_gpfs_pread_state *state;
+
+       req = tevent_req_create(mem_ctx, &state, struct vfs_gpfs_pread_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       state->fsp = fsp;
+       subreq = SMB_VFS_NEXT_PREAD_SEND(state, ev, handle, fsp, data,
+                                        n, offset);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, vfs_gpfs_pread_done, req);
+       return req;
+}
+
+static void vfs_gpfs_pread_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct vfs_gpfs_pread_state *state = tevent_req_data(
+               req, struct vfs_gpfs_pread_state);
+
+       state->ret = SMB_VFS_PREAD_RECV(subreq, &state->err);
+       TALLOC_FREE(subreq);
+       tevent_req_done(req);
+}
+
+static ssize_t vfs_gpfs_pread_recv(struct tevent_req *req, int *err)
+{
+       struct vfs_gpfs_pread_state *state = tevent_req_data(
+               req, struct vfs_gpfs_pread_state);
+       struct files_struct *fsp = state->fsp;
+
+       if (tevent_req_is_unix_error(req, err)) {
+               return -1;
+       }
+       *err = state->err;
+
+       DEBUG(10, ("vfs_private = %x\n",
+                  (unsigned int)fsp->fsp_name->st.vfs_private));
+
+       if ((state->ret != -1) &&
+           ((fsp->fsp_name->st.vfs_private & GPFS_WINATTR_OFFLINE) != 0)) {
+               fsp->fsp_name->st.vfs_private &= ~GPFS_WINATTR_OFFLINE;
+               DEBUG(10, ("sending notify\n"));
+               notify_fname(fsp->conn, NOTIFY_ACTION_MODIFIED,
+                            FILE_NOTIFY_CHANGE_ATTRIBUTES,
+                            fsp->fsp_name->base_name);
+       }
+
+       return state->ret;
+}
+
+static ssize_t vfs_gpfs_pwrite(vfs_handle_struct *handle, files_struct *fsp,
+                              const void *data, size_t n, off_t offset)
+{
+       ssize_t ret;
+
+       ret = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
+
+       DEBUG(10, ("vfs_private = %x\n",
+                  (unsigned int)fsp->fsp_name->st.vfs_private));
+
+       if ((ret != -1) &&
+           ((fsp->fsp_name->st.vfs_private & GPFS_WINATTR_OFFLINE) != 0)) {
+               fsp->fsp_name->st.vfs_private &= ~GPFS_WINATTR_OFFLINE;
+               notify_fname(handle->conn, NOTIFY_ACTION_MODIFIED,
+                            FILE_NOTIFY_CHANGE_ATTRIBUTES,
+                            fsp->fsp_name->base_name);
+       }
+
+       return ret;
+}
+
+struct vfs_gpfs_pwrite_state {
+       struct files_struct *fsp;
+       ssize_t ret;
+       int err;
+};
+
+static void vfs_gpfs_pwrite_done(struct tevent_req *subreq);
+
+static struct tevent_req *vfs_gpfs_pwrite_send(
+       struct vfs_handle_struct *handle,
+       TALLOC_CTX *mem_ctx,
+       struct tevent_context *ev,
+       struct files_struct *fsp,
+       const void *data, size_t n,
+       off_t offset)
+{
+       struct tevent_req *req, *subreq;
+       struct vfs_gpfs_pwrite_state *state;
+
+       req = tevent_req_create(mem_ctx, &state, struct vfs_gpfs_pwrite_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       state->fsp = fsp;
+       subreq = SMB_VFS_NEXT_PWRITE_SEND(state, ev, handle, fsp, data,
+                                        n, offset);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, vfs_gpfs_pwrite_done, req);
+       return req;
+}
+
+static void vfs_gpfs_pwrite_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct vfs_gpfs_pwrite_state *state = tevent_req_data(
+               req, struct vfs_gpfs_pwrite_state);
+
+       state->ret = SMB_VFS_PWRITE_RECV(subreq, &state->err);
+       TALLOC_FREE(subreq);
+       tevent_req_done(req);
+}
+
+static ssize_t vfs_gpfs_pwrite_recv(struct tevent_req *req, int *err)
+{
+       struct vfs_gpfs_pwrite_state *state = tevent_req_data(
+               req, struct vfs_gpfs_pwrite_state);
+       struct files_struct *fsp = state->fsp;
+
+       if (tevent_req_is_unix_error(req, err)) {
+               return -1;
+       }
+       *err = state->err;
+
+       DEBUG(10, ("vfs_private = %x\n",
+                  (unsigned int)fsp->fsp_name->st.vfs_private));
+
+       if ((state->ret != -1) &&
+           ((fsp->fsp_name->st.vfs_private & GPFS_WINATTR_OFFLINE) != 0)) {
+               fsp->fsp_name->st.vfs_private &= ~GPFS_WINATTR_OFFLINE;
+               DEBUG(10, ("sending notify\n"));
+               notify_fname(fsp->conn, NOTIFY_ACTION_MODIFIED,
+                            FILE_NOTIFY_CHANGE_ATTRIBUTES,
+                            fsp->fsp_name->base_name);
+       }
+
+       return state->ret;
+}
+
 
 static struct vfs_fn_pointers vfs_gpfs_fns = {
        .connect_fn = vfs_gpfs_connect,
-       .fs_capabilities = vfs_gpfs_capabilities,
-       .kernel_flock = vfs_gpfs_kernel_flock,
-        .linux_setlease = vfs_gpfs_setlease,
-        .get_real_filename = vfs_gpfs_get_real_filename,
-        .fget_nt_acl = gpfsacl_fget_nt_acl,
-        .get_nt_acl = gpfsacl_get_nt_acl,
-        .fset_nt_acl = gpfsacl_fset_nt_acl,
-        .sys_acl_get_file = gpfsacl_sys_acl_get_file,
-        .sys_acl_get_fd = gpfsacl_sys_acl_get_fd,
-        .sys_acl_set_file = gpfsacl_sys_acl_set_file,
-        .sys_acl_set_fd = gpfsacl_sys_acl_set_fd,
-        .sys_acl_delete_def_file = gpfsacl_sys_acl_delete_def_file,
-        .chmod = vfs_gpfs_chmod,
-        .fchmod = vfs_gpfs_fchmod,
-        .close_fn = vfs_gpfs_close,
-        .setxattr = gpfs_set_xattr,
-        .getxattr = gpfs_get_xattr,
-        .stat = vfs_gpfs_stat,
-        .fstat = vfs_gpfs_fstat,
-        .lstat = vfs_gpfs_lstat,
-       .ntimes = vfs_gpfs_ntimes,
-       .is_offline = vfs_gpfs_is_offline,
-       .aio_force = vfs_gpfs_aio_force,
-       .ftruncate = vfs_gpfs_ftruncate
+       .disk_free_fn = vfs_gpfs_disk_free,
+       .fs_capabilities_fn = vfs_gpfs_capabilities,
+       .kernel_flock_fn = vfs_gpfs_kernel_flock,
+       .linux_setlease_fn = vfs_gpfs_setlease,
+       .get_real_filename_fn = vfs_gpfs_get_real_filename,
+       .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,
+       .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,
+       .aio_force_fn = vfs_gpfs_aio_force,
+       .sendfile_fn = vfs_gpfs_sendfile,
+       .fallocate_fn = vfs_gpfs_fallocate,
+       .open_fn = vfs_gpfs_open,
+       .pread_fn = vfs_gpfs_pread,
+       .pread_send_fn = vfs_gpfs_pread_send,
+       .pread_recv_fn = vfs_gpfs_pread_recv,
+       .pwrite_fn = vfs_gpfs_pwrite,
+       .pwrite_send_fn = vfs_gpfs_pwrite_send,
+       .pwrite_recv_fn = vfs_gpfs_pwrite_recv,
+       .ftruncate_fn = vfs_gpfs_ftruncate
 };
 
 NTSTATUS vfs_gpfs_init(void);