From 62616784ae5460bfd8f68ccfe596f80f55727b87 Mon Sep 17 00:00:00 2001 From: Christof Schmitt Date: Fri, 2 Mar 2012 14:26:24 -0700 Subject: [PATCH] s3:vfs_gpfs: Report disk space and usage on GPFS share according to quotas When a client requests the information about free space and space used, adjust the reported values according to quotas in the GPFS file system: - Retrieve quotas for the current user, current group and fileset for the top level of the share. - If the soft block quota grace time has expired, report disk as full. - If a hard block quota has been exceeded, report disk as full. - If none of the hard block quotas been exceeded, report share size and free space according to the lowest limits found in the quotas. - If no applicable hard block quota has been set, report the information from the statfs call. This feature is disabled by default and has to be enabled by setting the option gpfs:dfreequota. --- docs-xml/manpages-3/vfs_gpfs.8.xml | 47 ++++++++++++++ source3/modules/vfs_gpfs.c | 129 +++++++++++++++++++++++++++++++++++++ 2 files changed, 176 insertions(+) diff --git a/docs-xml/manpages-3/vfs_gpfs.8.xml b/docs-xml/manpages-3/vfs_gpfs.8.xml index 2107b74a6cf..398dcff2556 100644 --- a/docs-xml/manpages-3/vfs_gpfs.8.xml +++ b/docs-xml/manpages-3/vfs_gpfs.8.xml @@ -221,6 +221,53 @@ + gpfs:dfreequota = [ yes | no ] + + + Adjust reporting of the size and free space of a share + according to quotas. If this setting is "yes", a + request for size and free space will also evaluate the + following quotas: + + + + The user quota of the user requesting + the data. + The group quota of the primary group + of the user. + The fileset quota for the fileset + containing the top level directory of the share. + + + + + If any of the soft or hard quota limits has been + reached, the free space will be reported as 0. If a + quota is in place, but the limits have not been + reached, the free space will be reported according to + the space left in the quota. If more than one quota + applies the free space will be reported as the smallest + space left in those quotas. The size of the share + will be reported according to the quota usage. If more + than one quota applies, the smallest size will be + reported for the share size according to these quotas. + + + + + yes - include the quotas + when reporting the share size and free space + + + no(default) - do not include quotas, + simply report the size and free space of the file system + + + + + + + nfs4:mode = [ simple | special ] diff --git a/source3/modules/vfs_gpfs.c b/source3/modules/vfs_gpfs.c index 6a9d3d5b846..95979992324 100644 --- a/source3/modules/vfs_gpfs.c +++ b/source3/modules/vfs_gpfs.c @@ -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,7 @@ struct gpfs_config_data { bool winattr; bool ftruncate; bool getrealfilename; + bool dfreequota; }; @@ -1385,6 +1387,9 @@ 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); + SMB_VFS_HANDLE_SET_DATA(handle, config, NULL, struct gpfs_config_data, return -1); @@ -1392,6 +1397,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) { @@ -1429,6 +1557,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, -- 2.11.4.GIT