s3-vfs_gpfs: Fix bug #9003, posix acl on gpfs
[ddiss/samba.git] / source3 / modules / vfs_gpfs.c
index 8400c50d2e401a357ef9850bd00473685f4c7fc7..a165cfadd84207245303d27fc73d096cd5c87fcc 100644 (file)
@@ -33,6 +33,7 @@
 #include "nfs4_acls.h"
 #include "vfs_gpfs.h"
 #include "system/filesys.h"
+#include "auth.h"
 
 struct gpfs_config_data {
        bool sharemodes;
@@ -42,6 +43,9 @@ struct gpfs_config_data {
        bool winattr;
        bool ftruncate;
        bool getrealfilename;
+       bool dfreequota;
+       bool prealloc;
+       bool acl;
 };
 
 
@@ -50,6 +54,7 @@ static int vfs_gpfs_kernel_flock(vfs_handle_struct *handle, files_struct *fsp,
 {
 
        struct gpfs_config_data *config;
+       int ret = 0;
 
        SMB_VFS_HANDLE_GET_DATA(handle, config,
                                struct gpfs_config_data,
@@ -61,12 +66,12 @@ static int vfs_gpfs_kernel_flock(vfs_handle_struct *handle, files_struct *fsp,
 
        if (config->sharemodes
                && !set_gpfs_sharemode(fsp, access_mask, fsp->share_access)) {
-               return -1;
+               ret = -1;
        }
 
        END_PROFILE(syscall_kernel_flock);
 
-       return 0;
+       return ret;
 }
 
 static int vfs_gpfs_close(vfs_handle_struct *handle, files_struct *fsp)
@@ -95,21 +100,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;
@@ -234,7 +233,7 @@ 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));
@@ -249,7 +248,7 @@ static struct gpfs_acl *gpfs_getacl_alloc(const char *fname, gpfs_aclType_t type
                new_acl->acl_type = acl->acl_type;
                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)
        {
@@ -352,8 +351,18 @@ static NTSTATUS gpfsacl_fget_nt_acl(vfs_handle_struct *handle,
 {
        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, ppdesc);
+       }
+
        result = gpfs_get_nfs4_acl(fsp->fsp_name->base_name, &pacl);
 
        if (result == 0)
@@ -374,8 +383,18 @@ static NTSTATUS gpfsacl_get_nt_acl(vfs_handle_struct *handle,
 {
        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, ppdesc);
+       }
+
        result = gpfs_get_nfs4_acl(name, &pacl);
 
        if (result == 0)
@@ -516,6 +535,16 @@ 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);
 }
 
@@ -612,8 +641,8 @@ static SMB_ACL_T gpfsacl_get_posix_acl(const char *path, gpfs_aclType_t type)
                   pacl->acl_nace));
 
        result = gpfs2smb_acl(pacl);
-       if (result == NULL) {
-               goto done;
+       if (result != NULL) {
+               errno = 0;
        }
 
  done:
@@ -629,6 +658,15 @@ static SMB_ACL_T gpfsacl_sys_acl_get_file(vfs_handle_struct *handle,
                                          SMB_ACL_TYPE_T type)
 {
        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);
+       }
 
        switch(type) {
        case SMB_ACL_TYPE_ACCESS:
@@ -648,6 +686,16 @@ static SMB_ACL_T gpfsacl_sys_acl_get_file(vfs_handle_struct *handle,
 static SMB_ACL_T gpfsacl_sys_acl_get_fd(vfs_handle_struct *handle,
                                        files_struct *fsp)
 {
+       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);
+       }
+
        return gpfsacl_get_posix_acl(fsp->fsp_name->base_name,
                                     GPFS_ACL_TYPE_ACCESS);
 }
@@ -746,6 +794,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) {
@@ -762,6 +819,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);
 }
@@ -769,6 +836,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;
 }
@@ -1131,6 +1208,7 @@ static int vfs_gpfs_stat(struct vfs_handle_struct *handle,
        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;
@@ -1162,6 +1240,7 @@ static int vfs_gpfs_fstat(struct vfs_handle_struct *handle,
 
        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;
        }
@@ -1197,6 +1276,7 @@ static int vfs_gpfs_lstat(struct vfs_handle_struct *handle,
        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;
@@ -1221,7 +1301,11 @@ static int vfs_gpfs_ntimes(struct vfs_handle_struct *handle,
 
         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;
         }
 
@@ -1254,8 +1338,44 @@ 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;
@@ -1327,7 +1447,7 @@ static bool vfs_gpfs_aio_force(struct vfs_handle_struct *handle,
 
 static ssize_t vfs_gpfs_sendfile(vfs_handle_struct *handle, int tofd,
                                 files_struct *fsp, const DATA_BLOB *hdr,
-                                SMB_OFF_T offset, size_t n)
+                                off_t offset, size_t n)
 {
        if ((fsp->fsp_name->st.vfs_private & GPFS_WINATTR_OFFLINE) != 0) {
                errno = ENOSYS;
@@ -1336,14 +1456,15 @@ static ssize_t vfs_gpfs_sendfile(vfs_handle_struct *handle, int tofd,
        return SMB_VFS_NEXT_SENDFILE(handle, tofd, fsp, hdr, offset, n);
 }
 
-int vfs_gpfs_connect(struct vfs_handle_struct *handle, const char *service,
-                       const char *user)
+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;
@@ -1377,6 +1498,14 @@ int vfs_gpfs_connect(struct vfs_handle_struct *handle, const char *service,
        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);
@@ -1384,6 +1513,129 @@ int vfs_gpfs_connect(struct vfs_handle_struct *handle, const char *service,
        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;
+
+       err = get_gpfs_fset_id(path, fset_id);
+       if (err) {
+               DEBUG(0, ("Get fset id failed, errno %d.\n", 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.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)
 {
@@ -1421,6 +1673,7 @@ static int vfs_gpfs_open(struct vfs_handle_struct *handle,
 
 static struct vfs_fn_pointers vfs_gpfs_fns = {
        .connect_fn = vfs_gpfs_connect,
+       .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,
@@ -1445,6 +1698,7 @@ static struct vfs_fn_pointers vfs_gpfs_fns = {
        .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,
        .ftruncate_fn = vfs_gpfs_ftruncate
 };