diff options
| author | Vikas Gorur <vikas@zresearch.com> | 2009-02-18 17:36:07 +0530 | 
|---|---|---|
| committer | Vikas Gorur <vikas@zresearch.com> | 2009-02-18 17:36:07 +0530 | 
| commit | 77adf4cd648dce41f89469dd185deec6b6b53a0b (patch) | |
| tree | 02e155a5753b398ee572b45793f889b538efab6b /xlators/features/quota | |
| parent | f3b2e6580e5663292ee113c741343c8a43ee133f (diff) | |
Added all files
Diffstat (limited to 'xlators/features/quota')
| -rw-r--r-- | xlators/features/quota/Makefile.am | 3 | ||||
| -rw-r--r-- | xlators/features/quota/src/Makefile.am | 13 | ||||
| -rw-r--r-- | xlators/features/quota/src/quota.c | 1056 | 
3 files changed, 1072 insertions, 0 deletions
diff --git a/xlators/features/quota/Makefile.am b/xlators/features/quota/Makefile.am new file mode 100644 index 00000000000..d471a3f9243 --- /dev/null +++ b/xlators/features/quota/Makefile.am @@ -0,0 +1,3 @@ +SUBDIRS = src + +CLEANFILES =  diff --git a/xlators/features/quota/src/Makefile.am b/xlators/features/quota/src/Makefile.am new file mode 100644 index 00000000000..886d839643c --- /dev/null +++ b/xlators/features/quota/src/Makefile.am @@ -0,0 +1,13 @@ +xlator_LTLIBRARIES = quota.la +xlatordir = $(libdir)/glusterfs/$(PACKAGE_VERSION)/xlator/features + +quota_la_LDFLAGS = -module -avoidversion + +quota_la_SOURCES = quota.c +quota_la_LIBADD = $(top_builddir)/libglusterfs/src/libglusterfs.la  + +AM_CFLAGS = -fPIC -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE -Wall -D$(GF_HOST_OS) \ +	-I$(top_srcdir)/libglusterfs/src -shared -nostartfiles $(GF_CFLAGS) + +CLEANFILES =  + diff --git a/xlators/features/quota/src/quota.c b/xlators/features/quota/src/quota.c new file mode 100644 index 00000000000..c898899b5e3 --- /dev/null +++ b/xlators/features/quota/src/quota.c @@ -0,0 +1,1056 @@ +/* +  Copyright (c) 2008, 2009 Z RESEARCH, Inc. <http://www.zresearch.com> +  This file is part of GlusterFS. + +  GlusterFS is free software; you can redistribute it and/or modify +  it under the terms of the GNU General Public License as published +  by the Free Software Foundation; either version 3 of the License, +  or (at your option) any later version. + +  GlusterFS is distributed in the hope that it will be useful, but +  WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +  General Public License for more details. + +  You should have received a copy of the GNU General Public License +  along with this program.  If not, see +  <http://www.gnu.org/licenses/>. +*/ + +#ifndef _CONFIG_H +#define _CONFIG_H +#include "config.h" +#endif + +#include <sys/time.h> + +#include "xlator.h" +#include "defaults.h" +#include "common-utils.h" + +struct quota_local { +	struct stat    stbuf; +	inode_t       *inode; +	char          *path; +	fd_t          *fd; +	off_t          offset; +	int32_t        count; +	struct iovec  *vector; +	dict_t        *refs; +	loc_t          loc; +}; + + +struct quota_priv { +	char       only_first_time;          /* Used to make sure a call is done only one time */ +	gf_lock_t  lock;                     /* Used while updating variables */ + +	uint64_t   disk_usage_limit;         /* Used for Disk usage quota */ +	uint64_t   current_disk_usage;       /* Keep the current usage value */ + +	uint32_t   min_free_disk_limit;        /* user specified limit, in %*/ +	uint32_t   current_free_disk;          /* current free disk space available, in % */ +	uint32_t   refresh_interval;           /* interval in seconds */ +	uint32_t   min_disk_last_updated_time; /* used for interval calculation */	 +}; + + +int +quota_statvfs_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +		   int32_t op_ret, int32_t op_errno, struct statvfs *stbuf) +{ +	struct quota_priv *priv = this->private; +	 +	if (op_ret >= 0) { +		priv->current_free_disk = +			(stbuf->f_bavail * 100) / stbuf->f_blocks; +	} + +	STACK_DESTROY (frame->root); +	return 0; +} + + +static void +build_root_loc (xlator_t *this, loc_t *loc) +{ +	loc->path = "/"; +} + + +void +gf_quota_usage_subtract (xlator_t *this, size_t size) +{ +	struct quota_priv *priv = NULL; + +	priv = this->private; + +	LOCK (&priv->lock); +	{ +		if (priv->current_disk_usage < size) +			priv->current_disk_usage = 0; +		else +			priv->current_disk_usage -= size; +	} +	UNLOCK (&priv->lock); +} + + +void +gf_quota_usage_add (xlator_t *this, size_t size) +{ +	struct quota_priv *priv = this->private; + +	LOCK (&priv->lock); +	{ +		priv->current_disk_usage += size; +	} +	UNLOCK (&priv->lock); +} + + +void  +gf_quota_update_current_free_disk (xlator_t *this) +{ +	call_frame_t *frame = NULL; +	call_pool_t   *pool = NULL; +	loc_t          loc; + +	pool  = this->ctx->pool; +	frame = create_frame (this, pool); +   +	build_root_loc (this, &loc); + +	STACK_WIND (frame, quota_statvfs_cbk, +		    this->children->xlator, +		    this->children->xlator->fops->statfs, &loc); + +	return ; +} + + +int +gf_quota_check_free_disk (xlator_t *this)  +{ +        struct quota_priv * priv = NULL; +	struct timeval tv = {0, 0}; + +	priv = this->private; +	if (priv->min_free_disk_limit) { +		gettimeofday (&tv, NULL); +		if (tv.tv_sec > (priv->refresh_interval +  +				 priv->min_disk_last_updated_time)) { +			priv->min_disk_last_updated_time = tv.tv_sec; +			gf_quota_update_current_free_disk (this); +		} +		if (priv->current_free_disk <= priv->min_free_disk_limit) +			return -1; +	} + +	return 0; +} + + +int +quota_truncate_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +		    int32_t op_ret, int32_t op_errno, struct stat *buf) +{ +	struct quota_priv *priv = this->private; +	struct quota_local *local = NULL; + +	local = frame->local; + +	if ((op_ret >= 0) && priv->disk_usage_limit) { +		gf_quota_usage_subtract (this, (local->stbuf.st_blocks - +						buf->st_blocks) * 512); +		loc_wipe (&local->loc); +	} + +	STACK_UNWIND (frame, op_ret, op_errno, buf); +	return 0; +} + + +int +quota_truncate_stat_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +			 int32_t op_ret, int32_t op_errno, struct stat *buf) +{ +	struct quota_local *local = NULL; +	struct quota_priv  *priv = NULL; + +	priv = this->private; +	local = frame->local; + +	if (op_ret >= 0) { +		local->stbuf = *buf; +	} + +	STACK_WIND (frame, quota_truncate_cbk, +		    FIRST_CHILD (this), FIRST_CHILD (this)->fops->truncate, +		    &local->loc, local->offset); +	return 0; +} + + +int +quota_truncate (call_frame_t *frame, xlator_t *this, +		loc_t *loc, off_t offset) +{ +	struct quota_local *local = NULL; +	struct quota_priv  *priv = NULL; + +	priv = this->private; + +	if (priv->disk_usage_limit) { +		local = CALLOC (1, sizeof (struct quota_local)); +		frame->local  = local; + +		loc_copy (&local->loc, loc); +		local->offset = offset; + +		STACK_WIND (frame, quota_truncate_stat_cbk, +			    FIRST_CHILD(this), +			    FIRST_CHILD(this)->fops->stat, loc); +		return 0; +	} + +	STACK_WIND (frame, quota_truncate_cbk, +		    FIRST_CHILD(this), +		    FIRST_CHILD(this)->fops->truncate, +		    loc, offset); +	return 0; +} + + +int +quota_ftruncate_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +		     int32_t op_ret, int32_t op_errno, struct stat *buf) +{ +	struct quota_priv  *priv = NULL; +	struct quota_local *local = NULL; + +	local = frame->local; +	priv = this->private; + +	if ((op_ret >= 0) && priv->disk_usage_limit) { +		gf_quota_usage_subtract (this, (local->stbuf.st_blocks - +						buf->st_blocks) * 512); +		fd_unref (local->fd); +	} + +	STACK_UNWIND (frame, op_ret, op_errno, buf); +	return 0; +} + + +int +quota_ftruncate_fstat_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +			   int32_t op_ret, int32_t op_errno, struct stat *buf) +{ +	struct quota_local *local = NULL; +	struct quota_priv  *priv = NULL; + +	priv = this->private; +	local = frame->local; + +	if (op_ret >= 0) { +		local->stbuf = *buf; +	} + +	STACK_WIND (frame, quota_ftruncate_cbk, +		    FIRST_CHILD (this), FIRST_CHILD (this)->fops->ftruncate, +		    local->fd, local->offset); +	return 0; +} + + +int +quota_ftruncate (call_frame_t *frame, xlator_t *this, +		 fd_t *fd, off_t offset) +{ +	struct quota_local *local = NULL; +	struct quota_priv  *priv = NULL; + + +	priv = this->private; + +	if (priv->disk_usage_limit) { +		local = CALLOC (1, sizeof (struct quota_local)); +		frame->local  = local; + +		local->fd = fd_ref (fd); +		local->offset = offset; + +		STACK_WIND (frame, quota_ftruncate_fstat_cbk, +			    FIRST_CHILD(this), +			    FIRST_CHILD(this)->fops->fstat, fd); +		return 0; +	} + +	STACK_WIND (frame, quota_ftruncate_cbk, +		    FIRST_CHILD(this), +		    FIRST_CHILD(this)->fops->ftruncate, +		    fd, offset); +	return 0; +} + + +int +quota_mknod_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +		 int32_t op_ret, int32_t op_errno, +		 inode_t *inode, struct stat *buf) +{ +	struct quota_priv *priv = NULL; + +	priv = this->private; + +	if ((op_ret >= 0) && priv->disk_usage_limit) { +		gf_quota_usage_add (this, buf->st_blocks * 512); +	} + +	STACK_UNWIND (frame, op_ret, op_errno, inode, buf); +	return 0; +} + + +int +quota_mknod (call_frame_t *frame, xlator_t *this, +	     loc_t *loc, mode_t mode, dev_t rdev) +{ +	struct quota_priv *priv = NULL; + +	priv = this->private; + +	if (gf_quota_check_free_disk (this) == -1) { +		gf_log (this->name, GF_LOG_ERROR,  +			"min-free-disk limit (%u) crossed, current available is %u", +			priv->min_free_disk_limit, priv->current_free_disk); +		STACK_UNWIND (frame, -1, ENOSPC, NULL, NULL); +		return 0; +	} + +        if (priv->current_disk_usage > priv->disk_usage_limit) { +		gf_log (this->name, GF_LOG_ERROR,  +			"Disk usage limit (%"PRIu64") crossed, current usage is %"PRIu64"", +			priv->disk_usage_limit, priv->current_disk_usage); +		STACK_UNWIND (frame, -1, ENOSPC, NULL, NULL); +		return 0; +        } + +	STACK_WIND (frame, quota_mknod_cbk, +		    FIRST_CHILD(this), +		    FIRST_CHILD(this)->fops->mknod, +		    loc, mode, rdev); +	return 0; +} + + +int +quota_mkdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +		 int32_t op_ret, int32_t op_errno, inode_t *inode, +		 struct stat *buf) +{ +	struct quota_priv *priv = NULL; + +	priv = this->private; + +	if ((op_ret >= 0) && priv->disk_usage_limit) { +		gf_quota_usage_subtract (this, buf->st_blocks * 512); +	} + +	STACK_UNWIND (frame, op_ret, op_errno, inode, buf); +	return 0; +} + + +int +quota_mkdir (call_frame_t *frame, xlator_t *this, loc_t *loc, mode_t mode) +{ +	struct quota_priv *priv = NULL; + +	priv = this->private; + +	if (gf_quota_check_free_disk (this) == -1) { +		gf_log (this->name, GF_LOG_ERROR,  +			"min-free-disk limit (%u) crossed, current available is %u", +			priv->min_free_disk_limit, priv->current_free_disk); +		STACK_UNWIND (frame, -1, ENOSPC, NULL, NULL); +		return 0; +		 +	} + +        if (priv->current_disk_usage > priv->disk_usage_limit) { +		gf_log (this->name, GF_LOG_ERROR,  +			"Disk usage limit (%"PRIu64") crossed, current usage is %"PRIu64"", +			priv->disk_usage_limit, priv->current_disk_usage); +		STACK_UNWIND (frame, -1, ENOSPC, NULL, NULL); +		return 0; +        } + +	STACK_WIND (frame, quota_mkdir_cbk, +		    FIRST_CHILD(this), +		    FIRST_CHILD(this)->fops->mkdir, +		    loc, mode); + +	return 0; +} + + +int +quota_unlink_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +		  int32_t op_ret, int32_t op_errno) +{ +	struct quota_local *local = NULL; + +	local = frame->local; + +	if (local) { +		if (op_ret >= 0) { +			gf_quota_usage_subtract (this, +						 local->stbuf.st_blocks * 512); +		} +		loc_wipe (&local->loc); +	} + +	STACK_UNWIND (frame, op_ret, op_errno); +	return 0; +} + + +int +quota_unlink_stat_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +		       int32_t op_ret, int32_t op_errno, struct stat *buf) +{ +	struct quota_local *local = NULL; + +	local = frame->local; + +	if (op_ret >= 0) { +		if (buf->st_nlink == 1) { +			local->stbuf = *buf; +		} +	} + +	STACK_WIND (frame, quota_unlink_cbk, +		    FIRST_CHILD(this), +		    FIRST_CHILD(this)->fops->unlink, +		    &local->loc); + +	return 0; +} + + +int +quota_unlink (call_frame_t *frame, xlator_t *this, loc_t *loc) +{ +	struct quota_local *local = NULL; +	struct quota_priv  *priv = NULL; + +	priv = this->private; + +	if (priv->disk_usage_limit) { +		local = CALLOC (1, sizeof (struct quota_local)); +		frame->local = local; + +		loc_copy (&local->loc, loc); + +		STACK_WIND (frame, +			    quota_unlink_stat_cbk, +			    FIRST_CHILD(this), +			    FIRST_CHILD(this)->fops->stat, +			    loc); +		return 0; +	} + +	STACK_WIND (frame, quota_unlink_cbk, +		    FIRST_CHILD(this), +		    FIRST_CHILD(this)->fops->unlink, +		    loc); +	return 0; +} + + +int +quota_rmdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +		 int32_t op_ret, int32_t op_errno) +{ +	struct quota_local *local = NULL; + +	local = frame->local; + +	if (local) { +		if (op_ret >= 0) { +			gf_quota_usage_subtract (this, local->stbuf.st_blocks * 512); +		} +		loc_wipe (&local->loc); +	} + +	STACK_UNWIND (frame, op_ret, op_errno); +	return 0; +} + + +int +quota_rmdir_stat_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +		      int32_t op_ret, int32_t op_errno, struct stat *buf) +{ +	struct quota_local *local = NULL; + +	local = frame->local; + +	if (op_ret >= 0) { +		local->stbuf = *buf; +	} + +	STACK_WIND (frame, quota_rmdir_cbk, +		    FIRST_CHILD(this), +		    FIRST_CHILD(this)->fops->rmdir, +		    &local->loc); + +	return 0; +} + + +int +quota_rmdir (call_frame_t *frame, xlator_t *this, loc_t *loc) +{ +	struct quota_local *local = NULL; +	struct quota_priv  *priv = NULL; + +	priv = this->private; + +	if (priv->disk_usage_limit) { +		local = CALLOC (1, sizeof (struct quota_local)); +		frame->local = local; + +		loc_copy (&local->loc, loc); + +		STACK_WIND (frame, quota_rmdir_stat_cbk, +			    FIRST_CHILD(this), +			    FIRST_CHILD(this)->fops->stat, loc); +		return 0; +	} + +	STACK_WIND (frame, quota_rmdir_cbk, +		    FIRST_CHILD(this), +		    FIRST_CHILD(this)->fops->rmdir, +		    loc); +	return 0; +} + + +int +quota_symlink_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +		   int32_t op_ret, int32_t op_errno, inode_t *inode, +		   struct stat *buf) +{ +	struct quota_priv *priv = NULL; + +	priv = this->private; + +	if ((op_ret >= 0) && priv->disk_usage_limit) { +		gf_quota_usage_add (this, buf->st_blocks * 512); +	} + +	STACK_UNWIND (frame, op_ret, op_errno, inode, buf); +	return 0; +} + + +int +quota_symlink (call_frame_t *frame, xlator_t *this, +	       const char *linkpath, loc_t *loc) +{ +	struct quota_priv *priv = NULL; + +	priv = this->private; + +	if (gf_quota_check_free_disk (this) == -1) { +		gf_log (this->name, GF_LOG_ERROR,  +			"min-free-disk limit (%u) crossed, current available is %u", +			priv->min_free_disk_limit, priv->current_free_disk); +		STACK_UNWIND (frame, -1, ENOSPC, NULL, NULL); +		return 0; +		 +	} +        if (priv->current_disk_usage > priv->disk_usage_limit) { +		gf_log (this->name, GF_LOG_ERROR,  +			"Disk usage limit (%"PRIu64") crossed, current usage is %"PRIu64"", +			priv->disk_usage_limit, priv->current_disk_usage); +		STACK_UNWIND (frame, -1, ENOSPC, NULL, NULL); +		return 0; +        } + +	STACK_WIND (frame, quota_symlink_cbk, +		    FIRST_CHILD(this), +		    FIRST_CHILD(this)->fops->symlink, +		    linkpath, loc); +	return 0; +} + + +int +quota_create_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +		  int32_t op_ret, int32_t op_errno, +		  fd_t *fd, inode_t *inode, struct stat *buf) +{ +	struct quota_priv *priv = this->private; +	int                ret = 0; + +	if ((op_ret >= 0) && priv->disk_usage_limit) { +		gf_quota_usage_add (this, buf->st_blocks * 512); + +		ret = fd_ctx_set (fd, this, 1); +	} + +	STACK_UNWIND (frame, op_ret, op_errno, fd, inode, buf); +	return 0; +} + + +int +quota_create (call_frame_t *frame, xlator_t *this, +	      loc_t *loc, int32_t flags, mode_t mode, fd_t *fd) +{ +	struct quota_priv *priv = NULL; + +	priv = this->private; + +	if (gf_quota_check_free_disk (this) == -1) { +		gf_log (this->name, GF_LOG_ERROR,  +			"min-free-disk limit (%u) crossed, current available is %u", +			priv->min_free_disk_limit, priv->current_free_disk); +		STACK_UNWIND (frame, -1, ENOSPC, NULL, NULL, NULL); +		return 0; +		 +	} +        if (priv->current_disk_usage > priv->disk_usage_limit) { +		gf_log (this->name, GF_LOG_ERROR,  +			"Disk usage limit (%"PRIu64") crossed, current usage is %"PRIu64"", +			priv->disk_usage_limit, priv->current_disk_usage); +		STACK_UNWIND (frame, -1, ENOSPC, NULL, NULL, NULL); +		return 0; +        } + +	STACK_WIND (frame, quota_create_cbk, +		    FIRST_CHILD(this), +		    FIRST_CHILD(this)->fops->create, +		    loc, flags, mode, fd); +	return 0; +} + + +int +quota_open_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +		int32_t op_ret, int32_t op_errno, fd_t *fd) +{ +	int                ret = 0; + +	if (op_ret >= 0) +		ret = fd_ctx_set (fd, this, 1); + +	STACK_UNWIND (frame, op_ret, op_errno, fd); +	return 0; +} + + +int +quota_open (call_frame_t *frame, xlator_t *this, +	    loc_t *loc, int32_t flags, fd_t *fd) +{ +	STACK_WIND (frame, quota_open_cbk, +		    FIRST_CHILD(this), +		    FIRST_CHILD(this)->fops->open, +		    loc, flags, fd); +	return 0; +} + + +int +quota_writev_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +		  int32_t op_ret, int32_t op_errno, struct stat *stbuf) +{ +	struct quota_priv *priv = NULL; +	struct quota_local *local = NULL; + + +	priv = this->private; +	local = frame->local; + +	if (priv->disk_usage_limit) { +		if (op_ret >= 0) {  +			gf_quota_usage_add (this, (stbuf->st_blocks - +						   local->stbuf.st_blocks) * 512); +		} +		fd_unref (local->fd); +		dict_unref (local->refs); +	} + +	STACK_UNWIND (frame, op_ret, op_errno, stbuf); +	return 0; +} + + +int +quota_writev_fstat_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +			int32_t op_ret,	int32_t op_errno, struct stat *buf) +{ +	struct quota_local *local = NULL; +	struct quota_priv  *priv = NULL; +	int                 iovlen = 0; + + +	local = frame->local; +	priv = this->private; + +	if (op_ret >= 0) { +		if (priv->current_disk_usage > priv->disk_usage_limit) { +			iovlen = iov_length (local->vector, local->count); + +			if (iovlen > (buf->st_blksize - (buf->st_size % buf->st_blksize))) { +				fd_unref (local->fd); +				dict_unref (local->refs); +				STACK_UNWIND (frame, -1, ENOSPC, NULL); +				return 0; +			} +		} +		local->stbuf = *buf; +	} +	 +	STACK_WIND (frame, quota_writev_cbk, +		    FIRST_CHILD(this), +		    FIRST_CHILD(this)->fops->writev, +		    local->fd, local->vector, local->count, local->offset); + +	return 0; +} + + +int +quota_writev (call_frame_t *frame, xlator_t *this, fd_t *fd, +	      struct iovec *vector, int32_t count, off_t off) +{ +	struct quota_local *local = NULL; +	struct quota_priv  *priv = NULL; + +	priv = this->private; + +	if (gf_quota_check_free_disk (this) == -1) { +		gf_log (this->name, GF_LOG_ERROR,  +			"min-free-disk limit (%u) crossed, current available is %u", +			priv->min_free_disk_limit, priv->current_free_disk); +		STACK_UNWIND (frame, -1, ENOSPC, NULL); +		return 0; +	} + +	if (priv->disk_usage_limit) { +		local = CALLOC (1, sizeof (struct quota_local)); +		local->fd     = fd_ref (fd); +		local->refs   = dict_ref (frame->root->req_refs); +		local->vector = vector; +		local->count  = count; +		local->offset = off; +		frame->local  = local; + +		STACK_WIND (frame, quota_writev_fstat_cbk, +			    FIRST_CHILD(this), +			    FIRST_CHILD(this)->fops->fstat, fd); +		return 0; +	} + +	STACK_WIND (frame, quota_writev_cbk, +		    FIRST_CHILD(this), +		    FIRST_CHILD(this)->fops->writev, +		    fd, vector, count, off); +	return 0; +} + + +int +quota_removexattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +		       int32_t op_ret, int32_t op_errno) +{ +	if (op_ret == -1) { +		gf_log (this->name, GF_LOG_CRITICAL,  +			"failed to remove the disk-usage value: %s", +			strerror (op_errno)); +	}  +	 +	STACK_DESTROY (frame->root); +	return 0; +} + + +int +quota_setxattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +		    int32_t op_ret, int32_t op_errno) +{ +	if (op_ret == -1) { +		gf_log (this->name, GF_LOG_CRITICAL,  +			"failed to set the disk-usage value: %s", +			strerror (op_errno)); +	}  + +	STACK_DESTROY (frame->root); +	return 0; +} + + +int +quota_statfs_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +		  int32_t op_ret, int32_t op_errno, struct statvfs *statvfs) +{ +	struct quota_priv *priv = NULL; +	uint64_t           f_blocks = 0; +	int64_t            f_bfree = 0; +	uint64_t           f_bused = 0; + + +	priv = this->private; + +	if (op_ret != 0) +		goto unwind; + +	f_blocks = priv->disk_usage_limit / statvfs->f_frsize; +	f_bused = priv->current_disk_usage / statvfs->f_frsize; + +	if (f_blocks && (f_blocks < statvfs->f_blocks)) +		statvfs->f_blocks = f_blocks; + +	f_bfree = (statvfs->f_blocks - f_bused); + +	if (f_bfree >= 0) +		statvfs->f_bfree = statvfs->f_bavail = f_bfree; +	else +		statvfs->f_bfree = statvfs->f_bavail = 0; + +unwind: +	STACK_UNWIND (frame, op_ret, op_errno, statvfs); +	return 0; +} + + +int +quota_statfs (call_frame_t *frame, xlator_t *this, loc_t *loc) +{ +	STACK_WIND (frame, quota_statfs_cbk, +		    FIRST_CHILD (this), FIRST_CHILD (this)->fops->statfs, loc); + +	return 0; +} + + +int +quota_getxattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +		    int32_t op_ret, int32_t op_errno, dict_t *value) +{ +	data_t *data = NULL; +	struct quota_priv *priv = this->private; +	 +	if (op_ret >= 0) { +		data = dict_get (value, "trusted.glusterfs-quota-du"); +		if (data) { +			LOCK (&priv->lock); +			{ +				priv->current_disk_usage = data_to_uint64 (data); +			} +			UNLOCK (&priv->lock); + +			return 0; +		} +	}  + +	STACK_DESTROY (frame->root); + +	return 0; +} + + +void +gf_quota_get_disk_usage (xlator_t *this) +{ +	call_frame_t *frame = NULL; +	call_pool_t  *pool = NULL; +	loc_t         loc; + +	pool = this->ctx->pool; +	frame = create_frame (this, pool); +	build_root_loc (this, &loc); + +	STACK_WIND (frame, quota_getxattr_cbk, +		    this->children->xlator, +		    this->children->xlator->fops->getxattr, +		    &loc, +		    "trusted.glusterfs-quota-du"); +	return ; +} + + +void +gf_quota_cache_sync (xlator_t *this) +{ +	struct quota_priv *priv = NULL; +	call_frame_t      *frame = NULL; +	dict_t            *dict = get_new_dict (); +	loc_t              loc; + + +	priv = this->private; +	build_root_loc (this, &loc); + +	frame = create_frame (this, this->ctx->pool); +	dict_set (dict, "trusted.glusterfs-quota-du",  +		  data_from_uint64 (priv->current_disk_usage)); + +	STACK_WIND (frame, quota_setxattr_cbk, +		    this->children->xlator, +		    this->children->xlator->fops->setxattr, +		    &loc, dict, 0); +} + + +int +quota_release (xlator_t *this, fd_t *fd) +{ +	gf_quota_cache_sync (this); + +	return 0; +} + + +/* notify */ +int32_t +notify (xlator_t *this, +	int32_t event, +	void *data, +	...) +{ +	struct quota_priv *priv = this->private; +	 +	switch (event) +	{ +	case GF_EVENT_CHILD_UP: +		if (priv->only_first_time) { +			priv->only_first_time = 0; +			if (priv->disk_usage_limit) { +				gf_quota_get_disk_usage (this); +			} +		} +	default: +		default_notify (this, event, data); +		break; +	} + +	return 0; +} + + +int32_t  +init (xlator_t *this) +{ +	int     ret  = 0; +        data_t *data = NULL; +	struct quota_priv *_private = NULL; + +	if (!this->children || this->children->next) { +		gf_log (this->name, GF_LOG_ERROR,  +			"FATAL: quota should have exactly one child"); +		return -1; +	} +	 +	if (!this->parents) { +		gf_log (this->name, GF_LOG_WARNING, +			"dangling volume. check volfile "); +	} + +	_private = CALLOC (1, sizeof (struct quota_priv)); +        _private->disk_usage_limit = 0; +        data = dict_get (this->options, "disk-usage-limit"); +        if (data) { +		if (gf_string2bytesize (data->data, &_private->disk_usage_limit) != 0) { +                        gf_log (this->name, GF_LOG_ERROR,  +                                "invalid number '%s' for disk-usage limit", data->data); +			ret = -1; +			goto out; +                } + +		LOCK_INIT (&_private->lock); +		_private->current_disk_usage = 0; +	} +	 +        _private->min_free_disk_limit = 0; +        data = dict_get (this->options, "min-free-disk-limit"); +        if (data) { +		if (gf_string2percent (data->data, &_private->min_free_disk_limit) != 0) { +                        gf_log (this->name, GF_LOG_ERROR,  +                                "invalid percent '%s' for min-free-disk limit", data->data); +			ret = -1; +			goto out; +                } +		_private->refresh_interval = 20; /* 20seconds is default */ +		data = dict_get (this->options, "refresh-interval"); +		if (data) { +			if (gf_string2time (data->data,  +					    &_private->refresh_interval)!= 0) { +				gf_log (this->name, GF_LOG_ERROR,  +					"invalid time '%s' for refresh " +					"interval", data->data); +				ret = -1; +				goto out; +			} +		} +        } + +	_private->only_first_time = 1; +        this->private = (void *)_private; +	ret = 0; + out: +	return ret; +} + +void  +fini (xlator_t *this) +{ +	struct quota_priv *_private = this->private; + +	if (_private) { +		gf_quota_cache_sync (this); +		this->private = NULL; +	} +	 +	return ; +} + +struct xlator_fops fops = { +	.create      = quota_create, +	.open        = quota_open, +	.truncate    = quota_truncate, +	.ftruncate   = quota_ftruncate, +	.writev      = quota_writev, +	.unlink      = quota_unlink, +	.rmdir       = quota_rmdir, +	.mknod       = quota_mknod, +	.mkdir       = quota_mkdir, +	.symlink     = quota_symlink, +	.statfs      = quota_statfs, +}; + +struct xlator_mops mops = { +}; + +struct xlator_cbks cbks = { +	.release     = quota_release +}; + +struct volume_options options[] = { +	{ .key  = {"min-free-disk-limit"},  +	  .type = GF_OPTION_TYPE_PERCENT +	}, +	{ .key  = {"refresh-interval"},  +	  .type = GF_OPTION_TYPE_TIME +	}, +	{ .key  = {"disk-usage-limit"},  +	  .type = GF_OPTION_TYPE_SIZET  +	}, +	{ .key = {NULL} }, +};  | 
