diff options
Diffstat (limited to 'xlators/features/quota/src')
| -rw-r--r-- | xlators/features/quota/src/Makefile.am | 22 | ||||
| -rw-r--r-- | xlators/features/quota/src/quota-enforcer-client.c | 403 | ||||
| -rw-r--r-- | xlators/features/quota/src/quota-mem-types.h | 26 | ||||
| -rw-r--r-- | xlators/features/quota/src/quota.c | 3537 | ||||
| -rw-r--r-- | xlators/features/quota/src/quota.h | 183 | ||||
| -rw-r--r-- | xlators/features/quota/src/quotad-aggregator.c | 423 | ||||
| -rw-r--r-- | xlators/features/quota/src/quotad-aggregator.h | 37 | ||||
| -rw-r--r-- | xlators/features/quota/src/quotad-helpers.c | 113 | ||||
| -rw-r--r-- | xlators/features/quota/src/quotad-helpers.h | 24 | ||||
| -rw-r--r-- | xlators/features/quota/src/quotad.c | 210 |
10 files changed, 3908 insertions, 1070 deletions
diff --git a/xlators/features/quota/src/Makefile.am b/xlators/features/quota/src/Makefile.am index 4baa5f06e..7165adc59 100644 --- a/xlators/features/quota/src/Makefile.am +++ b/xlators/features/quota/src/Makefile.am @@ -1,16 +1,22 @@ -xlator_LTLIBRARIES = quota.la +xlator_LTLIBRARIES = quota.la quotad.la xlatordir = $(libdir)/glusterfs/$(PACKAGE_VERSION)/xlator/features -quota_la_LDFLAGS = -module -avoidversion +quota_la_LDFLAGS = -module -avoid-version +quotad_la_LDFLAGS = -module -avoid-version -quota_la_SOURCES = quota.c +quota_la_SOURCES = quota.c quota-enforcer-client.c quota_la_LIBADD = $(top_builddir)/libglusterfs/src/libglusterfs.la -noinst_HEADERS = quota-mem-types.h quota.h +quotad_la_SOURCES = quotad.c quotad-helpers.c quotad-aggregator.c +quotad_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) \ - -I$(top_srcdir)/xlators/cluster/dht/src +noinst_HEADERS = quota-mem-types.h quota.h quotad-aggregator.h quotad-helpers.h -CLEANFILES = +AM_CPPFLAGS = $(GF_CPPFLAGS) -I$(top_srcdir)/libglusterfs/src \ + -I$(top_srcdir)/xlators/cluster/dht/src -I$(top_srcdir)/rpc/xdr/src/ \ + -I$(top_srcdir)/rpc/rpc-lib/src + +AM_CFLAGS = -Wall $(GF_CFLAGS) + +CLEANFILES = diff --git a/xlators/features/quota/src/quota-enforcer-client.c b/xlators/features/quota/src/quota-enforcer-client.c new file mode 100644 index 000000000..7d8ab937d --- /dev/null +++ b/xlators/features/quota/src/quota-enforcer-client.c @@ -0,0 +1,403 @@ +/* + Copyright (c) 2010-2012 Red Hat, Inc. <http://www.redhat.com> + This file is part of GlusterFS. + + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. +*/ +#include <stdio.h> +#include <string.h> +#include <netinet/in.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <sys/resource.h> +#include <sys/file.h> +#include <netdb.h> +#include <signal.h> +#include <libgen.h> + +#include <sys/utsname.h> + +#include <stdint.h> +#include <pthread.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <time.h> +#include <semaphore.h> +#include <errno.h> + +#ifndef _CONFIG_H +#define _CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_MALLOC_H +#include <malloc.h> +#endif + +#ifdef HAVE_MALLOC_STATS +#ifdef DEBUG +#include <mcheck.h> +#endif +#endif + +#include "quota.h" + +extern struct rpc_clnt_program quota_enforcer_clnt; + +int32_t +quota_validate_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, inode_t *inode, + struct iatt *buf, dict_t *xdata, struct iatt *postparent); + +int +quota_enforcer_submit_request (void *req, call_frame_t *frame, + rpc_clnt_prog_t *prog, + int procnum, struct iobref *iobref, + xlator_t *this, fop_cbk_fn_t cbkfn, + xdrproc_t xdrproc) +{ + int ret = -1; + int count = 0; + struct iovec iov = {0, }; + struct iobuf *iobuf = NULL; + char new_iobref = 0; + ssize_t xdr_size = 0; + quota_priv_t *priv = NULL; + + GF_ASSERT (this); + + priv = this->private; + + if (req) { + xdr_size = xdr_sizeof (xdrproc, req); + iobuf = iobuf_get2 (this->ctx->iobuf_pool, xdr_size); + if (!iobuf) { + goto out; + } + + if (!iobref) { + iobref = iobref_new (); + if (!iobref) { + goto out; + } + + new_iobref = 1; + } + + iobref_add (iobref, iobuf); + + iov.iov_base = iobuf->ptr; + iov.iov_len = iobuf_size (iobuf); + + /* Create the xdr payload */ + ret = xdr_serialize_generic (iov, req, xdrproc); + if (ret == -1) { + goto out; + } + iov.iov_len = ret; + count = 1; + } + + /* Send the msg */ + ret = rpc_clnt_submit (priv->rpc_clnt, prog, procnum, cbkfn, + &iov, count, + NULL, 0, iobref, frame, NULL, 0, NULL, 0, NULL); + ret = 0; + +out: + if (new_iobref) + iobref_unref (iobref); + if (iobuf) + iobuf_unref (iobuf); + + return ret; +} + +int +quota_enforcer_lookup_cbk (struct rpc_req *req, struct iovec *iov, + int count, void *myframe) +{ + quota_local_t *local = NULL; + call_frame_t *frame = NULL; + int ret = 0; + gfs3_lookup_rsp rsp = {0,}; + struct iatt stbuf = {0,}; + struct iatt postparent = {0,}; + int op_errno = EINVAL; + dict_t *xdata = NULL; + inode_t *inode = NULL; + xlator_t *this = NULL; + + this = THIS; + + frame = myframe; + local = frame->local; + inode = local->validate_loc.inode; + + if (-1 == req->rpc_status) { + rsp.op_ret = -1; + op_errno = ENOTCONN; + goto out; + } + + ret = xdr_to_generic (*iov, &rsp, (xdrproc_t)xdr_gfs3_lookup_rsp); + if (ret < 0) { + gf_log (this->name, GF_LOG_ERROR, "XDR decoding failed"); + rsp.op_ret = -1; + op_errno = EINVAL; + goto out; + } + + op_errno = gf_error_to_errno (rsp.op_errno); + gf_stat_to_iatt (&rsp.postparent, &postparent); + + if (rsp.op_ret == -1) + goto out; + + rsp.op_ret = -1; + gf_stat_to_iatt (&rsp.stat, &stbuf); + + GF_PROTOCOL_DICT_UNSERIALIZE (frame->this, xdata, (rsp.xdata.xdata_val), + (rsp.xdata.xdata_len), rsp.op_ret, + op_errno, out); + + if ((!uuid_is_null (inode->gfid)) + && (uuid_compare (stbuf.ia_gfid, inode->gfid) != 0)) { + gf_log (frame->this->name, GF_LOG_DEBUG, + "gfid changed for %s", local->validate_loc.path); + rsp.op_ret = -1; + op_errno = ESTALE; + goto out; + } + + rsp.op_ret = 0; + +out: + rsp.op_errno = op_errno; + if (rsp.op_ret == -1) { + /* any error other than ENOENT */ + if (rsp.op_errno != ENOENT) + gf_log (this->name, GF_LOG_WARNING, + "remote operation failed: %s. Path: %s (%s)", + strerror (rsp.op_errno), + local->validate_loc.path, + loc_gfid_utoa (&local->validate_loc)); + else + gf_log (this->name, GF_LOG_TRACE, + "not found on remote node"); + + } + + local->validate_cbk (frame, NULL, this, rsp.op_ret, rsp.op_errno, inode, + &stbuf, xdata, &postparent); + + if (xdata) + dict_unref (xdata); + + free (rsp.xdata.xdata_val); + + return 0; +} + +int +quota_enforcer_lookup (call_frame_t *frame, xlator_t *this, loc_t *loc, + dict_t *xdata, fop_lookup_cbk_t validate_cbk) +{ + quota_local_t *local = NULL; + gfs3_lookup_req req = {{0,},}; + int ret = 0; + int op_errno = ESTALE; + quota_priv_t *priv = NULL; + + if (!frame || !this || !loc) + goto unwind; + + local = frame->local; + local->validate_cbk = validate_cbk; + + priv = this->private; + + if (!(loc && loc->inode)) + goto unwind; + + if (!uuid_is_null (loc->inode->gfid)) + memcpy (req.gfid, loc->inode->gfid, 16); + else + memcpy (req.gfid, loc->gfid, 16); + + if (xdata) { + GF_PROTOCOL_DICT_SERIALIZE (this, xdata, + (&req.xdata.xdata_val), + req.xdata.xdata_len, + op_errno, unwind); + } + + if (loc->name) + req.bname = (char *)loc->name; + else + req.bname = ""; + + ret = quota_enforcer_submit_request (&req, frame, + priv->quota_enforcer, + GF_AGGREGATOR_LOOKUP, + NULL, this, + quota_enforcer_lookup_cbk, + (xdrproc_t)xdr_gfs3_lookup_req); + + if (ret) { + gf_log (this->name, GF_LOG_WARNING, "failed to send the fop"); + } + + GF_FREE (req.xdata.xdata_val); + + return 0; + +unwind: + validate_cbk (frame, NULL, this, -1, op_errno, NULL, NULL, NULL, NULL); + + GF_FREE (req.xdata.xdata_val); + + return 0; +} + +int +quota_enforcer_notify (struct rpc_clnt *rpc, void *mydata, + rpc_clnt_event_t event, void *data) +{ + xlator_t *this = NULL; + int ret = 0; + + this = mydata; + + switch (event) { + case RPC_CLNT_CONNECT: + { + gf_log (this->name, GF_LOG_TRACE, "got RPC_CLNT_CONNECT"); + break; + } + + case RPC_CLNT_DISCONNECT: + { + gf_log (this->name, GF_LOG_TRACE, "got RPC_CLNT_DISCONNECT"); + break; + } + + default: + gf_log (this->name, GF_LOG_TRACE, + "got some other RPC event %d", event); + ret = 0; + break; + } + + return ret; +} + +int +quota_enforcer_blocking_connect (rpc_clnt_t *rpc) +{ + dict_t *options = NULL; + int ret = -1; + + options = dict_new (); + if (options == NULL) + goto out; + + ret = dict_set_str (options, "non-blocking-io", "no"); + if (ret) + goto out; + + rpc->conn.trans->reconfigure (rpc->conn.trans, options); + + rpc_clnt_start (rpc); + + ret = dict_set_str (options, "non-blocking-io", "yes"); + if (ret) + goto out; + + rpc->conn.trans->reconfigure (rpc->conn.trans, options); + + ret = 0; +out: + dict_unref (options); + + return ret; +} + +//Returns a started rpc_clnt. Creates a new rpc_clnt if quota_priv doesn't have +//one already +struct rpc_clnt * +quota_enforcer_init (xlator_t *this, dict_t *options) +{ + struct rpc_clnt *rpc = NULL; + quota_priv_t *priv = NULL; + int ret = -1; + + priv = this->private; + if (priv->rpc_clnt) { + gf_log (this->name, GF_LOG_TRACE, "quota enforcer clnt already " + "inited"); + //Turns out to be a NOP if the clnt is already connected. + ret = quota_enforcer_blocking_connect (priv->rpc_clnt); + if (ret) + goto out; + + return priv->rpc_clnt; + } + + priv->quota_enforcer = "a_enforcer_clnt; + + ret = dict_set_str (options, "transport.address-family", "unix"); + if (ret) + goto out; + + ret = dict_set_str (options, "transport-type", "socket"); + if (ret) + goto out; + + ret = dict_set_str (options, "transport.socket.connect-path", + "/tmp/quotad.socket"); + if (ret) + goto out; + + rpc = rpc_clnt_new (options, this->ctx, this->name, 16); + if (!rpc) { + ret = -1; + goto out; + } + + ret = rpc_clnt_register_notify (rpc, quota_enforcer_notify, this); + if (ret) { + gf_log ("cli", GF_LOG_ERROR, "failed to register notify"); + goto out; + } + + ret = quota_enforcer_blocking_connect (rpc); + if (ret) + goto out; + + ret = 0; +out: + if (ret) { + if (rpc) + rpc_clnt_unref (rpc); + rpc = NULL; + } + + return rpc; +} + +struct rpc_clnt_procedure quota_enforcer_actors[GF_AGGREGATOR_MAXVALUE] = { + [GF_AGGREGATOR_NULL] = {"NULL", NULL}, + [GF_AGGREGATOR_LOOKUP] = {"LOOKUP", NULL}, +}; + +struct rpc_clnt_program quota_enforcer_clnt = { + .progname = "Quota enforcer", + .prognum = GLUSTER_AGGREGATOR_PROGRAM, + .progver = GLUSTER_AGGREGATOR_VERSION, + .numproc = GF_AGGREGATOR_MAXVALUE, + .proctable = quota_enforcer_actors, +}; diff --git a/xlators/features/quota/src/quota-mem-types.h b/xlators/features/quota/src/quota-mem-types.h index ff5777f97..97d916568 100644 --- a/xlators/features/quota/src/quota-mem-types.h +++ b/xlators/features/quota/src/quota-mem-types.h @@ -1,30 +1,19 @@ /* - Copyright (c) 2008-2010 Gluster, Inc. <http://www.gluster.com> + Copyright (c) 2008-2012 Red Hat, Inc. <http://www.redhat.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 Affero 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 - Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see - <http://www.gnu.org/licenses/>. + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. */ - #ifndef __QUOTA_MEM_TYPES_H__ #define __QUOTA_MEM_TYPES_H__ #include "mem-types.h" enum gf_quota_mem_types_ { - gf_quota_mt_quota_local_t = gf_common_mt_end + 1, - gf_quota_mt_quota_priv_t, + gf_quota_mt_quota_priv_t = gf_common_mt_end + 1, gf_quota_mt_quota_inode_ctx_t, gf_quota_mt_loc_t, gf_quota_mt_char, @@ -32,6 +21,9 @@ enum gf_quota_mem_types_ { gf_quota_mt_int32_t, gf_quota_mt_limits_t, gf_quota_mt_quota_dentry_t, + gf_quota_mt_quota_limits_level_t, + gf_quota_mt_qd_vols_conf_t, + gf_quota_mt_aggregator_state_t, gf_quota_mt_end }; #endif diff --git a/xlators/features/quota/src/quota.c b/xlators/features/quota/src/quota.c index dc936b284..2ca4da0c1 100644 --- a/xlators/features/quota/src/quota.c +++ b/xlators/features/quota/src/quota.c @@ -1,62 +1,111 @@ /* - Copyright (c) 2008-2010 Gluster, Inc. <http://www.gluster.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 Affero 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 - Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see - <http://www.gnu.org/licenses/>. + Copyright (c) 2008-2012 Red Hat, Inc. <http://www.redhat.com> + This file is part of GlusterFS. + + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. */ +#include <fnmatch.h> #include "quota.h" #include "common-utils.h" +#include "defaults.h" +#include "statedump.h" int32_t quota_check_limit (call_frame_t *frame, inode_t *inode, xlator_t *this, - char *name, ino_t par); + char *name, uuid_t par); + +int +quota_fill_inodectx (xlator_t *this, inode_t *inode, dict_t *dict, + loc_t *loc, struct iatt *buf, int32_t *op_errno); + +struct volume_options options[]; + +static int32_t +__quota_init_inode_ctx (inode_t *inode, xlator_t *this, + quota_inode_ctx_t **context) +{ + int32_t ret = -1; + quota_inode_ctx_t *ctx = NULL; + + if (inode == NULL) { + goto out; + } + + QUOTA_ALLOC_OR_GOTO (ctx, quota_inode_ctx_t, out); + + LOCK_INIT(&ctx->lock); + + if (context != NULL) { + *context = ctx; + } + + INIT_LIST_HEAD (&ctx->parents); + + ret = __inode_ctx_put (inode, this, (uint64_t )(long)ctx); + if (ret == -1) { + gf_log (this->name, GF_LOG_WARNING, + "cannot set quota context in inode (gfid:%s)", + uuid_utoa (inode->gfid)); + } +out: + return ret; +} + + +static int32_t +quota_inode_ctx_get (inode_t *inode, xlator_t *this, + quota_inode_ctx_t **ctx, char create_if_absent) +{ + int32_t ret = 0; + uint64_t ctx_int; + + LOCK (&inode->lock); + { + ret = __inode_ctx_get (inode, this, &ctx_int); + + if ((ret == 0) && (ctx != NULL)) { + *ctx = (quota_inode_ctx_t *) (unsigned long)ctx_int; + } else if (create_if_absent) { + ret = __quota_init_inode_ctx (inode, this, ctx); + } + } + UNLOCK (&inode->lock); + + return ret; +} int quota_loc_fill (loc_t *loc, inode_t *inode, inode_t *parent, char *path) { int ret = -1; - if (!loc) { + if (!loc || (inode == NULL)) return ret; - } if (inode) { loc->inode = inode_ref (inode); - loc->ino = inode->ino; + uuid_copy (loc->gfid, inode->gfid); } if (parent) { loc->parent = inode_ref (parent); } - loc->path = gf_strdup (path); - if (!loc->path) { - goto loc_wipe; - } + if (path != NULL) { + loc->path = gf_strdup (path); - loc->name = strrchr (loc->path, '/'); - if (loc->name) { - loc->name++; - } else { - goto loc_wipe; + loc->name = strrchr (loc->path, '/'); + if (loc->name) { + loc->name++; + } } ret = 0; -loc_wipe: if (ret < 0) { loc_wipe (loc); } @@ -79,28 +128,24 @@ quota_inode_loc_fill (inode_t *inode, loc_t *loc) this = THIS; - if ((inode) && (inode->ino == 1)) { + if ((inode) && __is_root_gfid (inode->gfid)) { loc->parent = NULL; goto ignore_parent; } parent = inode_parent (inode, 0, NULL); if (!parent) { - gf_log (this->name, GF_LOG_WARNING, - "cannot find parent for inode (ino:%"PRId64", " - "gfid:%s)", inode->ino, + gf_log (this->name, GF_LOG_DEBUG, + "cannot find parent for inode (gfid:%s)", uuid_utoa (inode->gfid)); - goto err; } ignore_parent: ret = inode_path (inode, NULL, &resolvedpath); if (ret < 0) { - gf_log (this->name, GF_LOG_WARNING, - "cannot construct path for inode (ino:%"PRId64", " - "gfid:%s)", inode->ino, + gf_log (this->name, GF_LOG_DEBUG, + "cannot construct path for inode (gfid:%s)", uuid_utoa (inode->gfid)); - goto err; } ret = quota_loc_fill (loc, inode, parent, resolvedpath); @@ -135,28 +180,33 @@ quota_local_cleanup (xlator_t *this, quota_local_t *local) inode_unref (local->inode); LOCK_DESTROY (&local->lock); + mem_put (local); out: return 0; } -quota_local_t * +static inline quota_local_t * quota_local_new () { - quota_local_t *local = NULL; - int32_t ret = 0; + quota_local_t *local = NULL; + local = mem_get0 (THIS->local_pool); + if (local == NULL) + goto out; - QUOTA_LOCAL_ALLOC_OR_GOTO (local, quota_local_t, err); -err: + LOCK_INIT (&local->lock); + local->space_available = -1; + +out: return local; } quota_dentry_t * -__quota_dentry_new (quota_inode_ctx_t *ctx, char *name, ino_t par) +__quota_dentry_new (quota_inode_ctx_t *ctx, char *name, uuid_t par) { - quota_dentry_t *dentry = NULL; - int32_t ret = 0; + quota_dentry_t *dentry = NULL; + GF_UNUSED int32_t ret = 0; QUOTA_ALLOC_OR_GOTO (dentry, quota_dentry_t, err); @@ -165,12 +215,15 @@ __quota_dentry_new (quota_inode_ctx_t *ctx, char *name, ino_t par) dentry->name = gf_strdup (name); if (dentry->name == NULL) { GF_FREE (dentry); + dentry = NULL; goto err; } - dentry->par = par; + uuid_copy (dentry->par, par); + + if (ctx != NULL) + list_add_tail (&dentry->next, &ctx->parents); - list_add_tail (&dentry->next, &ctx->parents); err: return dentry; } @@ -191,19 +244,64 @@ out: return; } +static inline void +quota_link_count_decrement (quota_local_t *local) +{ + call_stub_t *stub = NULL; + int link_count = -1; + + if (local == NULL) + goto out; + + LOCK (&local->lock); + { + link_count = --local->link_count; + if (link_count == 0) { + stub = local->stub; + local->stub = NULL; + } + } + UNLOCK (&local->lock); + + if (stub != NULL) { + call_resume (stub); + } +out: + return; +} + +static inline void +quota_handle_validate_error (quota_local_t *local, int32_t op_ret, + int32_t op_errno) +{ + if (local == NULL) + goto out; + + LOCK (&local->lock); + { + if (op_ret < 0) { + local->op_ret = op_ret; + local->op_errno = op_errno; + } + } + UNLOCK (&local->lock); + + /* we abort checking limits on this path to root */ + quota_link_count_decrement (local); +out: + return; +} int32_t quota_validate_cbk (call_frame_t *frame, void *cookie, xlator_t *this, - int32_t op_ret, int32_t op_errno, dict_t *dict) + int32_t op_ret, int32_t op_errno, inode_t *inode, + struct iatt *buf, dict_t *xdata, struct iatt *postparent) { - quota_local_t *local = NULL; - uint32_t validate_count = 0, link_count = 0; - int32_t ret = 0; - quota_inode_ctx_t *ctx = NULL; - quota_priv_t *priv = NULL; - int64_t *size = 0; - uint64_t value = 0; - call_stub_t *stub = NULL; + quota_local_t *local = NULL; + int32_t ret = 0; + quota_inode_ctx_t *ctx = NULL; + int64_t *size = 0; + uint64_t value = 0; local = frame->local; @@ -212,13 +310,10 @@ quota_validate_cbk (call_frame_t *frame, void *cookie, xlator_t *this, } GF_ASSERT (local); - - priv = this->private; - GF_ASSERT (frame); GF_VALIDATE_OR_GOTO_WITH_ERROR ("quota", this, unwind, op_errno, EINVAL); - GF_VALIDATE_OR_GOTO_WITH_ERROR (this->name, dict, unwind, op_errno, + GF_VALIDATE_OR_GOTO_WITH_ERROR (this->name, xdata, unwind, op_errno, EINVAL); ret = inode_ctx_get (local->validate_loc.inode, this, &value); @@ -226,14 +321,13 @@ quota_validate_cbk (call_frame_t *frame, void *cookie, xlator_t *this, ctx = (quota_inode_ctx_t *)(unsigned long)value; if ((ret == -1) || (ctx == NULL)) { gf_log (this->name, GF_LOG_WARNING, - "quota context is not present in inode (ino:%"PRId64", " - "gfid:%s)", local->validate_loc.inode->ino, + "quota context is not present in inode (gfid:%s)", uuid_utoa (local->validate_loc.inode->gfid)); op_errno = EINVAL; goto unwind; } - ret = dict_get_bin (dict, QUOTA_SIZE_KEY, (void **) &size); + ret = dict_get_bin (xdata, QUOTA_SIZE_KEY, (void **) &size); if (ret < 0) { gf_log (this->name, GF_LOG_WARNING, "size key not present in dict"); @@ -252,29 +346,11 @@ quota_validate_cbk (call_frame_t *frame, void *cookie, xlator_t *this, } UNLOCK (&ctx->lock); - quota_check_limit (frame, local->validate_loc.inode, this, NULL, 0); + quota_check_limit (frame, local->validate_loc.inode, this, NULL, NULL); return 0; unwind: - LOCK (&local->lock); - { - local->op_ret = -1; - local->op_errno = op_errno; - - validate_count = --local->validate_count; - link_count = local->link_count; - - if ((validate_count == 0) && (link_count == 0)) { - stub = local->stub; - local->stub = NULL; - } - } - UNLOCK (&local->lock); - - if (stub != NULL) { - call_resume (stub); - } - + quota_handle_validate_error (local, op_ret, op_errno); return 0; } @@ -301,336 +377,642 @@ quota_timeout (struct timeval *tv, int32_t timeout) return timed_out; } +static inline void +quota_add_parent (quota_dentry_t *dentry, struct list_head *list) +{ + quota_dentry_t *entry = NULL; + gf_boolean_t found = _gf_false; + + if ((dentry == NULL) || (list == NULL)) { + goto out; + } + + list_for_each_entry (entry, list, next) { + if (uuid_compare (dentry->par, entry->par) == 0) { + found = _gf_true; + goto out; + } + } + + list_add_tail (&dentry->next, list); + +out: + return; +} int32_t -quota_check_limit (call_frame_t *frame, inode_t *inode, xlator_t *this, - char *name, ino_t par) -{ - int32_t ret = -1; - inode_t *_inode = NULL, *parent = NULL; - quota_inode_ctx_t *ctx = NULL; - quota_priv_t *priv = NULL; - quota_local_t *local = NULL; - char need_validate = 0, need_unwind = 0; - int64_t delta = 0; - call_stub_t *stub = NULL; - int32_t validate_count = 0, link_count = 0; - uint64_t value = 0; - char just_validated = 0; - - GF_VALIDATE_OR_GOTO ("quota", this, out); - GF_VALIDATE_OR_GOTO (this->name, frame, out); - GF_VALIDATE_OR_GOTO (this->name, inode, out); +quota_build_ancestry_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, + gf_dirent_t *entries, dict_t *xdata) +{ + inode_t *parent = NULL, *tmp_parent = NULL; + gf_dirent_t *entry = NULL; + loc_t loc = {0, }; + quota_dentry_t *dentry = NULL, *tmp = NULL; + quota_inode_ctx_t *ctx = NULL; + struct list_head parents = {0, }; + quota_local_t *local = NULL; + + INIT_LIST_HEAD (&parents); local = frame->local; - GF_VALIDATE_OR_GOTO (this->name, local, out); + frame->local = NULL; - delta = local->delta; + if (op_ret < 0) + goto err; - GF_VALIDATE_OR_GOTO (this->name, local->stub, out); + parent = inode_parent (local->loc.inode, 0, NULL); + if (parent == NULL) { + gf_log (this->name, GF_LOG_WARNING, "parent is NULL"); + op_errno = EINVAL; + goto err; + } - priv = this->private; + if ((op_ret > 0) && (entries != NULL)) { + list_for_each_entry (entry, &entries->list, list) { + if (__is_root_gfid (entry->inode->gfid)) { + /* The list contains a sub-list for each + * possible path to the target inode. Each + * sub-list starts with the root entry of the + * tree and is followed by the child entries + * for a particular path to the target entry. + * The root entry is an implied sub-list + * delimiter, as it denotes we have started + * processing a new path. Reset the parent + * pointer and continue + */ + + tmp_parent = NULL; + } - inode_ctx_get (inode, this, &value); - ctx = (quota_inode_ctx_t *)(unsigned long)value; + uuid_copy (loc.gfid, entry->d_stat.ia_gfid); - _inode = inode_ref (inode); + loc.inode = inode_ref (entry->inode); + loc.parent = inode_ref (tmp_parent); + loc.name = entry->d_name; - just_validated = local->just_validated; - local->just_validated = 0; + quota_fill_inodectx (this, entry->inode, entry->dict, + &loc, &entry->d_stat, &op_errno); - do { - if (ctx != NULL) { - LOCK (&ctx->lock); - { - if (ctx->limit >= 0) { - if (!just_validated - && quota_timeout (&ctx->tv, - priv->timeout)) { - need_validate = 1; - } else if ((ctx->size + delta) - >= ctx->limit) { - local->op_ret = -1; - local->op_errno = EDQUOT; - need_unwind = 1; - } - } - } - UNLOCK (&ctx->lock); + tmp_parent = entry->inode; - if (need_validate) { - goto validate; - } + loc_wipe (&loc); + } + } - if (need_unwind) { - break; + quota_inode_ctx_get (local->loc.inode, this, &ctx, 0); + + if (ctx != NULL) { + LOCK (&ctx->lock); + { + list_for_each_entry (dentry, &ctx->parents, next) { + /* we built ancestry for a non-directory */ + tmp = __quota_dentry_new (NULL, dentry->name, + dentry->par); + quota_add_parent (tmp, &parents); + + if (list_empty (&tmp->next)) { + __quota_dentry_free (tmp); + tmp = NULL; + } } } + UNLOCK (&ctx->lock); + } - if (_inode->ino == 1) { - break; + if (list_empty (&parents)) { + /* we built ancestry for a directory */ + list_for_each_entry (entry, &entries->list, list) { + if (entry->inode == local->loc.inode) + break; } - parent = inode_parent (_inode, par, name); + GF_ASSERT (&entry->list != &entries->list); - if (name != NULL) { - name = NULL; - par = 0; - } + tmp = __quota_dentry_new (NULL, entry->d_name, parent->gfid); + quota_add_parent (tmp, &parents); + } - if (parent == NULL) { - gf_log (this->name, GF_LOG_WARNING, - "cannot find parent for inode (ino:%"PRId64", " - "gfid:%s), hence aborting enforcing " - "quota-limits and continuing with the fop", - _inode->ino, uuid_utoa (_inode->gfid)); - } + local->ancestry_cbk (&parents, local->loc.inode, 0, 0, + local->ancestry_data); + goto cleanup; - inode_unref (_inode); - _inode = parent; +err: + local->ancestry_cbk (NULL, NULL, -1, op_errno, local->ancestry_data); - if (_inode == NULL) { - break; - } +cleanup: + STACK_DESTROY (frame->root); + quota_local_cleanup (this, local); - value = 0; - inode_ctx_get (_inode, this, &value); - ctx = (quota_inode_ctx_t *)(unsigned long)value; - } while (1); + if (parent != NULL) { + inode_unref (parent); + parent = NULL; + } - ret = 0; + list_for_each_entry_safe (dentry, tmp, &parents, next) { + __quota_dentry_free (dentry); + } - if (_inode != NULL) { - inode_unref (_inode); + return 0; +} + +int32_t +quota_build_ancestry_open_cbk (call_frame_t *frame, void *cookie, + xlator_t *this, int32_t op_ret, int32_t op_errno, + fd_t *fd, dict_t *xdata) +{ + dict_t *xdata_req = NULL; + quota_local_t *local = NULL; + + if (op_ret < 0) { + goto err; } - LOCK (&local->lock); - { - if (just_validated) { - local->validate_count--; - } + xdata_req = dict_new (); + if (xdata_req == NULL) { + op_ret = -ENOMEM; + goto err; + } - validate_count = local->validate_count; - link_count = local->link_count; - if ((validate_count == 0) && (link_count == 0)) { - stub = local->stub; - local->stub = NULL; - } + op_ret = dict_set_int8 (xdata_req, QUOTA_LIMIT_KEY, 1); + if (op_ret < 0) { + op_errno = -op_ret; + goto err; } - UNLOCK (&local->lock); - if (stub != NULL) { - call_resume (stub); + op_ret = dict_set_int8 (xdata_req, GET_ANCESTRY_DENTRY_KEY, 1); + if (op_ret < 0) { + op_errno = -op_ret; + goto err; } -out: - return ret; + /* This would ask posix layer to construct dentry chain till root */ + STACK_WIND (frame, quota_build_ancestry_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->readdirp, fd, 0, 0, xdata_req); + + op_ret = 0; + +err: + fd_unref (fd); + + dict_unref (xdata_req); + + if (op_ret < 0) { + local = frame->local; + frame->local = NULL; + + local->ancestry_cbk (NULL, NULL, -1, op_errno, + local->ancestry_data); + quota_local_cleanup (this, local); + STACK_DESTROY (frame->root); + } + + return 0; +} + +int +quota_build_ancestry (inode_t *inode, quota_ancestry_built_t ancestry_cbk, + void *data) +{ + loc_t loc = {0, }; + fd_t *fd = NULL; + quota_local_t *local = NULL; + call_frame_t *new_frame = NULL; + int op_errno = EINVAL; + xlator_t *this = NULL; + + this = THIS; + + loc.inode = inode_ref (inode); + uuid_copy (loc.gfid, inode->gfid); + + fd = fd_create (inode, 0); + + new_frame = create_frame (this, this->ctx->pool); + if (new_frame == NULL) { + op_errno = ENOMEM; + goto err; + } + + new_frame->root->uid = new_frame->root->gid = 0; + + local = quota_local_new (); + if (local == NULL) { + op_errno = ENOMEM; + goto err; + } + + new_frame->local = local; + local->ancestry_cbk = ancestry_cbk; + local->ancestry_data = data; + local->loc.inode = inode_ref (inode); + + if (IA_ISDIR (inode->ia_type)) { + STACK_WIND (new_frame, quota_build_ancestry_open_cbk, + FIRST_CHILD(this), + FIRST_CHILD(this)->fops->opendir, &loc, fd, + NULL); + } else { + STACK_WIND (new_frame, quota_build_ancestry_open_cbk, + FIRST_CHILD(this), + FIRST_CHILD(this)->fops->open, &loc, 0, fd, + NULL); + } + + loc_wipe (&loc); + return 0; + +err: + ancestry_cbk (NULL, NULL, -1, op_errno, data); + + fd_unref (fd); + + local = new_frame->local; + new_frame->local = NULL; + + if (local != NULL) { + quota_local_cleanup (this, local); + } + + if (new_frame != NULL) { + STACK_DESTROY (new_frame->root); + } + + loc_wipe (&loc); + return 0; +} + +int +quota_validate (call_frame_t *frame, inode_t *inode, xlator_t *this, + fop_lookup_cbk_t cbk_fn) +{ + quota_local_t *local = NULL; + int ret = 0; + dict_t *xdata = NULL; + quota_priv_t *priv = NULL; + + local = frame->local; + priv = this->private; -validate: LOCK (&local->lock); { loc_wipe (&local->validate_loc); - if (just_validated) { - local->validate_count--; - } - - local->validate_count++; - ret = quota_inode_loc_fill (_inode, &local->validate_loc); + ret = quota_inode_loc_fill (inode, &local->validate_loc); if (ret < 0) { gf_log (this->name, GF_LOG_WARNING, - "cannot fill loc for inode (ino:%"PRId64", " - "gfid:%s), hence aborting quota-checks and " - "continuing with the fop", _inode->ino, - uuid_utoa (_inode->gfid)); - local->validate_count--; + "cannot fill loc for inode (gfid:%s), hence " + "aborting quota-checks and continuing with fop", + uuid_utoa (inode->gfid)); } } UNLOCK (&local->lock); if (ret < 0) { - goto loc_fill_failed; + ret = -ENOMEM; + goto err; } - STACK_WIND (frame, quota_validate_cbk, FIRST_CHILD(this), - FIRST_CHILD(this)->fops->getxattr, &local->validate_loc, - QUOTA_SIZE_KEY); + xdata = dict_new (); + if (xdata == NULL) { + ret = -ENOMEM; + goto err; + } -loc_fill_failed: - inode_unref (_inode); - return 0; -} + ret = dict_set_int8 (xdata, QUOTA_SIZE_KEY, 1); + if (ret < 0) { + gf_log (this->name, GF_LOG_WARNING, "dict set failed"); + ret = -ENOMEM; + goto err; + } + ret = dict_set_str (xdata, "volume-uuid", priv->volume_uuid); + if (ret < 0) { + gf_log (this->name, GF_LOG_WARNING, "dict set failed"); + ret = -ENOMEM; + goto err; + } -int32_t -quota_get_limit_value (inode_t *inode, xlator_t *this, int64_t *n) + ret = quota_enforcer_lookup (frame, this, &local->validate_loc, xdata, + cbk_fn); + if (ret < 0) { + ret = -ENOTCONN; + goto err; + } + + ret = 0; +err: + return ret; +} + +void +quota_check_limit_continuation (struct list_head *parents, inode_t *inode, + int32_t op_ret, int32_t op_errno, void *data) { - int32_t ret = 0; - char *path = NULL; - limits_t *limit_node = NULL; - quota_priv_t *priv = NULL; + call_frame_t *frame = NULL; + xlator_t *this = NULL; + quota_local_t *local = NULL; + quota_dentry_t *entry = NULL; + inode_t *parent = NULL; + int parent_count = 0; + + frame = data; + local = frame->local; + this = THIS; - if (inode == NULL || n == NULL) { - ret = -1; + if ((op_ret < 0) || list_empty (parents)) { + if (op_ret >= 0) { + gf_log (this->name, GF_LOG_WARNING, + "Couldn't build ancestry for inode (gfid:%s). " + "Without knowing ancestors till root, quota " + "cannot be enforced. " + "Hence, failing fop with EIO", + uuid_utoa (inode->gfid)); + op_errno = EIO; + } + + quota_handle_validate_error (local, -1, op_errno); goto out; } - *n = 0; + list_for_each_entry (entry, parents, next) { + parent_count++; + } - ret = inode_path (inode, NULL, &path); - if (ret < 0) { - ret = -1; - goto out; + LOCK (&local->lock); + { + local->link_count += (parent_count - 1); } + UNLOCK (&local->lock); - priv = this->private; + list_for_each_entry (entry, parents, next) { + parent = inode_find (inode->table, entry->par); - list_for_each_entry (limit_node, &priv->limit_head, limit_list) { - if (strcmp (limit_node->path, path) == 0) { - *n = limit_node->value; - break; - } + quota_check_limit (frame, parent, this, NULL, NULL); } out: - return ret; + return; } - -static int32_t -__quota_init_inode_ctx (inode_t *inode, int64_t limit, xlator_t *this, - dict_t *dict, struct iatt *buf, - quota_inode_ctx_t **context) +int32_t +quota_check_limit (call_frame_t *frame, inode_t *inode, xlator_t *this, + char *name, uuid_t par) { - int32_t ret = -1; - int64_t *size = 0; - quota_inode_ctx_t *ctx = NULL; - quota_priv_t *priv = NULL; + int32_t ret = -1, op_errno = EINVAL; + inode_t *_inode = NULL, *parent = NULL; + quota_inode_ctx_t *ctx = NULL; + quota_priv_t *priv = NULL; + quota_local_t *local = NULL; + char need_validate = 0; + gf_boolean_t hard_limit_exceeded = 0; + int64_t delta = 0, wouldbe_size = 0; + int64_t space_available = 0; + uint64_t value = 0; + char just_validated = 0; + uuid_t trav_uuid = {0,}; + uint32_t timeout = 0; + + GF_VALIDATE_OR_GOTO ("quota", this, err); + GF_VALIDATE_OR_GOTO (this->name, frame, err); + GF_VALIDATE_OR_GOTO (this->name, inode, err); + + local = frame->local; + GF_VALIDATE_OR_GOTO (this->name, local, err); - if (inode == NULL) { - goto out; + delta = local->delta; + + GF_VALIDATE_OR_GOTO (this->name, local->stub, err); + /* Allow all the trusted clients + * Don't block the gluster internal processes like rebalance, gsyncd, + * self heal etc from the disk quotas. + * + * Method: Allow all the clients with PID negative. This is by the + * assumption that any kernel assigned pid doesn't have the negative + * number. + */ + if (0 > frame->root->pid) { + ret = 0; + quota_link_count_decrement (local); + goto done; } priv = this->private; - QUOTA_ALLOC_OR_GOTO (ctx, quota_inode_ctx_t, out); + inode_ctx_get (inode, this, &value); + ctx = (quota_inode_ctx_t *)(unsigned long)value; - ctx->limit = limit; - if (buf) - ctx->buf = *buf; + _inode = inode_ref (inode); - LOCK_INIT(&ctx->lock); + LOCK (&local->lock); + { + just_validated = local->just_validated; + local->just_validated = 0; + } + UNLOCK (&local->lock); - if (context != NULL) { - *context = ctx; + if ( par != NULL ) { + uuid_copy (trav_uuid, par); } - INIT_LIST_HEAD (&ctx->parents); + do { + if (ctx != NULL && (ctx->hard_lim > 0 || ctx->soft_lim > 0)) { + wouldbe_size = ctx->size + delta; - if (dict != NULL) { - ret = dict_get_bin (dict, QUOTA_SIZE_KEY, (void **) &size); - if (ret == 0) { - ctx->size = ntoh64 (*size); - gettimeofday (&ctx->tv, NULL); + LOCK (&ctx->lock); + { + timeout = priv->soft_timeout; + + if ((ctx->soft_lim >= 0) + && (wouldbe_size > ctx->soft_lim)) { + timeout = priv->hard_timeout; + } + + if (!just_validated + && quota_timeout (&ctx->tv, timeout)) { + need_validate = 1; + } else if (wouldbe_size >= ctx->hard_lim) { + hard_limit_exceeded = 1; + } + } + UNLOCK (&ctx->lock); + + if (need_validate) { + ret = quota_validate (frame, _inode, this, + quota_validate_cbk); + if (ret < 0) { + op_errno = -ret; + goto err; + } + + break; + } + + if (hard_limit_exceeded) { + local->op_ret = -1; + local->op_errno = EDQUOT; + + space_available = ctx->hard_lim - ctx->size; + + if (space_available < 0) + space_available = 0; + + if ((local->space_available < 0) + || (local->space_available + > space_available)){ + local->space_available + = space_available; + + } + + if (space_available == 0) { + op_errno = EDQUOT; + goto err; + } + } + + /* We log usage only if quota limit is configured on + that inode. */ + quota_log_usage (this, ctx, _inode, delta); } - } - ret = __inode_ctx_put (inode, this, (uint64_t )(long)ctx); - if (ret == -1) { - gf_log (this->name, GF_LOG_WARNING, - "cannot set quota context in inode (ino:%"PRId64", " - "gfid:%s)", inode->ino, uuid_utoa (inode->gfid)); - } -out: - return ret; -} + if (__is_root_gfid (_inode->gfid)) { + quota_link_count_decrement (local); + break; + } + parent = inode_parent (_inode, trav_uuid, name); -static int32_t -quota_inode_ctx_get (inode_t *inode, int64_t limit, xlator_t *this, - dict_t *dict, struct iatt *buf, quota_inode_ctx_t **ctx, - char create_if_absent) -{ - int32_t ret = 0; - uint64_t ctx_int; + if (name != NULL) { + name = NULL; + uuid_clear (trav_uuid); + } - LOCK (&inode->lock); - { - ret = __inode_ctx_get (inode, this, &ctx_int); + if (parent == NULL) { + ret = quota_build_ancestry (_inode, + quota_check_limit_continuation, + frame); + if (ret < 0) { + op_errno = -ret; + goto err; + } - if ((ret == 0) && (ctx != NULL)) { - *ctx = (quota_inode_ctx_t *) (unsigned long)ctx_int; - } else if (create_if_absent) { - ret = __quota_init_inode_ctx (inode, limit, this, dict, - buf, ctx); + break; + } + + inode_unref (_inode); + _inode = parent; + just_validated = 0; + + if (_inode == NULL) { + break; } + + value = 0; + inode_ctx_get (_inode, this, &value); + ctx = (quota_inode_ctx_t *)(unsigned long)value; + } while (1); + + if (_inode != NULL) { + inode_unref (_inode); + _inode = NULL; } - UNLOCK (&inode->lock); - return ret; -} +done: + return 0; +err: + quota_handle_validate_error (local, -1, op_errno); -int32_t -quota_lookup_cbk (call_frame_t *frame, void *cookie, xlator_t *this, - int32_t op_ret, int32_t op_errno, inode_t *inode, - struct iatt *buf, dict_t *dict, struct iatt *postparent) + inode_unref (_inode); + return 0; +} + +static inline int +quota_get_limits (xlator_t *this, dict_t *dict, int64_t *hard_lim, + int64_t *soft_lim) { - int32_t ret = -1; - char found = 0; - quota_local_t *local = NULL; - quota_inode_ctx_t *ctx = NULL; - quota_dentry_t *dentry = NULL; - quota_priv_t *priv = NULL; - int64_t *size = 0; + quota_limit_t *limit = NULL; + quota_priv_t *priv = NULL; + int64_t soft_lim_percent = 0, *ptr = NULL; + int ret = 0; - local = frame->local; + if ((this == NULL) || (dict == NULL) || (hard_lim == NULL) + || (soft_lim == NULL)) + goto out; - if ((op_ret < 0) || (local == NULL) - || ((local->limit < 0) && !((IA_ISREG (buf->ia_type)) - || (IA_ISLNK (buf->ia_type))))) { - goto unwind; + priv = this->private; + + ret = dict_get_bin (dict, QUOTA_LIMIT_KEY, (void **) &ptr); + limit = (quota_limit_t *)ptr; + + if (limit) { + *hard_lim = ntoh64 (limit->hard_lim); + soft_lim_percent = ntoh64 (limit->soft_lim_percent); } - priv = this->private; + if (soft_lim_percent < 0) { + soft_lim_percent = priv->default_soft_lim; + } - ret = quota_inode_ctx_get (local->loc.inode, local->limit, this, dict, - buf, &ctx, 1); + if ((*hard_lim > 0) && (soft_lim_percent > 0)) { + *soft_lim = (soft_lim_percent * (*hard_lim))/100; + } + +out: + return 0; +} + +int +quota_fill_inodectx (xlator_t *this, inode_t *inode, dict_t *dict, + loc_t *loc, struct iatt *buf, int32_t *op_errno) +{ + int32_t ret = -1; + char found = 0; + quota_inode_ctx_t *ctx = NULL; + quota_dentry_t *dentry = NULL; + uint64_t value = 0; + int64_t hard_lim = -1, soft_lim = -1; + + quota_get_limits (this, dict, &hard_lim, &soft_lim); + + inode_ctx_get (inode, this, &value); + ctx = (quota_inode_ctx_t *)(unsigned long)value; + + if ((((ctx == NULL) || (ctx->hard_lim == hard_lim)) + && (hard_lim < 0) && !QUOTA_REG_OR_LNK_FILE (buf->ia_type))) { + ret = 0; + goto out; + } + + ret = quota_inode_ctx_get (inode, this, &ctx, 1); if ((ret == -1) || (ctx == NULL)) { gf_log (this->name, GF_LOG_WARNING, "cannot create quota " - "context in inode(ino:%"PRId64", gfid:%s)", - local->loc.inode->ino, - uuid_utoa (local->loc.inode->gfid)); - op_ret = -1; - op_errno = ENOMEM; - goto unwind; + "context in inode(gfid:%s)", + uuid_utoa (inode->gfid)); + ret = -1; + *op_errno = ENOMEM; + goto out; } LOCK (&ctx->lock); { - - if (dict != NULL) { - ret = dict_get_bin (dict, QUOTA_SIZE_KEY, - (void **) &size); - if (ret == 0) { - ctx->size = ntoh64 (*size); - gettimeofday (&ctx->tv, NULL); - } - } - - if (local->limit != ctx->limit) { - ctx->limit = local->limit; - } + ctx->hard_lim = hard_lim; + ctx->soft_lim = soft_lim; ctx->buf = *buf; - if (!(IA_ISREG (buf->ia_type) || IA_ISLNK (buf->ia_type))) { + if (!QUOTA_REG_OR_LNK_FILE (buf->ia_type)) { goto unlock; } + if (loc->name == NULL) + goto unlock; + list_for_each_entry (dentry, &ctx->parents, next) { - if ((strcmp (dentry->name, local->loc.name) == 0) - && (local->loc.parent->ino == dentry->par)) { + if ((strcmp (dentry->name, loc->name) == 0) && + (uuid_compare (loc->parent->gfid, + dentry->par) == 0)) { found = 1; break; } @@ -638,20 +1020,18 @@ quota_lookup_cbk (call_frame_t *frame, void *cookie, xlator_t *this, if (!found) { dentry = __quota_dentry_new (ctx, - (char *)local->loc.name, - local->loc.parent->ino); + (char *)loc->name, + loc->parent->gfid); if (dentry == NULL) { /* - gf_log (this->name, GF_LOG_WARNING, + gf_log (this->name, GF_LOG_WARNING, "cannot create a new dentry (par:%" PRId64", name:%s) for inode(ino:%" PRId64", gfid:%s)", - local->loc.parent->ino, - local->loc.inode->ino, uuid_utoa (local->loc.inode->gfid)); */ - op_ret = -1; - op_errno = ENOMEM; + ret = -1; + *op_errno = ENOMEM; goto unlock; } } @@ -659,6 +1039,25 @@ quota_lookup_cbk (call_frame_t *frame, void *cookie, xlator_t *this, unlock: UNLOCK (&ctx->lock); +out: + return ret; +} + +int32_t +quota_lookup_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, inode_t *inode, + struct iatt *buf, dict_t *dict, struct iatt *postparent) +{ + quota_local_t *local = NULL; + + if (op_ret < 0) + goto unwind; + + local = frame->local; + + op_ret = quota_fill_inodectx (this, inode, dict, &local->loc, buf, + &op_errno); + unwind: QUOTA_STACK_UNWIND (lookup, frame, op_ret, op_errno, inode, buf, dict, postparent); @@ -670,139 +1069,64 @@ int32_t quota_lookup (call_frame_t *frame, xlator_t *this, loc_t *loc, dict_t *xattr_req) { - int32_t ret = -1; - int64_t limit = -1; - limits_t *limit_node = NULL; - gf_boolean_t dict_newed = _gf_false; - quota_priv_t *priv = NULL; - quota_local_t *local = NULL; + quota_priv_t *priv = NULL; + int32_t ret = -1; + quota_local_t *local = NULL; priv = this->private; - list_for_each_entry (limit_node, &priv->limit_head, limit_list) { - if (strcmp (limit_node->path, loc->path) == 0) { - limit = limit_node->value; - } - } + WIND_IF_QUOTAOFF (priv->is_quota_on, off); - local = quota_local_new (); - if (local == NULL) { + xattr_req = xattr_req ? dict_ref(xattr_req) : dict_new(); + if (!xattr_req) goto err; - } - ret = loc_copy (&local->loc, loc); - if (ret == -1) { + local = quota_local_new (); + if (local == NULL) { goto err; } frame->local = local; + loc_copy (&local->loc, loc); - local->limit = limit; - - if (limit < 0) { - goto wind; - } - - if (xattr_req == NULL) { - xattr_req = dict_new (); - dict_newed = _gf_true; - } - - ret = dict_set_uint64 (xattr_req, QUOTA_SIZE_KEY, 0); + ret = dict_set_int8 (xattr_req, QUOTA_LIMIT_KEY, 1); if (ret < 0) { + gf_log (this->name, GF_LOG_WARNING, + "dict set of key for hard-limit failed"); goto err; } -wind: STACK_WIND (frame, quota_lookup_cbk, FIRST_CHILD(this), FIRST_CHILD(this)->fops->lookup, loc, xattr_req); ret = 0; err: + if (xattr_req) + dict_unref (xattr_req); + if (ret < 0) { QUOTA_STACK_UNWIND (lookup, frame, -1, ENOMEM, NULL, NULL, NULL, NULL); } - if (dict_newed == _gf_true) { - dict_unref (xattr_req); - } - return 0; -} - - -void -quota_update_size (xlator_t *this, inode_t *inode, char *name, ino_t par, - int64_t delta) -{ - inode_t *_inode = NULL, *parent = NULL; - uint64_t value = 0; - quota_inode_ctx_t *ctx = NULL; - - GF_VALIDATE_OR_GOTO ("quota", this, out); - GF_VALIDATE_OR_GOTO (this->name, inode, out); - - inode_ctx_get (inode, this, &value); - ctx = (quota_inode_ctx_t *)(unsigned long)value; - - _inode = inode_ref (inode); - - do { - if ((ctx != NULL) && (ctx->limit >= 0)) { - LOCK (&ctx->lock); - { - ctx->size += delta; - } - UNLOCK (&ctx->lock); - } - - if (_inode->ino == 1) { - break; - } - - parent = inode_parent (_inode, par, name); - if (parent == NULL) { - gf_log (this->name, GF_LOG_WARNING, - "cannot find parent for inode (ino:%"PRId64", " - "gfid:%s), hence aborting size updation of " - "parents", - _inode->ino, uuid_utoa (_inode->gfid)); - } - - if (name != NULL) { - name = NULL; - par = 0; - } - - inode_unref (_inode); - _inode = parent; - - if (_inode == NULL) { - break; - } - - inode_ctx_get (_inode, this, &value); - ctx = (quota_inode_ctx_t *)(unsigned long)value; - } while (1); -out: - return; +off: + STACK_WIND_TAIL (frame, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->lookup, loc, xattr_req); + return 0; } - int32_t quota_writev_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, struct iatt *prebuf, - struct iatt *postbuf) + struct iatt *postbuf, dict_t *xdata) { int32_t ret = 0; uint64_t ctx_int = 0; quota_inode_ctx_t *ctx = NULL; quota_local_t *local = NULL; - quota_dentry_t *dentry = NULL; - int64_t delta = 0; local = frame->local; @@ -811,14 +1135,18 @@ quota_writev_cbk (call_frame_t *frame, void *cookie, xlator_t *this, } ret = inode_ctx_get (local->loc.inode, this, &ctx_int); + if (ret) { + gf_log (this->name, GF_LOG_WARNING, + "%s: failed to get the context", local->loc.path); + goto out; + } ctx = (quota_inode_ctx_t *)(unsigned long) ctx_int; if (ctx == NULL) { gf_log (this->name, GF_LOG_WARNING, - "quota context not set in inode (ino:%"PRId64 - ", gfid:%s)", local->loc.inode->ino, - uuid_utoa (local->loc.inode->gfid)); + "quota context not set in %s (gfid:%s)", + local->loc.path, uuid_utoa (local->loc.inode->gfid)); goto out; } @@ -828,14 +1156,9 @@ quota_writev_cbk (call_frame_t *frame, void *cookie, xlator_t *this, } UNLOCK (&ctx->lock); - list_for_each_entry (dentry, &ctx->parents, next) { - delta = (postbuf->ia_blocks - prebuf->ia_blocks) * 512; - quota_update_size (this, local->loc.inode, - dentry->name, dentry->par, delta); - } - out: - QUOTA_STACK_UNWIND (writev, frame, op_ret, op_errno, prebuf, postbuf); + QUOTA_STACK_UNWIND (writev, frame, op_ret, op_errno, prebuf, postbuf, + xdata); return 0; } @@ -844,10 +1167,15 @@ out: int32_t quota_writev_helper (call_frame_t *frame, xlator_t *this, fd_t *fd, struct iovec *vector, int32_t count, off_t off, - struct iobref *iobref) + uint32_t flags, struct iobref *iobref, dict_t *xdata) { - quota_local_t *local = NULL; - int32_t op_errno = EINVAL; + quota_local_t *local = NULL; + int32_t op_errno = EINVAL; + quota_priv_t *priv = NULL; + struct iovec *new_vector = NULL; + int32_t new_count = 0; + + priv = this->private; local = frame->local; if (local == NULL) { @@ -857,16 +1185,42 @@ quota_writev_helper (call_frame_t *frame, xlator_t *this, fd_t *fd, if (local->op_ret == -1) { op_errno = local->op_errno; - goto unwind; + + if ((op_errno == EDQUOT) && (local->space_available > 0)) { + new_count = iov_subset (vector, count, 0, + local->space_available, NULL); + + new_vector = GF_CALLOC (new_count, + sizeof (struct iovec), + gf_common_mt_iovec); + if (new_vector == NULL) { + local->op_ret = -1; + local->op_errno = ENOMEM; + goto unwind; + } + + new_count = iov_subset (vector, count, 0, + local->space_available, + new_vector); + + vector = new_vector; + count = new_count; + } else { + goto unwind; + } } - STACK_WIND (frame, quota_writev_cbk, FIRST_CHILD(this), - FIRST_CHILD(this)->fops->writev, fd, vector, count, off, - iobref); + STACK_WIND (frame, quota_writev_cbk, + FIRST_CHILD(this), FIRST_CHILD(this)->fops->writev, fd, + vector, count, off, flags, iobref, xdata); + + if (new_vector != NULL) + GF_FREE (new_vector); + return 0; unwind: - QUOTA_STACK_UNWIND (writev, frame, -1, op_errno, NULL, NULL); + QUOTA_STACK_UNWIND (writev, frame, -1, op_errno, NULL, NULL, NULL); return 0; } @@ -874,16 +1228,23 @@ unwind: int32_t quota_writev (call_frame_t *frame, xlator_t *this, fd_t *fd, struct iovec *vector, int32_t count, off_t off, - struct iobref *iobref) + uint32_t flags, struct iobref *iobref, dict_t *xdata) { + quota_priv_t *priv = NULL; int32_t ret = -1, op_errno = EINVAL; int32_t parents = 0; uint64_t size = 0; quota_local_t *local = NULL; quota_inode_ctx_t *ctx = NULL; - quota_priv_t *priv = NULL; + quota_dentry_t *dentry = NULL, *tmp = NULL; call_stub_t *stub = NULL; - quota_dentry_t *dentry = NULL; + struct list_head head = {0, }; + + priv = this->private; + + WIND_IF_QUOTAOFF (priv->is_quota_on, off); + + INIT_LIST_HEAD (&head); GF_ASSERT (frame); GF_VALIDATE_OR_GOTO ("quota", this, unwind); @@ -897,17 +1258,17 @@ quota_writev (call_frame_t *frame, xlator_t *this, fd_t *fd, frame->local = local; local->loc.inode = inode_ref (fd->inode); - ret = quota_inode_ctx_get (fd->inode, -1, this, NULL, NULL, &ctx, 0); + ret = quota_inode_ctx_get (fd->inode, this, &ctx, 0); if (ctx == NULL) { - gf_log (this->name, GF_LOG_WARNING, - "quota context not set in inode (ino:%"PRId64", " - "gfid:%s)", fd->inode->ino, + gf_log (this->name, GF_LOG_DEBUG, "quota context is NULL on " + "inode (%s). " + "If quota is not enabled recently and crawler has " + "finished crawling, its an error", uuid_utoa (fd->inode->gfid)); - goto unwind; } stub = fop_writev_stub (frame, quota_writev_helper, fd, vector, count, - off, iobref); + off, flags, iobref, xdata); if (stub == NULL) { op_errno = ENOMEM; goto unwind; @@ -917,46 +1278,50 @@ quota_writev (call_frame_t *frame, xlator_t *this, fd_t *fd, GF_VALIDATE_OR_GOTO (this->name, priv, unwind); size = iov_length (vector, count); - LOCK (&ctx->lock); - { - list_for_each_entry (dentry, &ctx->parents, next) { - parents++; - } - } - UNLOCK (&ctx->lock); - - local->delta = size; - local->stub = stub; - local->link_count = parents; - - list_for_each_entry (dentry, &ctx->parents, next) { - ret = quota_check_limit (frame, fd->inode, this, dentry->name, - dentry->par); - if (ret == -1) { - break; + if (ctx != NULL) { + LOCK (&ctx->lock); + { + list_for_each_entry (dentry, &ctx->parents, next) { + tmp = __quota_dentry_new (NULL, dentry->name, + dentry->par); + list_add_tail (&tmp->next, &head); + parents++; + } } + UNLOCK (&ctx->lock); } - stub = NULL; - LOCK (&local->lock); { - local->link_count = 0; - if (local->validate_count == 0) { - stub = local->stub; - local->stub = NULL; - } + local->delta = size; + local->link_count = (parents != 0) ? parents : 1; + local->stub = stub; } UNLOCK (&local->lock); - if (stub != NULL) { - call_resume (stub); + if (parents == 0) { + /* nameless lookup on this inode, allow quota to reconstruct + * ancestry as part of check_limit. + */ + quota_check_limit (frame, fd->inode, this, NULL, NULL); + } else { + list_for_each_entry_safe (dentry, tmp, &head, next) { + quota_check_limit (frame, fd->inode, this, dentry->name, + dentry->par); + __quota_dentry_free (dentry); + } } return 0; unwind: - QUOTA_STACK_UNWIND (writev, frame, -1, op_errno, NULL, NULL); + QUOTA_STACK_UNWIND (writev, frame, -1, op_errno, NULL, NULL, NULL); + return 0; + +off: + STACK_WIND_TAIL (frame, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->writev, fd, + vector, count, off, flags, iobref, xdata); return 0; } @@ -965,17 +1330,17 @@ int32_t quota_mkdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, inode_t *inode, struct iatt *buf, struct iatt *preparent, - struct iatt *postparent) + struct iatt *postparent, dict_t *xdata) { QUOTA_STACK_UNWIND (mkdir, frame, op_ret, op_errno, inode, - buf, preparent, postparent); + buf, preparent, postparent, xdata); return 0; } int32_t quota_mkdir_helper (call_frame_t *frame, xlator_t *this, loc_t *loc, - mode_t mode, dict_t *params) + mode_t mode, mode_t umask, dict_t *xdata) { quota_local_t *local = NULL; int32_t op_errno = EINVAL; @@ -992,24 +1357,31 @@ quota_mkdir_helper (call_frame_t *frame, xlator_t *this, loc_t *loc, goto unwind; } - STACK_WIND (frame, quota_mkdir_cbk, FIRST_CHILD(this), - FIRST_CHILD(this)->fops->mkdir, loc, mode, params); + STACK_WIND (frame, quota_mkdir_cbk, + FIRST_CHILD(this), FIRST_CHILD(this)->fops->mkdir, loc, + mode, umask, xdata); + return 0; unwind: QUOTA_STACK_UNWIND (mkdir, frame, -1, op_errno, NULL, NULL, - NULL, NULL); + NULL, NULL, NULL); return 0; } int32_t quota_mkdir (call_frame_t *frame, xlator_t *this, loc_t *loc, mode_t mode, - dict_t *params) + mode_t umask, dict_t *xdata) { - int32_t ret = 0, op_errno = 0; - quota_local_t *local = NULL; - call_stub_t *stub = NULL; + quota_priv_t *priv = NULL; + int32_t ret = 0, op_errno = 0; + quota_local_t *local = NULL; + call_stub_t *stub = NULL; + + priv = this->private; + + WIND_IF_QUOTAOFF (priv->is_quota_on, off); local = quota_local_new (); if (local == NULL) { @@ -1019,8 +1391,6 @@ quota_mkdir (call_frame_t *frame, xlator_t *this, loc_t *loc, mode_t mode, frame->local = local; - local->link_count = 1; - ret = loc_copy (&local->loc, loc); if (ret) { op_errno = ENOMEM; @@ -1028,38 +1398,34 @@ quota_mkdir (call_frame_t *frame, xlator_t *this, loc_t *loc, mode_t mode, goto err; } - stub = fop_mkdir_stub (frame, quota_mkdir_helper, loc, mode, params); + stub = fop_mkdir_stub (frame, quota_mkdir_helper, loc, mode, umask, + xdata); if (stub == NULL) { op_errno = ENOMEM; goto err; } - local->stub = stub; - local->delta = 0; - - quota_check_limit (frame, loc->parent, this, NULL, 0); - - stub = NULL; - LOCK (&local->lock); { - if (local->validate_count == 0) { - stub = local->stub; - local->stub = NULL; - } - - local->link_count = 0; + local->stub = stub; + local->delta = 0; + local->link_count = 1; } UNLOCK (&local->lock); - if (stub != NULL) { - call_resume (stub); - } - + quota_check_limit (frame, loc->parent, this, NULL, NULL); return 0; + err: QUOTA_STACK_UNWIND (mkdir, frame, -1, op_errno, NULL, NULL, NULL, - NULL); + NULL, NULL); + + return 0; + +off: + STACK_WIND_TAIL (frame, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->mkdir, + loc, mode, umask, xdata); return 0; } @@ -1069,7 +1435,7 @@ int32_t 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 iatt *buf, struct iatt *preparent, - struct iatt *postparent) + struct iatt *postparent, dict_t *xdata) { int32_t ret = -1; quota_local_t *local = NULL; @@ -1081,11 +1447,11 @@ quota_create_cbk (call_frame_t *frame, void *cookie, xlator_t *this, goto unwind; } - ret = quota_inode_ctx_get (inode, -1, this, NULL, buf, &ctx, 1); + ret = quota_inode_ctx_get (inode, this, &ctx, 1); if ((ret == -1) || (ctx == NULL)) { gf_log (this->name, GF_LOG_WARNING, "cannot create quota " - "context in inode(ino:%"PRId64", gfid:%s)", - inode->ino, uuid_utoa (inode->gfid)); + "context in inode(gfid:%s)", + uuid_utoa (inode->gfid)); op_ret = -1; op_errno = ENOMEM; goto unwind; @@ -1096,13 +1462,11 @@ quota_create_cbk (call_frame_t *frame, void *cookie, xlator_t *this, ctx->buf = *buf; dentry = __quota_dentry_new (ctx, (char *)local->loc.name, - local->loc.parent->ino); + local->loc.parent->gfid); if (dentry == NULL) { gf_log (this->name, GF_LOG_WARNING, - "cannot create a new dentry (par:%" - PRId64", name:%s) for inode(ino:%" - PRId64", gfid:%s)", local->loc.parent->ino, - local->loc.name, local->loc.inode->ino, + "cannot create a new dentry (name:%s) for " + "inode(gfid:%s)", local->loc.name, uuid_utoa (local->loc.inode->gfid)); op_ret = -1; op_errno = ENOMEM; @@ -1114,19 +1478,24 @@ unlock: unwind: QUOTA_STACK_UNWIND (create, frame, op_ret, op_errno, fd, inode, buf, - preparent, postparent); + preparent, postparent, xdata); return 0; } int32_t quota_create_helper (call_frame_t *frame, xlator_t *this, loc_t *loc, - int32_t flags, mode_t mode, fd_t *fd, dict_t *params) + int32_t flags, mode_t mode, mode_t umask, fd_t *fd, + dict_t *xdata) { quota_local_t *local = NULL; int32_t op_errno = EINVAL; + quota_priv_t *priv = NULL; local = frame->local; + + priv = this->private; + if (local == NULL) { gf_log (this->name, GF_LOG_WARNING, "local is NULL"); goto unwind; @@ -1137,28 +1506,36 @@ quota_create_helper (call_frame_t *frame, xlator_t *this, loc_t *loc, goto unwind; } - STACK_WIND (frame, quota_create_cbk, FIRST_CHILD(this), - FIRST_CHILD(this)->fops->create, loc, flags, mode, fd, - params); + + STACK_WIND (frame, quota_create_cbk, + FIRST_CHILD (this), FIRST_CHILD (this)->fops->create, loc, + flags, mode, umask, fd, xdata); return 0; unwind: QUOTA_STACK_UNWIND (create, frame, -1, op_errno, NULL, NULL, - NULL, NULL, NULL); + NULL, NULL, NULL, NULL); return 0; } int32_t quota_create (call_frame_t *frame, xlator_t *this, loc_t *loc, int32_t flags, - mode_t mode, fd_t *fd, dict_t *params) + mode_t mode, mode_t umask, fd_t *fd, dict_t *xdata) { - int32_t ret = -1; - quota_local_t *local = NULL; - call_stub_t *stub = NULL; + quota_priv_t *priv = NULL; + int32_t ret = -1; + quota_local_t *local = NULL; + int32_t op_errno = 0; + call_stub_t *stub = NULL; + + priv = this->private; + + WIND_IF_QUOTAOFF (priv->is_quota_on, off); local = quota_local_new (); if (local == NULL) { + op_errno = ENOMEM; goto err; } @@ -1167,42 +1544,36 @@ quota_create (call_frame_t *frame, xlator_t *this, loc_t *loc, int32_t flags, ret = loc_copy (&local->loc, loc); if (ret) { gf_log (this->name, GF_LOG_WARNING, "loc_copy failed"); + op_errno = ENOMEM; goto err; } stub = fop_create_stub (frame, quota_create_helper, loc, flags, mode, - fd, params); + umask, fd, xdata); if (stub == NULL) { goto err; } - local->link_count = 1; - local->stub = stub; - local->delta = 0; - - quota_check_limit (frame, loc->parent, this, NULL, 0); - - stub = NULL; - LOCK (&local->lock); { - local->link_count = 0; - if (local->validate_count == 0) { - stub = local->stub; - local->stub = NULL; - } + local->link_count = 1; + local->stub = stub; + local->delta = 0; } UNLOCK (&local->lock); - if (stub != NULL) { - call_resume (stub); - } - + quota_check_limit (frame, loc->parent, this, NULL, NULL); return 0; err: - QUOTA_STACK_UNWIND (create, frame, -1, ENOMEM, NULL, NULL, NULL, NULL, - NULL); + QUOTA_STACK_UNWIND (create, frame, -1, op_errno, NULL, NULL, NULL, + NULL, NULL, NULL); + + return 0; +off: + STACK_WIND_TAIL (frame, FIRST_CHILD (this), + FIRST_CHILD (this)->fops->create, loc, + flags, mode, umask, fd, xdata); return 0; } @@ -1210,11 +1581,13 @@ err: int32_t quota_unlink_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, struct iatt *preparent, - struct iatt *postparent) + struct iatt *postparent, dict_t *xdata) { quota_local_t *local = NULL; quota_inode_ctx_t *ctx = NULL; uint64_t value = 0; + quota_dentry_t *dentry = NULL; + quota_dentry_t *old_dentry = NULL; if (op_ret < 0) { goto out; @@ -1227,29 +1600,45 @@ quota_unlink_cbk (call_frame_t *frame, void *cookie, xlator_t *this, if (ctx == NULL) { gf_log (this->name, GF_LOG_WARNING, - "quota context not set in inode (ino:%"PRId64 - ", gfid:%s)", local->loc.inode->ino, + "quota context not set in inode (gfid:%s)", uuid_utoa (local->loc.inode->gfid)); goto out; } - quota_update_size (this, local->loc.inode, (char *)local->loc.name, - local->loc.parent->ino, - (-(ctx->buf.ia_blocks * 512))); + LOCK (&ctx->lock); + { + list_for_each_entry (dentry, &ctx->parents, next) { + if ((strcmp (dentry->name, local->loc.name) == 0) && + (uuid_compare (local->loc.parent->gfid, + dentry->par) == 0)) { + old_dentry = dentry; + break; + } + } + if (old_dentry) + __quota_dentry_free (old_dentry); + } + UNLOCK (&ctx->lock); out: QUOTA_STACK_UNWIND (unlink, frame, op_ret, op_errno, preparent, - postparent); + postparent, xdata); return 0; } int32_t -quota_unlink (call_frame_t *frame, xlator_t *this, loc_t *loc) +quota_unlink (call_frame_t *frame, xlator_t *this, loc_t *loc, int xflag, + dict_t *xdata) { - int32_t ret = 0; + quota_priv_t *priv = NULL; + int32_t ret = -1; quota_local_t *local = NULL; + priv = this->private; + + WIND_IF_QUOTAOFF (priv->is_quota_on, off); + local = quota_local_new (); if (local == NULL) { goto err; @@ -1257,6 +1646,10 @@ quota_unlink (call_frame_t *frame, xlator_t *this, loc_t *loc) frame->local = local; + if (xdata && dict_get (xdata, GLUSTERFS_INTERNAL_FOP_KEY)) { + local->skip_check = _gf_true; + } + ret = loc_copy (&local->loc, loc); if (ret) { gf_log (this->name, GF_LOG_WARNING, "loc_copy failed"); @@ -1264,16 +1657,21 @@ quota_unlink (call_frame_t *frame, xlator_t *this, loc_t *loc) } STACK_WIND (frame, quota_unlink_cbk, FIRST_CHILD(this), - FIRST_CHILD(this)->fops->unlink, loc); + FIRST_CHILD(this)->fops->unlink, loc, xflag, xdata); ret = 0; err: if (ret == -1) { - QUOTA_STACK_UNWIND (unlink, frame, -1, 0, NULL, NULL); + QUOTA_STACK_UNWIND (unlink, frame, -1, 0, NULL, NULL, NULL); } return 0; + +off: + STACK_WIND_TAIL (frame, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->unlink, loc, xflag, xdata); + return 0; } @@ -1281,7 +1679,7 @@ int32_t quota_link_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, inode_t *inode, struct iatt *buf, struct iatt *preparent, - struct iatt *postparent) + struct iatt *postparent, dict_t *xdata) { int32_t ret = -1; quota_local_t *local = NULL; @@ -1295,31 +1693,30 @@ quota_link_cbk (call_frame_t *frame, void *cookie, xlator_t *this, local = (quota_local_t *) frame->local; - quota_update_size (this, local->loc.parent, NULL, 0, - (buf->ia_blocks * 512)); + if (local->skip_check) + goto out; - ret = quota_inode_ctx_get (inode, -1, this, NULL, NULL, &ctx, 0); + ret = quota_inode_ctx_get (inode, this, &ctx, 0); if ((ret == -1) || (ctx == NULL)) { - gf_log (this->name, GF_LOG_WARNING, "cannot find quota " - "context in inode(ino:%"PRId64", gfid:%s)", - inode->ino, uuid_utoa (inode->gfid)); - op_ret = -1; - op_errno = EINVAL; + gf_log (this->name, GF_LOG_DEBUG, "quota context is NULL on " + "inode (%s). " + "If quota is not enabled recently and crawler has " + "finished crawling, its an error", + uuid_utoa (inode->gfid)); goto out; } LOCK (&ctx->lock); { list_for_each_entry (dentry, &ctx->parents, next) { - if ((strcmp (dentry->name, local->loc.name) == 0) - && (local->loc.parent->ino == dentry->par)) { + if ((strcmp (dentry->name, local->loc.name) == 0) && + (uuid_compare (local->loc.parent->gfid, + dentry->par) == 0)) { found = 1; gf_log (this->name, GF_LOG_WARNING, - "new entry being linked (par:%" - PRId64", name:%s) for inode (ino:%" - PRId64", gfid:%s) is already present " - "in inode-dentry-list", dentry->par, - dentry->name, local->loc.inode->ino, + "new entry being linked (name:%s) for " + "inode (gfid:%s) is already present " + "in inode-dentry-list", dentry->name, uuid_utoa (local->loc.inode->gfid)); break; } @@ -1328,15 +1725,11 @@ quota_link_cbk (call_frame_t *frame, void *cookie, xlator_t *this, if (!found) { dentry = __quota_dentry_new (ctx, (char *)local->loc.name, - local->loc.parent->ino); + local->loc.parent->gfid); if (dentry == NULL) { gf_log (this->name, GF_LOG_WARNING, - "cannot create a new dentry (par:%" - PRId64", name:%s) for inode(ino:%" - PRId64", gfid:%s)", - local->loc.parent->ino, - local->loc.name, - local->loc.inode->ino, + "cannot create a new dentry (name:%s) " + "for inode(gfid:%s)", local->loc.name, uuid_utoa (local->loc.inode->gfid)); op_ret = -1; op_errno = ENOMEM; @@ -1351,7 +1744,7 @@ unlock: out: QUOTA_STACK_UNWIND (link, frame, op_ret, op_errno, inode, buf, - preparent, postparent); + preparent, postparent, xdata); return 0; } @@ -1359,10 +1752,13 @@ out: int32_t quota_link_helper (call_frame_t *frame, xlator_t *this, loc_t *oldloc, - loc_t *newloc) + loc_t *newloc, dict_t *xdata) { quota_local_t *local = NULL; int32_t op_errno = EINVAL; + quota_priv_t *priv = NULL; + + priv = this->private; local = frame->local; if (local == NULL) { @@ -1376,24 +1772,44 @@ quota_link_helper (call_frame_t *frame, xlator_t *this, loc_t *oldloc, goto unwind; } - STACK_WIND (frame, quota_link_cbk, FIRST_CHILD(this), - FIRST_CHILD(this)->fops->link, oldloc, newloc); + STACK_WIND (frame, quota_link_cbk, + FIRST_CHILD(this), FIRST_CHILD(this)->fops->link, oldloc, + newloc, xdata); return 0; unwind: QUOTA_STACK_UNWIND (link, frame, -1, op_errno, NULL, NULL, - NULL, NULL); + NULL, NULL, NULL); return 0; } int32_t -quota_link (call_frame_t *frame, xlator_t *this, loc_t *oldloc, loc_t *newloc) +quota_link (call_frame_t *frame, xlator_t *this, loc_t *oldloc, loc_t *newloc, + dict_t *xdata) { + quota_priv_t *priv = NULL; int32_t ret = -1, op_errno = ENOMEM; quota_local_t *local = NULL; + quota_inode_ctx_t *ctx = NULL; call_stub_t *stub = NULL; - quota_inode_ctx_t *ctx = NULL; + + priv = this->private; + + WIND_IF_QUOTAOFF (priv->is_quota_on, off); + + if (xdata && dict_get (xdata, GLUSTERFS_INTERNAL_FOP_KEY)) { + goto off; + } + + quota_inode_ctx_get (oldloc->inode, this, &ctx, 0); + if (ctx == NULL) { + gf_log (this->name, GF_LOG_DEBUG, "quota context is NULL on " + "inode (%s). " + "If quota is not enabled recently and crawler has " + "finished crawling, its an error", + uuid_utoa (oldloc->inode->gfid)); + } local = quota_local_new (); if (local == NULL) { @@ -1402,59 +1818,38 @@ quota_link (call_frame_t *frame, xlator_t *this, loc_t *oldloc, loc_t *newloc) frame->local = (void *) local; + ret = loc_copy (&local->loc, newloc); if (ret == -1) { gf_log (this->name, GF_LOG_WARNING, "loc_copy failed"); goto err; } - stub = fop_link_stub (frame, quota_link_helper, oldloc, newloc); + stub = fop_link_stub (frame, quota_link_helper, oldloc, newloc, xdata); if (stub == NULL) { goto err; } - local->link_count = 1; - local->stub = stub; - - ret = quota_inode_ctx_get (oldloc->inode, -1, this, NULL, NULL, &ctx, - 0); - if (ctx == NULL) { - gf_log (this->name, GF_LOG_WARNING, - "quota context not set in inode (ino:%"PRId64 - ", gfid:%s)", oldloc->inode?oldloc->inode->ino:0, - oldloc->inode?uuid_utoa (oldloc->inode->gfid):"0"); - op_errno = EINVAL; - goto err; - } - - local->delta = ctx->buf.ia_blocks * 512; - - quota_check_limit (frame, newloc->parent, this, NULL, 0); - - stub = NULL; - LOCK (&local->lock); { - if (local->validate_count == 0) { - stub = local->stub; - local->stub = NULL; - } - - local->link_count = 0; + local->link_count = 1; + local->stub = stub; + local->delta = (ctx != NULL) ? ctx->buf.ia_blocks * 512 : 0; } UNLOCK (&local->lock); - if (stub != NULL) { - call_resume (stub); - } + quota_check_limit (frame, newloc->parent, this, NULL, NULL); + return 0; - ret = 0; err: - if (ret < 0) { - QUOTA_STACK_UNWIND (link, frame, -1, op_errno, NULL, NULL, - NULL, NULL); - } + QUOTA_STACK_UNWIND (link, frame, -1, op_errno, NULL, NULL, + NULL, NULL, NULL); + return 0; +off: + STACK_WIND_TAIL (frame, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->link, oldloc, + newloc, xdata); return 0; } @@ -1463,14 +1858,15 @@ int32_t quota_rename_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, struct iatt *buf, struct iatt *preoldparent, struct iatt *postoldparent, - struct iatt *prenewparent, struct iatt *postnewparent) + struct iatt *prenewparent, struct iatt *postnewparent, + dict_t *xdata) { int32_t ret = -1; + int64_t size = 0; quota_local_t *local = NULL; quota_inode_ctx_t *ctx = NULL; quota_dentry_t *old_dentry = NULL, *dentry = NULL; char new_dentry_found = 0; - int64_t size = 0; if (op_ret < 0) { goto out; @@ -1484,28 +1880,22 @@ quota_rename_cbk (call_frame_t *frame, void *cookie, xlator_t *this, goto out; } - if (IA_ISREG (local->oldloc.inode->ia_type) - || IA_ISLNK (local->oldloc.inode->ia_type)) { + if (QUOTA_REG_OR_LNK_FILE (local->oldloc.inode->ia_type)) { size = buf->ia_blocks * 512; } - quota_update_size (this, local->oldloc.parent, NULL, 0, (-size)); - quota_update_size (this, local->newloc.parent, NULL, 0, size); - - if (!(IA_ISREG (local->oldloc.inode->ia_type) - || IA_ISLNK (local->oldloc.inode->ia_type))) { + if (!QUOTA_REG_OR_LNK_FILE (local->oldloc.inode->ia_type)) { goto out; } - ret = quota_inode_ctx_get (local->oldloc.inode, -1, this, NULL, NULL, - &ctx, 0); + ret = quota_inode_ctx_get (local->oldloc.inode, this, &ctx, 0); if ((ret == -1) || (ctx == NULL)) { - gf_log (this->name, GF_LOG_WARNING, "quota context not" - "set in inode(ino:%"PRId64", gfid:%s)", - local->oldloc.inode->ino, + gf_log (this->name, GF_LOG_DEBUG, "quota context is NULL on " + "inode (%s). " + "If quota is not enabled recently and crawler has " + "finished crawling, its an error", uuid_utoa (local->oldloc.inode->gfid)); - op_ret = -1; - op_errno = EINVAL; + goto out; } @@ -1518,19 +1908,19 @@ quota_rename_cbk (call_frame_t *frame, void *cookie, xlator_t *this, * should be changed to set a new context in newloc->inode. */ list_for_each_entry (dentry, &ctx->parents, next) { - if ((strcmp (dentry->name, local->oldloc.name) == 0) - && (local->oldloc.parent->ino == dentry->par)) { + if ((strcmp (dentry->name, local->oldloc.name) == 0) && + (uuid_compare (local->oldloc.parent->gfid, + dentry->par) == 0)) { old_dentry = dentry; - } else if ((strcmp (dentry->name, local->newloc.name) - == 0) && (local->oldloc.parent->ino - == dentry->par)) { + } else if ((strcmp (dentry->name, + local->newloc.name) == 0) && + (uuid_compare (local->oldloc.parent->gfid, + dentry->par) == 0)) { new_dentry_found = 1; gf_log (this->name, GF_LOG_WARNING, - "new entry being linked (par:%" - PRId64", name:%s) for inode (ino:%" - PRId64", gfid:%s) is already present " - "in inode-dentry-list", dentry->par, - dentry->name, local->newloc.inode->ino, + "new entry being linked (name:%s) for " + "inode (gfid:%s) is already present " + "in inode-dentry-list", dentry->name, uuid_utoa (local->newloc.inode->gfid)); break; } @@ -1541,22 +1931,18 @@ quota_rename_cbk (call_frame_t *frame, void *cookie, xlator_t *this, } else { gf_log (this->name, GF_LOG_WARNING, "dentry corresponding to the path just renamed " - "(par:%"PRId64", name:%s) is not present", - local->oldloc.inode->ino, local->oldloc.name); + "(name:%s) is not present", local->oldloc.name); } if (!new_dentry_found) { dentry = __quota_dentry_new (ctx, (char *)local->newloc.name, - local->newloc.parent->ino); + local->newloc.parent->gfid); if (dentry == NULL) { gf_log (this->name, GF_LOG_WARNING, - "cannot create a new dentry (par:%" - PRId64", name:%s) for inode(ino:%" - PRId64", gfid:%s)", - local->newloc.parent->ino, + "cannot create a new dentry (name:%s) " + "for inode(gfid:%s)", local->newloc.name, - local->newloc.inode->ino, uuid_utoa (local->newloc.inode->gfid)); op_ret = -1; op_errno = ENOMEM; @@ -1571,7 +1957,7 @@ unlock: out: QUOTA_STACK_UNWIND (rename, frame, op_ret, op_errno, buf, preoldparent, - postoldparent, prenewparent, postnewparent); + postoldparent, prenewparent, postnewparent, xdata); return 0; } @@ -1579,10 +1965,13 @@ out: int32_t quota_rename_helper (call_frame_t *frame, xlator_t *this, loc_t *oldloc, - loc_t *newloc) + loc_t *newloc, dict_t *xdata) { quota_local_t *local = NULL; int32_t op_errno = EINVAL; + quota_priv_t *priv = NULL; + + priv = this->private; local = frame->local; if (local == NULL) { @@ -1596,25 +1985,72 @@ quota_rename_helper (call_frame_t *frame, xlator_t *this, loc_t *oldloc, goto unwind; } - STACK_WIND (frame, quota_rename_cbk, FIRST_CHILD(this), - FIRST_CHILD(this)->fops->rename, oldloc, newloc); + STACK_WIND (frame, quota_rename_cbk, + FIRST_CHILD(this), FIRST_CHILD(this)->fops->rename, oldloc, + newloc, xdata); + return 0; unwind: QUOTA_STACK_UNWIND (rename, frame, -1, op_errno, NULL, NULL, - NULL, NULL, NULL); + NULL, NULL, NULL, NULL); return 0; } +static int32_t +quota_rename_get_size_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, inode_t *inode, + struct iatt *buf, dict_t *xdata, + struct iatt *postparent) +{ + quota_local_t *local = NULL; + int32_t ret = 0; + int64_t *size = 0; + + GF_ASSERT (frame); + GF_VALIDATE_OR_GOTO_WITH_ERROR ("quota", this, out, op_errno, + EINVAL); + GF_VALIDATE_OR_GOTO_WITH_ERROR (this->name, xdata, out, op_errno, + EINVAL); + local = frame->local; + GF_ASSERT (local); + local->link_count = 1; + + if (op_ret < 0) + goto out; + + + ret = dict_get_bin (xdata, QUOTA_SIZE_KEY, (void **) &size); + if (ret < 0) { + gf_log (this->name, GF_LOG_WARNING, + "size key not present in dict"); + op_errno = EINVAL; + goto out; + } + local->delta = ntoh64 (*size); + quota_check_limit (frame, local->newloc.parent, this, + NULL, NULL); + return 0; + +out: + quota_handle_validate_error (local, -1, op_errno); + return 0; +} + int32_t quota_rename (call_frame_t *frame, xlator_t *this, loc_t *oldloc, - loc_t *newloc) + loc_t *newloc, dict_t *xdata) { - int32_t ret = -1, op_errno = ENOMEM; - quota_local_t *local = NULL; - call_stub_t *stub = NULL; - quota_inode_ctx_t *ctx = NULL; + quota_priv_t *priv = NULL; + int32_t ret = -1, op_errno = ENOMEM; + quota_local_t *local = NULL; + quota_inode_ctx_t *ctx = NULL; + call_stub_t *stub = NULL; + + priv = this->private; + + WIND_IF_QUOTAOFF (priv->is_quota_on, off); local = quota_local_new (); if (local == NULL) { @@ -1635,58 +2071,69 @@ quota_rename (call_frame_t *frame, xlator_t *this, loc_t *oldloc, goto err; } - stub = fop_rename_stub (frame, quota_rename_helper, oldloc, newloc); + stub = fop_rename_stub (frame, quota_rename_helper, oldloc, newloc, + xdata); if (stub == NULL) { goto err; } - local->link_count = 1; - local->stub = stub; + LOCK (&local->lock); + { + local->link_count = 1; + local->stub = stub; + } + UNLOCK (&local->lock); - if (IA_ISREG (oldloc->inode->ia_type) - || IA_ISLNK (oldloc->inode->ia_type)) { - ret = quota_inode_ctx_get (oldloc->inode, -1, this, NULL, NULL, - &ctx, 0); + if (QUOTA_REG_OR_LNK_FILE (oldloc->inode->ia_type)) { + ret = quota_inode_ctx_get (oldloc->inode, this, &ctx, 0); if (ctx == NULL) { gf_log (this->name, GF_LOG_WARNING, - "quota context not set in inode (ino:%"PRId64 - ", gfid:%s)", - oldloc->inode ? oldloc->inode->ino:0, + "quota context not set in inode (gfid:%s), " + "considering file size as zero while enforcing " + "quota on new ancestry", oldloc->inode ? uuid_utoa (oldloc->inode->gfid) - :"0"); - op_errno = EINVAL; - goto err; - } - local->delta = ctx->buf.ia_blocks * 512; - } else { - local->delta = 0; - } + : "0"); + local->delta = 0; + + } else { - quota_check_limit (frame, newloc->parent, this, NULL, 0); + /* FIXME: We need to account for the size occupied by this + * inode on the target directory. To avoid double + * accounting, we need to modify enforcer to perform + * quota_check_limit only uptil the least common ancestor + * directory inode*/ - stub = NULL; + /* FIXME: The following code assumes that regular files and + *linkfiles are present, in their entirety, in a single + brick. This *assumption is invalid in the case of + stripe.*/ - LOCK (&local->lock); - { - if (local->validate_count == 0) { - stub = local->stub; - local->stub = NULL; + local->delta = ctx->buf.ia_blocks * 512; } - local->link_count = 0; - } - UNLOCK (&local->lock); + } else if (IA_ISDIR (oldloc->inode->ia_type)) { + ret = quota_validate (frame, oldloc->inode, this, + quota_rename_get_size_cbk); + if (ret){ + op_errno = -ret; + goto err; + } - if (stub != NULL) { - call_resume (stub); + return 0; } - ret = 0; + quota_check_limit (frame, newloc->parent, this, NULL, NULL); + return 0; + err: - if (ret == -1) { - QUOTA_STACK_UNWIND (rename, frame, -1, op_errno, NULL, - NULL, NULL, NULL, NULL); - } + QUOTA_STACK_UNWIND (rename, frame, -1, op_errno, NULL, + NULL, NULL, NULL, NULL, NULL); + return 0; + +off: + STACK_WIND_TAIL (frame, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->rename, oldloc, + newloc, xdata); return 0; } @@ -1696,9 +2143,8 @@ int32_t quota_symlink_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, inode_t *inode, struct iatt *buf, struct iatt *preparent, - struct iatt *postparent) + struct iatt *postparent, dict_t *xdata) { - int64_t size = 0; quota_local_t *local = NULL; quota_inode_ctx_t *ctx = NULL; quota_dentry_t *dentry = NULL; @@ -1708,17 +2154,15 @@ quota_symlink_cbk (call_frame_t *frame, void *cookie, xlator_t *this, } local = frame->local; - size = buf->ia_blocks * 512; - - quota_update_size (this, local->loc.parent, NULL, 0, size); - quota_inode_ctx_get (local->loc.inode, -1, this, NULL, NULL, - &ctx, 1); + quota_inode_ctx_get (local->loc.inode, this, &ctx, 1); if (ctx == NULL) { - gf_log (this->name, GF_LOG_WARNING, - "quota context not set in inode (ino:%"PRId64 - ", gfid:%s)", local->loc.inode->ino, + gf_log (this->name, GF_LOG_DEBUG, "quota context is NULL on " + "inode (%s). " + "If quota is not enabled recently and crawler has " + "finished crawling, its an error", uuid_utoa (local->loc.inode->gfid)); + goto out; } @@ -1727,13 +2171,11 @@ quota_symlink_cbk (call_frame_t *frame, void *cookie, xlator_t *this, ctx->buf = *buf; dentry = __quota_dentry_new (ctx, (char *)local->loc.name, - local->loc.parent->ino); + local->loc.parent->gfid); if (dentry == NULL) { gf_log (this->name, GF_LOG_WARNING, - "cannot create a new dentry (par:%" - PRId64", name:%s) for inode(ino:%" - PRId64", gfid:%s)", local->loc.parent->ino, - local->loc.name, local->loc.inode->ino, + "cannot create a new dentry (name:%s) for " + "inode(gfid:%s)", local->loc.name, uuid_utoa (local->loc.inode->gfid)); op_ret = -1; op_errno = ENOMEM; @@ -1743,7 +2185,7 @@ quota_symlink_cbk (call_frame_t *frame, void *cookie, xlator_t *this, out: QUOTA_STACK_UNWIND (symlink, frame, op_ret, op_errno, inode, buf, - preparent, postparent); + preparent, postparent, xdata); return 0; } @@ -1751,10 +2193,11 @@ out: int quota_symlink_helper (call_frame_t *frame, xlator_t *this, const char *linkpath, - loc_t *loc, dict_t *params) + loc_t *loc, mode_t umask, dict_t *xdata) { quota_local_t *local = NULL; int32_t op_errno = EINVAL; + quota_priv_t *priv = NULL; local = frame->local; if (local == NULL) { @@ -1762,30 +2205,38 @@ quota_symlink_helper (call_frame_t *frame, xlator_t *this, const char *linkpath, goto unwind; } + priv = this->private; + if (local->op_ret == -1) { op_errno = local->op_errno; goto unwind; } - STACK_WIND (frame, quota_symlink_cbk, FIRST_CHILD(this), - FIRST_CHILD(this)->fops->symlink, linkpath, loc, params); + STACK_WIND (frame, quota_symlink_cbk, + FIRST_CHILD(this), FIRST_CHILD(this)->fops->symlink, + linkpath, loc, umask, xdata); return 0; unwind: QUOTA_STACK_UNWIND (symlink, frame, -1, op_errno, NULL, NULL, - NULL, NULL); + NULL, NULL, NULL); return 0; } int quota_symlink (call_frame_t *frame, xlator_t *this, const char *linkpath, - loc_t *loc, dict_t *params) + loc_t *loc, mode_t umask, dict_t *xdata) { - int32_t ret = -1; - int32_t op_errno = ENOMEM; - quota_local_t *local = NULL; - call_stub_t *stub = NULL; + quota_priv_t *priv = NULL; + int32_t ret = -1; + int32_t op_errno = ENOMEM; + quota_local_t *local = NULL; + call_stub_t *stub = NULL; + + priv = this->private; + + WIND_IF_QUOTAOFF (priv->is_quota_on, off); local = quota_local_new (); if (local == NULL) { @@ -1800,42 +2251,33 @@ quota_symlink (call_frame_t *frame, xlator_t *this, const char *linkpath, goto err; } - local->link_count = 1; - stub = fop_symlink_stub (frame, quota_symlink_helper, linkpath, loc, - params); + umask, xdata); if (stub == NULL) { goto err; } - local->stub = stub; - local->delta = strlen (linkpath); - - quota_check_limit (frame, loc->parent, this, NULL, 0); - - stub = NULL; - LOCK (&local->lock); { - if (local->validate_count == 0) { - stub = local->stub; - local->stub = NULL; - } - - local->link_count = 0; + local->stub = stub; + local->delta = strlen (linkpath); + local->link_count = 1; } UNLOCK (&local->lock); - if (stub != NULL) { - call_resume (stub); - } - + quota_check_limit (frame, loc->parent, this, NULL, NULL); return 0; err: QUOTA_STACK_UNWIND (symlink, frame, -1, op_errno, NULL, NULL, NULL, - NULL); + NULL, NULL); + + return 0; +off: + STACK_WIND_TAIL (frame, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->symlink, + linkpath, loc, umask, xdata); return 0; } @@ -1843,10 +2285,9 @@ err: int32_t quota_truncate_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, struct iatt *prebuf, - struct iatt *postbuf) + struct iatt *postbuf, dict_t *xdata) { quota_local_t *local = NULL; - int64_t delta = 0; quota_inode_ctx_t *ctx = NULL; if (op_ret < 0) { @@ -1859,16 +2300,12 @@ quota_truncate_cbk (call_frame_t *frame, void *cookie, xlator_t *this, goto out; } - delta = (postbuf->ia_blocks - prebuf->ia_blocks) * 512; - - quota_update_size (this, local->loc.inode, NULL, 0, delta); - - quota_inode_ctx_get (local->loc.inode, -1, this, NULL, NULL, - &ctx, 0); + quota_inode_ctx_get (local->loc.inode, this, &ctx, 0); if (ctx == NULL) { - gf_log (this->name, GF_LOG_WARNING, - "quota context not set in inode (ino:%"PRId64 - ", gfid:%s)", local->loc.inode->ino, + gf_log (this->name, GF_LOG_DEBUG, "quota context is NULL on " + "inode (%s). " + "If quota is not enabled recently and crawler has " + "finished crawling, its an error", uuid_utoa (local->loc.inode->gfid)); goto out; } @@ -1881,17 +2318,23 @@ quota_truncate_cbk (call_frame_t *frame, void *cookie, xlator_t *this, out: QUOTA_STACK_UNWIND (truncate, frame, op_ret, op_errno, prebuf, - postbuf); + postbuf, xdata); return 0; } int32_t -quota_truncate (call_frame_t *frame, xlator_t *this, loc_t *loc, off_t offset) +quota_truncate (call_frame_t *frame, xlator_t *this, loc_t *loc, off_t offset, + dict_t *xdata) { + quota_priv_t *priv = NULL; int32_t ret = -1; quota_local_t *local = NULL; + priv = this->private; + + WIND_IF_QUOTAOFF (priv->is_quota_on, off); + local = quota_local_new (); if (local == NULL) { goto err; @@ -1906,23 +2349,27 @@ quota_truncate (call_frame_t *frame, xlator_t *this, loc_t *loc, off_t offset) } STACK_WIND (frame, quota_truncate_cbk, FIRST_CHILD(this), - FIRST_CHILD(this)->fops->truncate, loc, offset); + FIRST_CHILD(this)->fops->truncate, loc, offset, xdata); return 0; + err: - QUOTA_STACK_UNWIND (truncate, frame, -1, ENOMEM, NULL, NULL); + QUOTA_STACK_UNWIND (truncate, frame, -1, ENOMEM, NULL, NULL, NULL); return 0; +off: + STACK_WIND_TAIL (frame, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->truncate, loc, offset, xdata); + return 0; } int32_t quota_ftruncate_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, struct iatt *prebuf, - struct iatt *postbuf) + struct iatt *postbuf, dict_t *xdata) { quota_local_t *local = NULL; - int64_t delta = 0; quota_inode_ctx_t *ctx = NULL; if (op_ret < 0) { @@ -1935,16 +2382,12 @@ quota_ftruncate_cbk (call_frame_t *frame, void *cookie, xlator_t *this, goto out; } - delta = (postbuf->ia_blocks - prebuf->ia_blocks) * 512; - - quota_update_size (this, local->loc.inode, NULL, 0, delta); - - quota_inode_ctx_get (local->loc.inode, -1, this, NULL, NULL, - &ctx, 0); + quota_inode_ctx_get (local->loc.inode, this, &ctx, 0); if (ctx == NULL) { - gf_log (this->name, GF_LOG_WARNING, - "quota context not set in inode (ino:%"PRId64 - ", gfid:%s)", local->loc.inode->ino, + gf_log (this->name, GF_LOG_DEBUG, "quota context is NULL on " + "inode (%s). " + "If quota is not enabled recently and crawler has " + "finished crawling, its an error", uuid_utoa (local->loc.inode->gfid)); goto out; } @@ -1957,16 +2400,22 @@ quota_ftruncate_cbk (call_frame_t *frame, void *cookie, xlator_t *this, out: QUOTA_STACK_UNWIND (ftruncate, frame, op_ret, op_errno, prebuf, - postbuf); + postbuf, xdata); return 0; } int32_t -quota_ftruncate (call_frame_t *frame, xlator_t *this, fd_t *fd, off_t offset) +quota_ftruncate (call_frame_t *frame, xlator_t *this, fd_t *fd, off_t offset, + dict_t *xdata) { + quota_priv_t *priv = NULL; quota_local_t *local = NULL; + priv = this->private; + + WIND_IF_QUOTAOFF (priv->is_quota_on, off); + local = quota_local_new (); if (local == NULL) goto err; @@ -1975,14 +2424,21 @@ quota_ftruncate (call_frame_t *frame, xlator_t *this, fd_t *fd, off_t offset) local->loc.inode = inode_ref (fd->inode); - STACK_WIND (frame, quota_ftruncate_cbk, FIRST_CHILD(this), - FIRST_CHILD(this)->fops->ftruncate, fd, offset); + STACK_WIND (frame, quota_ftruncate_cbk, + FIRST_CHILD(this), FIRST_CHILD(this)->fops->ftruncate, fd, + offset, xdata); return 0; err: - QUOTA_STACK_UNWIND (ftruncate, frame, -1, ENOMEM, NULL, NULL); + QUOTA_STACK_UNWIND (ftruncate, frame, -1, ENOMEM, NULL, NULL, NULL); return 0; + +off: + STACK_WIND_TAIL (frame, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->ftruncate, fd, + offset, xdata); + return 0; } @@ -1995,14 +2451,23 @@ quota_send_dir_limit_to_cli (call_frame_t *frame, xlator_t *this, dict_t *dict = NULL; quota_inode_ctx_t *ctx = NULL; uint64_t value = 0; + quota_priv_t *priv = NULL; + + priv = this->private; + if (!priv->is_quota_on) { + snprintf (dir_limit, 1024, "Quota is disabled please turn on"); + goto dict_set; + } ret = inode_ctx_get (inode, this, &value); if (ret < 0) goto out; ctx = (quota_inode_ctx_t *)(unsigned long)value; - snprintf (dir_limit, 1024, "%"PRId64",%"PRId64, ctx->size, ctx->limit); + snprintf (dir_limit, 1024, "%"PRId64",%"PRId64, ctx->size, + ctx->hard_lim); +dict_set: dict = dict_new (); if (dict == NULL) { ret = -1; @@ -2013,9 +2478,9 @@ quota_send_dir_limit_to_cli (call_frame_t *frame, xlator_t *this, if (ret < 0) goto out; - gf_log (this->name, GF_LOG_INFO, "str = %s", dir_limit); + gf_log (this->name, GF_LOG_DEBUG, "str = %s", dir_limit); - QUOTA_STACK_UNWIND (getxattr, frame, 0, 0, dict); + QUOTA_STACK_UNWIND (getxattr, frame, 0, 0, dict, NULL); ret = 0; @@ -2026,11 +2491,11 @@ out: int32_t quota_fgetxattr (call_frame_t *frame, xlator_t *this, fd_t *fd, - const char *name) + const char *name, dict_t *xdata) { int32_t ret = 0; - if (strcasecmp (name, "trusted.limit.list") == 0) { + if (name && strcasecmp (name, "trusted.limit.list") == 0) { ret = quota_send_dir_limit_to_cli (frame, this, fd->inode, name); if (ret == 0) { @@ -2039,14 +2504,14 @@ quota_fgetxattr (call_frame_t *frame, xlator_t *this, fd_t *fd, } STACK_WIND (frame, default_fgetxattr_cbk, FIRST_CHILD(this), - FIRST_CHILD(this)->fops->fgetxattr, fd, name); + FIRST_CHILD(this)->fops->fgetxattr, fd, name, xdata); return 0; } int32_t quota_getxattr (call_frame_t *frame, xlator_t *this, loc_t *loc, - const char *name) + const char *name, dict_t *xdata) { int32_t ret = 0; @@ -2058,14 +2523,15 @@ quota_getxattr (call_frame_t *frame, xlator_t *this, loc_t *loc, } STACK_WIND (frame, default_getxattr_cbk, FIRST_CHILD(this), - FIRST_CHILD(this)->fops->getxattr, loc, name); + FIRST_CHILD(this)->fops->getxattr, loc, name, xdata); return 0; } int32_t quota_stat_cbk (call_frame_t *frame, void *cookie, xlator_t *this, - int32_t op_ret, int32_t op_errno, struct iatt *buf) + int32_t op_ret, int32_t op_errno, struct iatt *buf, + dict_t *xdata) { quota_local_t *local = NULL; quota_inode_ctx_t *ctx = NULL; @@ -2080,13 +2546,17 @@ quota_stat_cbk (call_frame_t *frame, void *cookie, xlator_t *this, goto out; } - quota_inode_ctx_get (local->loc.inode, -1, this, NULL, NULL, - &ctx, 0); + quota_inode_ctx_get (local->loc.inode, this, &ctx, 0); if (ctx == NULL) { - gf_log (this->name, GF_LOG_WARNING, - "quota context not set in inode (ino:%"PRId64 - ", gfid:%s)", local->loc.inode->ino, - uuid_utoa (local->loc.inode->gfid)); + if (!IA_ISDIR (buf->ia_type)) { + gf_log (this->name, GF_LOG_DEBUG, + "quota context is NULL on " + "inode (%s). " + "If quota is not enabled recently and crawler " + "has finished crawling, its an error", + uuid_utoa (local->loc.inode->gfid)); + } + goto out; } @@ -2098,17 +2568,22 @@ quota_stat_cbk (call_frame_t *frame, void *cookie, xlator_t *this, UNLOCK (&ctx->lock); out: - QUOTA_STACK_UNWIND (stat, frame, op_ret, op_errno, buf); + QUOTA_STACK_UNWIND (stat, frame, op_ret, op_errno, buf, xdata); return 0; } int32_t -quota_stat (call_frame_t *frame, xlator_t *this, loc_t *loc) +quota_stat (call_frame_t *frame, xlator_t *this, loc_t *loc, dict_t *xdata) { + quota_priv_t *priv = NULL; quota_local_t *local = NULL; int32_t ret = -1; + priv = this->private; + + WIND_IF_QUOTAOFF (priv->is_quota_on, off); + local = quota_local_new (); if (local == NULL) { goto unwind; @@ -2122,18 +2597,26 @@ quota_stat (call_frame_t *frame, xlator_t *this, loc_t *loc) } STACK_WIND (frame, quota_stat_cbk, FIRST_CHILD(this), - FIRST_CHILD(this)->fops->stat, loc); + FIRST_CHILD(this)->fops->stat, loc, + xdata); return 0; unwind: - QUOTA_STACK_UNWIND (stat, frame, -1, ENOMEM, NULL); + QUOTA_STACK_UNWIND (stat, frame, -1, ENOMEM, NULL, NULL); + return 0; + +off: + STACK_WIND_TAIL (frame, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->stat, loc, + xdata); return 0; } int32_t quota_fstat_cbk (call_frame_t *frame, void *cookie, xlator_t *this, - int32_t op_ret, int32_t op_errno, struct iatt *buf) + int32_t op_ret, int32_t op_errno, struct iatt *buf, + dict_t *xdata) { quota_local_t *local = NULL; quota_inode_ctx_t *ctx = NULL; @@ -2148,13 +2631,17 @@ quota_fstat_cbk (call_frame_t *frame, void *cookie, xlator_t *this, goto out; } - quota_inode_ctx_get (local->loc.inode, -1, this, NULL, NULL, - &ctx, 0); + quota_inode_ctx_get (local->loc.inode, this, &ctx, 0); if (ctx == NULL) { - gf_log (this->name, GF_LOG_WARNING, - "quota context not set in inode (ino:%"PRId64 - ", gfid:%s)", local->loc.inode->ino, - uuid_utoa (local->loc.inode->gfid)); + if (!IA_ISDIR (buf->ia_type)) { + gf_log (this->name, GF_LOG_DEBUG, + "quota context is NULL on " + "inode (%s). " + "If quota is not enabled recently and crawler " + "has finished crawling, its an error", + uuid_utoa (local->loc.inode->gfid)); + } + goto out; } @@ -2166,16 +2653,21 @@ quota_fstat_cbk (call_frame_t *frame, void *cookie, xlator_t *this, UNLOCK (&ctx->lock); out: - QUOTA_STACK_UNWIND (fstat, frame, op_ret, op_errno, buf); + QUOTA_STACK_UNWIND (fstat, frame, op_ret, op_errno, buf, xdata); return 0; } int32_t -quota_fstat (call_frame_t *frame, xlator_t *this, fd_t *fd) +quota_fstat (call_frame_t *frame, xlator_t *this, fd_t *fd, dict_t *xdata) { + quota_priv_t *priv = NULL; quota_local_t *local = NULL; + priv = this->private; + + WIND_IF_QUOTAOFF (priv->is_quota_on, off); + local = quota_local_new (); if (local == NULL) { goto unwind; @@ -2185,12 +2677,19 @@ quota_fstat (call_frame_t *frame, xlator_t *this, fd_t *fd) local->loc.inode = inode_ref (fd->inode); - STACK_WIND (frame, quota_fstat_cbk, FIRST_CHILD(this), - FIRST_CHILD(this)->fops->fstat, fd); + STACK_WIND (frame, quota_fstat_cbk, + FIRST_CHILD(this), FIRST_CHILD(this)->fops->fstat, fd, + xdata); return 0; unwind: - QUOTA_STACK_UNWIND (fstat, frame, -1, ENOMEM, NULL); + QUOTA_STACK_UNWIND (fstat, frame, -1, ENOMEM, NULL, NULL); + return 0; + +off: + STACK_WIND_TAIL (frame, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->fstat, fd, + xdata); return 0; } @@ -2198,7 +2697,7 @@ unwind: int32_t quota_readlink_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, const char *path, - struct iatt *buf) + struct iatt *buf, dict_t *xdata) { quota_local_t *local = NULL; quota_inode_ctx_t *ctx = NULL; @@ -2213,12 +2712,12 @@ quota_readlink_cbk (call_frame_t *frame, void *cookie, xlator_t *this, goto out; } - quota_inode_ctx_get (local->loc.inode, -1, this, NULL, NULL, - &ctx, 0); + quota_inode_ctx_get (local->loc.inode, this, &ctx, 0); if (ctx == NULL) { - gf_log (this->name, GF_LOG_WARNING, - "quota context not set in inode (ino:%"PRId64 - ", gfid:%s)", local->loc.inode->ino, + gf_log (this->name, GF_LOG_DEBUG, "quota context is NULL on " + "inode (%s). " + "If quota is not enabled recently and crawler has " + "finished crawling, its an error", uuid_utoa (local->loc.inode->gfid)); goto out; } @@ -2230,17 +2729,24 @@ quota_readlink_cbk (call_frame_t *frame, void *cookie, xlator_t *this, UNLOCK (&ctx->lock); out: - QUOTA_STACK_UNWIND (readlink, frame, op_ret, op_errno, path, buf); + QUOTA_STACK_UNWIND (readlink, frame, op_ret, op_errno, path, buf, + xdata); return 0; } int32_t -quota_readlink (call_frame_t *frame, xlator_t *this, loc_t *loc, size_t size) +quota_readlink (call_frame_t *frame, xlator_t *this, loc_t *loc, size_t size, + dict_t *xdata) { + quota_priv_t *priv = NULL; quota_local_t *local = NULL; int32_t ret = -1; + priv = this->private; + + WIND_IF_QUOTAOFF (priv->is_quota_on, off); + local = quota_local_new (); if (local == NULL) { goto unwind; @@ -2254,12 +2760,19 @@ quota_readlink (call_frame_t *frame, xlator_t *this, loc_t *loc, size_t size) goto unwind; } - STACK_WIND (frame, quota_readlink_cbk, FIRST_CHILD(this), - FIRST_CHILD(this)->fops->readlink, loc, size); + STACK_WIND (frame, quota_readlink_cbk, + FIRST_CHILD(this), FIRST_CHILD(this)->fops->readlink, loc, + size, xdata); return 0; unwind: - QUOTA_STACK_UNWIND (readlink, frame, -1, ENOMEM, NULL, NULL); + QUOTA_STACK_UNWIND (readlink, frame, -1, ENOMEM, NULL, NULL, NULL); + return 0; + +off: + STACK_WIND_TAIL (frame, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->readlink, loc, + size, xdata); return 0; } @@ -2267,7 +2780,8 @@ unwind: int32_t quota_readv_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, struct iovec *vector, - int32_t count, struct iatt *buf, struct iobref *iobref) + int32_t count, struct iatt *buf, struct iobref *iobref, + dict_t *xdata) { quota_local_t *local = NULL; quota_inode_ctx_t *ctx = NULL; @@ -2282,12 +2796,12 @@ quota_readv_cbk (call_frame_t *frame, void *cookie, xlator_t *this, goto out; } - quota_inode_ctx_get (local->loc.inode, -1, this, NULL, NULL, - &ctx, 0); + quota_inode_ctx_get (local->loc.inode, this, &ctx, 0); if (ctx == NULL) { - gf_log (this->name, GF_LOG_WARNING, - "quota context not set in inode (ino:%"PRId64 - ", gfid:%s)", local->loc.inode->ino, + gf_log (this->name, GF_LOG_DEBUG, "quota context is NULL on " + "inode (%s). " + "If quota is not enabled recently and crawler has " + "finished crawling, its an error", uuid_utoa (local->loc.inode->gfid)); goto out; } @@ -2300,17 +2814,22 @@ quota_readv_cbk (call_frame_t *frame, void *cookie, xlator_t *this, out: QUOTA_STACK_UNWIND (readv, frame, op_ret, op_errno, vector, count, - buf, iobref); + buf, iobref, xdata); return 0; } int32_t quota_readv (call_frame_t *frame, xlator_t *this, fd_t *fd, size_t size, - off_t offset) + off_t offset, uint32_t flags, dict_t *xdata) { + quota_priv_t *priv = NULL; quota_local_t *local = NULL; + priv = this->private; + + WIND_IF_QUOTAOFF (priv->is_quota_on, off); + local = quota_local_new (); if (local == NULL) { goto unwind; @@ -2320,12 +2839,20 @@ quota_readv (call_frame_t *frame, xlator_t *this, fd_t *fd, size_t size, local->loc.inode = inode_ref (fd->inode); - STACK_WIND (frame, quota_readv_cbk, FIRST_CHILD(this), - FIRST_CHILD(this)->fops->readv, fd, size, offset); + STACK_WIND (frame, quota_readv_cbk, + FIRST_CHILD(this), FIRST_CHILD(this)->fops->readv, fd, + size, offset, flags, xdata); return 0; unwind: - QUOTA_STACK_UNWIND (readv, frame, -1, ENOMEM, NULL, -1, NULL, NULL); + QUOTA_STACK_UNWIND (readv, frame, -1, ENOMEM, NULL, -1, NULL, NULL, + NULL); + return 0; + +off: + STACK_WIND_TAIL (frame, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->readv, fd, + size, offset, flags, xdata); return 0; } @@ -2333,7 +2860,7 @@ unwind: int32_t quota_fsync_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, struct iatt *prebuf, - struct iatt *postbuf) + struct iatt *postbuf, dict_t *xdata) { quota_local_t *local = NULL; quota_inode_ctx_t *ctx = NULL; @@ -2348,12 +2875,12 @@ quota_fsync_cbk (call_frame_t *frame, void *cookie, xlator_t *this, goto out; } - quota_inode_ctx_get (local->loc.inode, -1, this, NULL, NULL, - &ctx, 0); + quota_inode_ctx_get (local->loc.inode, this, &ctx, 0); if (ctx == NULL) { - gf_log (this->name, GF_LOG_WARNING, - "quota context not set in inode (ino:%"PRId64 - ", gfid:%s)", local->loc.inode->ino, + gf_log (this->name, GF_LOG_DEBUG, "quota context is NULL on " + "inode (%s). " + "If quota is not enabled recently and crawler has " + "finished crawling, its an error", uuid_utoa (local->loc.inode->gfid)); goto out; } @@ -2365,16 +2892,23 @@ quota_fsync_cbk (call_frame_t *frame, void *cookie, xlator_t *this, UNLOCK (&ctx->lock); out: - QUOTA_STACK_UNWIND (fsync, frame, op_ret, op_errno, prebuf, postbuf); + QUOTA_STACK_UNWIND (fsync, frame, op_ret, op_errno, prebuf, postbuf, + xdata); return 0; } int32_t -quota_fsync (call_frame_t *frame, xlator_t *this, fd_t *fd, int32_t flags) +quota_fsync (call_frame_t *frame, xlator_t *this, fd_t *fd, int32_t flags, + dict_t *xdata) { + quota_priv_t *priv = NULL; quota_local_t *local = NULL; + priv = this->private; + + WIND_IF_QUOTAOFF (priv->is_quota_on, off); + local = quota_local_new (); if (local == NULL) { goto unwind; @@ -2385,20 +2919,26 @@ quota_fsync (call_frame_t *frame, xlator_t *this, fd_t *fd, int32_t flags) frame->local = local; STACK_WIND (frame, quota_fsync_cbk, FIRST_CHILD(this), - FIRST_CHILD(this)->fops->fsync, fd, flags); + FIRST_CHILD(this)->fops->fsync, fd, + flags, xdata); return 0; unwind: - QUOTA_STACK_UNWIND (fsync, frame, -1, ENOMEM, NULL, NULL); + QUOTA_STACK_UNWIND (fsync, frame, -1, ENOMEM, NULL, NULL, NULL); return 0; +off: + STACK_WIND_TAIL (frame, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->fsync, fd, + flags, xdata); + return 0; } int32_t quota_setattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, struct iatt *statpre, - struct iatt *statpost) + struct iatt *statpost, dict_t *xdata) { quota_local_t *local = NULL; quota_inode_ctx_t *ctx = NULL; @@ -2413,13 +2953,16 @@ quota_setattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this, goto out; } - quota_inode_ctx_get (local->loc.inode, -1, this, NULL, NULL, - &ctx, 0); + quota_inode_ctx_get (local->loc.inode, this, &ctx, 0); if (ctx == NULL) { - gf_log (this->name, GF_LOG_WARNING, - "quota context not set in inode (ino:%"PRId64 - ", gfid:%s)", local->loc.inode->ino, - uuid_utoa (local->loc.inode->gfid)); + if (!IA_ISDIR (statpost->ia_type)) { + gf_log (this->name, GF_LOG_DEBUG, "quota context is " + "NULL on inode (%s). " + "If quota is not enabled recently and crawler " + "has finished crawling, its an error", + uuid_utoa (local->loc.inode->gfid)); + } + goto out; } @@ -2432,18 +2975,23 @@ quota_setattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this, out: QUOTA_STACK_UNWIND (setattr, frame, op_ret, op_errno, statpre, - statpost); + statpost, xdata); return 0; } int32_t quota_setattr (call_frame_t *frame, xlator_t *this, loc_t *loc, - struct iatt *stbuf, int32_t valid) + struct iatt *stbuf, int32_t valid, dict_t *xdata) { + quota_priv_t *priv = NULL; quota_local_t *local = NULL; int32_t ret = -1; + priv = this->private; + + WIND_IF_QUOTAOFF (priv->is_quota_on, off); + local = quota_local_new (); if (local == NULL) { goto unwind; @@ -2458,11 +3006,18 @@ quota_setattr (call_frame_t *frame, xlator_t *this, loc_t *loc, } STACK_WIND (frame, quota_setattr_cbk, FIRST_CHILD (this), - FIRST_CHILD (this)->fops->setattr, loc, stbuf, valid); + FIRST_CHILD (this)->fops->setattr, loc, + stbuf, valid, xdata); return 0; unwind: - QUOTA_STACK_UNWIND (setattr, frame, -1, ENOMEM, NULL, NULL); + QUOTA_STACK_UNWIND (setattr, frame, -1, ENOMEM, NULL, NULL, NULL); + return 0; + +off: + STACK_WIND_TAIL (frame, FIRST_CHILD (this), + FIRST_CHILD (this)->fops->setattr, loc, + stbuf, valid, xdata); return 0; } @@ -2470,7 +3025,7 @@ unwind: int32_t quota_fsetattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this, int32_t op_ret, int32_t op_errno, struct iatt *statpre, - struct iatt *statpost) + struct iatt *statpost, dict_t *xdata) { quota_local_t *local = NULL; quota_inode_ctx_t *ctx = NULL; @@ -2485,13 +3040,16 @@ quota_fsetattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this, goto out; } - quota_inode_ctx_get (local->loc.inode, -1, this, NULL, NULL, - &ctx, 0); + quota_inode_ctx_get (local->loc.inode, this, &ctx, 0); if (ctx == NULL) { - gf_log (this->name, GF_LOG_WARNING, - "quota context not set in inode (ino:%"PRId64 - ", gfid:%s)", local->loc.inode->ino, - uuid_utoa (local->loc.inode->gfid)); + if (!IA_ISDIR (statpost->ia_type)) { + gf_log (this->name, GF_LOG_DEBUG, "quota context is " + "NULL on inode (%s). " + "If quota is not enabled recently and crawler " + "has finished crawling, its an error", + uuid_utoa (local->loc.inode->gfid)); + } + goto out; } @@ -2503,17 +3061,22 @@ quota_fsetattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this, out: QUOTA_STACK_UNWIND (fsetattr, frame, op_ret, op_errno, statpre, - statpost); + statpost, xdata); return 0; } int32_t quota_fsetattr (call_frame_t *frame, xlator_t *this, fd_t *fd, - struct iatt *stbuf, int32_t valid) + struct iatt *stbuf, int32_t valid, dict_t *xdata) { + quota_priv_t *priv = NULL; quota_local_t *local = NULL; + priv = this->private; + + WIND_IF_QUOTAOFF (priv->is_quota_on, off); + local = quota_local_new (); if (local == NULL) { goto unwind; @@ -2523,169 +3086,1043 @@ quota_fsetattr (call_frame_t *frame, xlator_t *this, fd_t *fd, local->loc.inode = inode_ref (fd->inode); - STACK_WIND (frame, quota_fsetattr_cbk, FIRST_CHILD (this), - FIRST_CHILD (this)->fops->fsetattr, fd, stbuf, valid); + STACK_WIND (frame, quota_fsetattr_cbk, + FIRST_CHILD (this), FIRST_CHILD (this)->fops->fsetattr, fd, + stbuf, valid, xdata); return 0; unwind: - QUOTA_STACK_UNWIND (fsetattr, frame, -1, ENOMEM, NULL, NULL); + QUOTA_STACK_UNWIND (fsetattr, frame, -1, ENOMEM, NULL, NULL, NULL); + return 0; + +off: + STACK_WIND_TAIL (frame, FIRST_CHILD (this), + FIRST_CHILD (this)->fops->fsetattr, fd, + stbuf, valid, xdata); return 0; } int32_t -mem_acct_init (xlator_t *this) +quota_mknod_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, inode_t *inode, + struct iatt *buf, struct iatt *preparent, + struct iatt *postparent, dict_t *xdata) { - int ret = -1; + int32_t ret = -1; + quota_local_t *local = NULL; + quota_inode_ctx_t *ctx = NULL; + quota_dentry_t *dentry = NULL; - if (!this) - return ret; + local = frame->local; + if (op_ret < 0) { + goto unwind; + } - ret = xlator_mem_acct_init (this, gf_quota_mt_end + 1); + ret = quota_inode_ctx_get (inode, this, &ctx, 1); + if ((ret == -1) || (ctx == NULL)) { + gf_log (this->name, GF_LOG_WARNING, "cannot create quota " + "context in inode (gfid:%s)", uuid_utoa (inode->gfid)); + op_ret = -1; + op_errno = ENOMEM; + goto unwind; + } - if (ret != 0) { - gf_log (this->name, GF_LOG_WARNING, "Memory accounting" - "init failed"); - return ret; + LOCK (&ctx->lock); + { + ctx->buf = *buf; + + dentry = __quota_dentry_new (ctx, (char *)local->loc.name, + local->loc.parent->gfid); + if (dentry == NULL) { + gf_log (this->name, GF_LOG_WARNING, + "cannot create a new dentry (name:%s) for " + "inode(gfid:%s)", local->loc.name, + uuid_utoa (local->loc.inode->gfid)); + op_ret = -1; + op_errno = ENOMEM; + goto unlock; + } } +unlock: + UNLOCK (&ctx->lock); - return ret; +unwind: + QUOTA_STACK_UNWIND (mknod, frame, op_ret, op_errno, inode, + buf, preparent, postparent, xdata); + return 0; } -int32_t -quota_forget (xlator_t *this, inode_t *inode) +int +quota_mknod_helper (call_frame_t *frame, xlator_t *this, loc_t *loc, + mode_t mode, dev_t rdev, mode_t umask, dict_t *xdata) { - int32_t ret = 0; - uint64_t ctx_int = 0; - quota_inode_ctx_t *ctx = NULL; - quota_dentry_t *dentry = NULL, *tmp; + quota_local_t *local = NULL; + int32_t op_errno = EINVAL; + quota_priv_t *priv = NULL; - ret = inode_ctx_del (inode, this, &ctx_int); + local = frame->local; + if (local == NULL) { + gf_log (this->name, GF_LOG_WARNING, "local is NULL"); + goto unwind; + } - if (ret < 0) { - return 0; + priv = this->private; + + if (local->op_ret == -1) { + op_errno = local->op_errno; + goto unwind; } - ctx = (quota_inode_ctx_t *) (long)ctx_int; + STACK_WIND (frame, quota_mknod_cbk, + FIRST_CHILD(this), FIRST_CHILD(this)->fops->mknod, loc, + mode, rdev, umask, xdata); + + return 0; + +unwind: + QUOTA_STACK_UNWIND (mknod, frame, -1, op_errno, NULL, NULL, + NULL, NULL, NULL); + return 0; +} + + +int +quota_mknod (call_frame_t *frame, xlator_t *this, loc_t *loc, mode_t mode, + dev_t rdev, mode_t umask, dict_t *xdata) +{ + quota_priv_t *priv = NULL; + int32_t ret = -1; + quota_local_t *local = NULL; + call_stub_t *stub = NULL; + + priv = this->private; + + WIND_IF_QUOTAOFF (priv->is_quota_on, off); + + local = quota_local_new (); + if (local == NULL) { + goto err; + } + + frame->local = local; + + ret = loc_copy (&local->loc, loc); + if (ret) { + gf_log (this->name, GF_LOG_WARNING, "loc_copy failed"); + goto err; + } + + stub = fop_mknod_stub (frame, quota_mknod_helper, loc, mode, rdev, + umask, xdata); + if (stub == NULL) { + goto err; + } + + LOCK (&local->lock); + { + local->link_count = 1; + local->stub = stub; + local->delta = 0; + } + UNLOCK (&local->lock); + + quota_check_limit (frame, loc->parent, this, NULL, NULL); + return 0; + +err: + QUOTA_STACK_UNWIND (mknod, frame, -1, ENOMEM, NULL, NULL, NULL, NULL, + NULL); + return 0; + +off: + STACK_WIND_TAIL (frame, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->mknod, loc, + mode, rdev, umask, xdata); + return 0; +} + +int +quota_setxattr_cbk (call_frame_t *frame, void *cookie, + xlator_t *this, int op_ret, int op_errno, dict_t *xdata) +{ + quota_local_t *local = NULL; + quota_inode_ctx_t *ctx = NULL; + int ret = 0; + + local = frame->local; + if (!local) + goto out; + + ret = quota_inode_ctx_get (local->loc.inode, this, &ctx, 1); + if ((ret < 0) || (ctx == NULL)) { + op_errno = ENOMEM; + goto out; + } LOCK (&ctx->lock); { - list_for_each_entry_safe (dentry, tmp, &ctx->parents, next) { - __quota_dentry_free (dentry); - } + ctx->hard_lim = local->limit.hard_lim; + ctx->soft_lim = local->limit.soft_lim_percent; } UNLOCK (&ctx->lock); - LOCK_DESTROY (&ctx->lock); +out: + QUOTA_STACK_UNWIND (setxattr, frame, op_ret, op_errno, xdata); + return 0; +} - GF_FREE (ctx); +int +quota_setxattr (call_frame_t *frame, xlator_t *this, + loc_t *loc, dict_t *dict, int flags, dict_t *xdata) +{ + quota_priv_t *priv = NULL; + int op_errno = EINVAL; + int op_ret = -1; + int64_t hard_lim = -1, soft_lim = -1; + quota_local_t *local = NULL; + + priv = this->private; + + WIND_IF_QUOTAOFF (priv->is_quota_on, off); + + VALIDATE_OR_GOTO (frame, err); + VALIDATE_OR_GOTO (this, err); + VALIDATE_OR_GOTO (loc, err); + + if (frame->root->pid >= 0) { + GF_IF_INTERNAL_XATTR_GOTO ("trusted.glusterfs.quota*", dict, + op_errno, err); + GF_IF_INTERNAL_XATTR_GOTO ("trusted.pgfid*", dict, op_errno, + err); + } + + quota_get_limits (this, dict, &hard_lim, &soft_lim); + + if (hard_lim > 0) { + local = quota_local_new (); + if (local == NULL) { + op_errno = ENOMEM; + goto err; + } + + frame->local = local; + loc_copy (&local->loc, loc); + + local->limit.hard_lim = hard_lim; + local->limit.soft_lim_percent = soft_lim; + } + + STACK_WIND (frame, quota_setxattr_cbk, + FIRST_CHILD(this), FIRST_CHILD(this)->fops->setxattr, loc, + dict, flags, xdata); + return 0; +err: + QUOTA_STACK_UNWIND (setxattr, frame, op_ret, op_errno, NULL); + return 0; +off: + STACK_WIND_TAIL (frame, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->setxattr, loc, + dict, flags, xdata); return 0; } int -validate_options (xlator_t *this, char **op_errstr) +quota_fsetxattr_cbk (call_frame_t *frame, void *cookie, + xlator_t *this, int op_ret, int op_errno, dict_t *xdata) { - int ret = 0; - volume_opt_list_t *vol_opt = NULL; - volume_opt_list_t *tmp; + quota_inode_ctx_t *ctx = NULL; + quota_local_t *local = NULL; - if (!this) { - gf_log (this->name, GF_LOG_DEBUG, "'this' not a valid ptr"); - ret =-1; + local = frame->local; + if (!local) goto out; - } - if (list_empty (&this->volume_options)) + op_ret = quota_inode_ctx_get (local->loc.inode, this, &ctx, 1); + if ((op_ret < 0) || (ctx == NULL)) { + op_errno = ENOMEM; goto out; + } - vol_opt = list_entry (this->volume_options.next, - volume_opt_list_t, list); - list_for_each_entry_safe (vol_opt, tmp, &this->volume_options, list) { - ret = validate_xlator_volume_options_attacherr (this, - vol_opt->given_opt, - op_errstr); + LOCK (&ctx->lock); + { + ctx->hard_lim = local->limit.hard_lim; + ctx->soft_lim = local->limit.soft_lim_percent; } + UNLOCK (&ctx->lock); out: + QUOTA_STACK_UNWIND (fsetxattr, frame, op_ret, op_errno, xdata); + return 0; +} - return ret; +int +quota_fsetxattr (call_frame_t *frame, xlator_t *this, fd_t *fd, + dict_t *dict, int flags, dict_t *xdata) +{ + quota_priv_t *priv = NULL; + int32_t op_ret = -1; + int32_t op_errno = EINVAL; + quota_local_t *local = NULL; + int64_t hard_lim = -1, soft_lim = -1; + + priv = this->private; + + WIND_IF_QUOTAOFF (priv->is_quota_on, off); + + VALIDATE_OR_GOTO (frame, err); + VALIDATE_OR_GOTO (this, err); + VALIDATE_OR_GOTO (fd, err); + + if (0 <= frame->root->pid) { + GF_IF_INTERNAL_XATTR_GOTO ("trusted.glusterfs.quota*", + dict, op_errno, err); + GF_IF_INTERNAL_XATTR_GOTO ("trusted.pgfid*", dict, + op_errno, err); + } + + quota_get_limits (this, dict, &hard_lim, &soft_lim); + + if (hard_lim > 0) { + local = quota_local_new (); + if (local == NULL) { + op_errno = ENOMEM; + goto err; + } + frame->local = local; + local->loc.inode = inode_ref (fd->inode); + + local->limit.hard_lim = hard_lim; + local->limit.soft_lim_percent = soft_lim; + } + + STACK_WIND (frame, quota_fsetxattr_cbk, + FIRST_CHILD(this), FIRST_CHILD(this)->fops->fsetxattr, fd, + dict, flags, xdata); + return 0; +err: + QUOTA_STACK_UNWIND (fsetxattr, frame, op_ret, op_errno, NULL); + return 0; + +off: + STACK_WIND_TAIL (frame, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->fsetxattr, fd, + dict, flags, xdata); + return 0; +} + + +int +quota_removexattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, dict_t *xdata) +{ + QUOTA_STACK_UNWIND (removexattr, frame, op_ret, op_errno, xdata); + return 0; } +int +quota_removexattr (call_frame_t *frame, xlator_t *this, + loc_t *loc, const char *name, dict_t *xdata) +{ + quota_priv_t *priv = NULL; + int32_t op_errno = EINVAL; + + priv = this->private; + + WIND_IF_QUOTAOFF (priv->is_quota_on, off); + + VALIDATE_OR_GOTO (this, err); + + /* all quota xattrs can be cleaned up by doing setxattr on special key. + * Hence its ok that we don't allow removexattr on quota keys here. + */ + if (frame->root->pid >= 0) { + GF_IF_NATIVE_XATTR_GOTO ("trusted.glusterfs.quota*", + name, op_errno, err); + GF_IF_NATIVE_XATTR_GOTO ("trusted.pgfid*", name, + op_errno, err); + } + + VALIDATE_OR_GOTO (frame, err); + VALIDATE_OR_GOTO (loc, err); + + STACK_WIND (frame, quota_removexattr_cbk, + FIRST_CHILD(this), FIRST_CHILD(this)->fops->removexattr, + loc, name, xdata); + return 0; + +err: + QUOTA_STACK_UNWIND (removexattr, frame, -1, op_errno, NULL); + return 0; + +off: + STACK_WIND_TAIL (frame, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->removexattr, + loc, name, xdata); + return 0; +} + + +int +quota_fremovexattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, dict_t *xdata) +{ + QUOTA_STACK_UNWIND (fremovexattr, frame, op_ret, op_errno, xdata); + return 0; +} + +int +quota_fremovexattr (call_frame_t *frame, xlator_t *this, + fd_t *fd, const char *name, dict_t *xdata) +{ + quota_priv_t *priv = NULL; + int32_t op_ret = -1; + int32_t op_errno = EINVAL; + + priv = this->private; + + WIND_IF_QUOTAOFF (priv->is_quota_on, off); + + VALIDATE_OR_GOTO (frame, err); + VALIDATE_OR_GOTO (this, err); + VALIDATE_OR_GOTO (fd, err); + + if (frame->root->pid >= 0) { + GF_IF_NATIVE_XATTR_GOTO ("trusted.glusterfs.quota*", + name, op_errno, err); + GF_IF_NATIVE_XATTR_GOTO ("trusted.pgfid*", name, + op_errno, err); + } + STACK_WIND (frame, quota_fremovexattr_cbk, + FIRST_CHILD(this), FIRST_CHILD(this)->fops->fremovexattr, + fd, name, xdata); + return 0; +err: + QUOTA_STACK_UNWIND (fremovexattr, frame, op_ret, op_errno, NULL); + return 0; + +off: + STACK_WIND_TAIL (frame, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->fremovexattr, + fd, name, xdata); + return 0; +} + + int32_t -quota_parse_options (quota_priv_t *priv, xlator_t *this, dict_t *options) +quota_statfs_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct statvfs *buf, + dict_t *xdata) { - int32_t ret = -1; - char *str = NULL; - char *str_val = NULL; - char *path = NULL; - uint64_t value = 0; - limits_t *quota_lim = NULL; + inode_t *inode = NULL; + uint64_t value = 0; + int64_t usage = -1; + int64_t avail = -1; + int64_t blocks = 0; + quota_inode_ctx_t *ctx = NULL; + int ret = 0; + gf_boolean_t dict_created = _gf_false; - ret = dict_get_str (options, "limit-set", &str); + inode = cookie; - if (str) { - path = strtok (str, ":"); + /* This fop will fail mostly in case of client disconnect's, + * which is already logged. Hence, not logging here */ + if (op_ret == -1) + goto unwind; + /* + * We should never get here unless quota_statfs (below) sent us a + * cookie, and it would only do so if the value was non-NULL. This + * check is therefore just routine defensive coding. + */ + if (!inode) { + gf_log(this->name,GF_LOG_WARNING, + "null inode, cannot adjust for quota"); + goto unwind; + } - while (path) { - str_val = strtok (NULL, ","); + inode_ctx_get (inode, this, &value); + if (!value) { + goto unwind; + } - ret = gf_string2bytesize (str_val, &value); - if (ret != 0) - goto err; + /* if limit is set on this inode, report statfs based on this inode + * else report based on root. + */ + ctx = (quota_inode_ctx_t *)(unsigned long)value; + if (ctx->hard_lim <= 0) { + inode_ctx_get (inode->table->root, this, &value); + ctx = (quota_inode_ctx_t *)(unsigned long) value; + if (!ctx || ctx->hard_lim < 0) + goto unwind; + } + + { /* statfs is adjusted in this code block */ + usage = (ctx->size) / buf->f_bsize; + + blocks = ctx->hard_lim / buf->f_bsize; + buf->f_blocks = blocks; + + avail = buf->f_blocks - usage; + avail = max (avail, 0); + + buf->f_bfree = avail; + /* + * We have to assume that the total assigned quota + * won't cause us to dip into the reserved space, + * because dealing with the overcommitted cases is + * just too hairy (especially when different bricks + * might be using different reserved percentages and + * such). + */ + buf->f_bavail = buf->f_bfree; + } + + if (!xdata) { + xdata = dict_new (); + if (!xdata) + goto unwind; + dict_created = _gf_true; + } + + ret = dict_set_int8 (xdata, "quota-deem-statfs", 1); + if (-1 == ret) + gf_log (this->name, GF_LOG_ERROR, "Dict set failed, " + "deem-statfs option may have no effect"); + +unwind: + QUOTA_STACK_UNWIND (statfs, frame, op_ret, op_errno, buf, xdata); + + if (dict_created) + dict_unref (xdata); + return 0; +} + + +int32_t +quota_statfs_helper (call_frame_t *frame, xlator_t *this, loc_t *loc, + dict_t *xdata) +{ + quota_local_t *local = NULL; + int op_errno = EINVAL; + + GF_VALIDATE_OR_GOTO ("quota", (local = frame->local), err); + + if (-1 == local->op_ret) { + op_errno = local->op_errno; + goto err; + } + + STACK_WIND_COOKIE (frame, quota_statfs_cbk, loc->inode, + FIRST_CHILD(this), + FIRST_CHILD(this)->fops->statfs, loc, xdata); + return 0; +err: + QUOTA_STACK_UNWIND (statfs, frame, -1, op_errno, NULL, NULL); + return 0; +} - QUOTA_ALLOC_OR_GOTO (quota_lim, limits_t, err); +int32_t +quota_statfs_validate_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, inode_t *inode, + struct iatt *buf, dict_t *xdata, + struct iatt *postparent) +{ + quota_local_t *local = NULL; + int32_t ret = 0; + quota_inode_ctx_t *ctx = NULL; + int64_t *size = 0; + uint64_t value = 0; - quota_lim->path = path; + local = frame->local; - quota_lim->value = value; + if (op_ret < 0) + goto resume; - gf_log (this->name, GF_LOG_INFO, "%s:%"PRId64, - quota_lim->path, quota_lim->value); + GF_ASSERT (local); + GF_ASSERT (frame); + GF_VALIDATE_OR_GOTO_WITH_ERROR ("quota", this, resume, op_errno, + EINVAL); + GF_VALIDATE_OR_GOTO_WITH_ERROR (this->name, xdata, resume, op_errno, + EINVAL); - list_add_tail ("a_lim->limit_list, - &priv->limit_head); + ret = inode_ctx_get (local->validate_loc.inode, this, &value); - path = strtok (NULL, ":"); + ctx = (quota_inode_ctx_t *)(unsigned long)value; + if ((ret == -1) || (ctx == NULL)) { + gf_log (this->name, GF_LOG_WARNING, + "quota context is not present in inode (gfid:%s)", + uuid_utoa (local->validate_loc.inode->gfid)); + op_errno = EINVAL; + goto resume; + } + + ret = dict_get_bin (xdata, QUOTA_SIZE_KEY, (void **) &size); + if (ret < 0) { + gf_log (this->name, GF_LOG_WARNING, + "size key not present in dict"); + op_errno = EINVAL; + goto resume; + } + + LOCK (&ctx->lock); + { + ctx->size = ntoh64 (*size); + gettimeofday (&ctx->tv, NULL); + } + UNLOCK (&ctx->lock); + +resume: + quota_link_count_decrement (local); + return 0; +} + +int32_t +quota_statfs (call_frame_t *frame, xlator_t *this, loc_t *loc, dict_t *xdata) +{ + quota_local_t *local = NULL; + int op_errno = 0; + call_stub_t *stub = NULL; + quota_priv_t *priv = NULL; + int ret = 0; + + priv = this->private; + + WIND_IF_QUOTAOFF (priv->is_quota_on, off); + + if (priv->consider_statfs && loc->inode) { + local = quota_local_new (); + if (!local) { + op_errno = ENOMEM; + goto err; } - } else { - gf_log (this->name, GF_LOG_INFO, - "no \"limit-set\" option provided"); + frame->local = local; + + stub = fop_statfs_stub (frame, quota_statfs_helper, loc, xdata); + if (!stub) { + op_errno = ENOMEM; + goto err; + } + + LOCK (&local->lock); + { + local->inode = inode_ref (loc->inode); + local->link_count = 1; + local->stub = stub; + } + UNLOCK (&local->lock); + + ret = quota_validate (frame, local->inode, this, + quota_statfs_validate_cbk); + if (0 > ret) { + quota_handle_validate_error (local, -1, -ret); + } + + return 0; + } + + /* + * We have to make sure that we never get to quota_statfs_cbk + * with a cookie that points to something other than an inode, + * which is exactly what would happen with STACK_UNWIND using + * that as a callback. Therefore, use default_statfs_cbk in + * this case instead. + * + * Also if the option deem-statfs is not set to "on" don't + * bother calculating quota limit on / in statfs_cbk. + */ + if (priv->consider_statfs) + gf_log (this->name,GF_LOG_WARNING, + "missing inode, cannot adjust for quota"); + +off: + STACK_WIND_TAIL (frame, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->statfs, loc, xdata); + return 0; + +err: + STACK_UNWIND_STRICT (statfs, frame, -1, op_errno, NULL, NULL); + + if (local) + quota_local_cleanup (this, local); + return 0; +} + +int +quota_readdirp_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int op_ret, int op_errno, gf_dirent_t *entries, + dict_t *xdata) +{ + gf_dirent_t *entry = NULL; + quota_local_t *local = NULL; + loc_t loc = {0, }; + + if (op_ret <= 0) + goto unwind; + + local = frame->local; + + list_for_each_entry (entry, &entries->list, list) { + if ((strcmp (entry->d_name, ".") == 0) + || (strcmp (entry->d_name, "..") == 0)) + continue; + + uuid_copy (loc.gfid, entry->d_stat.ia_gfid); + loc.inode = inode_ref (entry->inode); + loc.parent = inode_ref (local->loc.inode); + uuid_copy (loc.pargfid, loc.parent->gfid); + loc.name = entry->d_name; + + quota_fill_inodectx (this, entry->inode, entry->dict, + &loc, &entry->d_stat, &op_errno); + + loc_wipe (&loc); } - ret = dict_get_str (options, "timeout", &str); - if (str) { - ret = gf_string2bytesize (str, &value); +unwind: + QUOTA_STACK_UNWIND (readdirp, frame, op_ret, op_errno, entries, xdata); + + return 0; +} + +int +quota_readdirp (call_frame_t *frame, xlator_t *this, fd_t *fd, size_t size, + off_t offset, dict_t *dict) +{ + quota_priv_t *priv = NULL; + int ret = 0; + gf_boolean_t new_dict = _gf_false; + quota_local_t *local = NULL; + + priv = this->private; + + WIND_IF_QUOTAOFF (priv->is_quota_on, off); + + local = quota_local_new (); + + if (local == NULL) { + goto err; + } + + frame->local = local; + + local->loc.inode = inode_ref (fd->inode); + + if (dict == NULL) { + dict = dict_new (); + new_dict = _gf_true; + } + + if (dict) { + ret = dict_set_int8 (dict, QUOTA_LIMIT_KEY, 1); if (ret < 0) { - gf_log (this->name, GF_LOG_INFO, - "Invalid quota timout value."); - ret = -1; + gf_log (this->name, GF_LOG_WARNING, + "dict set of key for hard-limit failed"); goto err; - } else { - priv->timeout = (int64_t) value; - gf_log (this->name, GF_LOG_INFO, - "quota timeout value = %"PRId64, - priv->timeout); } - } else { - gf_log (this->name, GF_LOG_INFO, "timeout option not provided, " - "taking default as 0"); - priv->timeout = 0; } - list_for_each_entry (quota_lim, &priv->limit_head, limit_list) { - gf_log (this->name, GF_LOG_INFO, "%s:%"PRId64, quota_lim->path, - quota_lim->value); + STACK_WIND (frame, quota_readdirp_cbk, + FIRST_CHILD(this), FIRST_CHILD(this)->fops->readdirp, fd, + size, offset, dict); + + if (new_dict) { + dict_unref (dict); } - ret = 0; + return 0; err: + STACK_UNWIND_STRICT (readdirp, frame, -1, EINVAL, NULL, NULL); + + if (new_dict) { + dict_unref (dict); + } + + return 0; + +off: + STACK_WIND_TAIL (frame, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->readdirp, fd, + size, offset, dict); + return 0; +} + +int32_t +quota_fallocate_cbk(call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, struct iatt *prebuf, + struct iatt *postbuf, dict_t *xdata) +{ + int32_t ret = 0; + uint64_t ctx_int = 0; + quota_inode_ctx_t *ctx = NULL; + quota_local_t *local = NULL; + + local = frame->local; + + if ((op_ret < 0) || (local == NULL)) { + goto out; + } + + ret = inode_ctx_get (local->loc.inode, this, &ctx_int); + if (ret) { + gf_log (this->name, GF_LOG_WARNING, + "%s: failed to get the context", local->loc.path); + goto out; + } + + ctx = (quota_inode_ctx_t *)(unsigned long) ctx_int; + + if (ctx == NULL) { + gf_log (this->name, GF_LOG_WARNING, + "quota context not set in %s (gfid:%s)", + local->loc.path, uuid_utoa (local->loc.inode->gfid)); + goto out; + } + + LOCK (&ctx->lock); + { + ctx->buf = *postbuf; + } + UNLOCK (&ctx->lock); + +out: + QUOTA_STACK_UNWIND (fallocate, frame, op_ret, op_errno, prebuf, postbuf, + xdata); + + return 0; +} + + +int32_t +quota_fallocate_helper (call_frame_t *frame, xlator_t *this, fd_t *fd, + int32_t mode, off_t offset, size_t len, dict_t *xdata) +{ + quota_local_t *local = NULL; + int32_t op_errno = EINVAL; + quota_priv_t *priv = NULL; + + local = frame->local; + if (local == NULL) { + gf_log (this->name, GF_LOG_WARNING, "local is NULL"); + goto unwind; + } + + priv = this->private; + + if (local->op_ret == -1) { + op_errno = local->op_errno; + goto unwind; + } + + STACK_WIND (frame, quota_fallocate_cbk, + FIRST_CHILD(this), + FIRST_CHILD(this)->fops->fallocate, fd, mode, offset, len, + xdata); + return 0; + +unwind: + QUOTA_STACK_UNWIND (fallocate, frame, -1, op_errno, NULL, NULL, NULL); + return 0; +} + + +int32_t +quota_fallocate(call_frame_t *frame, xlator_t *this, fd_t *fd, int32_t mode, + off_t offset, size_t len, dict_t *xdata) +{ + int32_t ret = -1, op_errno = EINVAL; + int32_t parents = 0; + quota_local_t *local = NULL; + quota_inode_ctx_t *ctx = NULL; + quota_priv_t *priv = NULL; + quota_dentry_t *dentry = NULL; + call_stub_t *stub = NULL; + + priv = this->private; + GF_VALIDATE_OR_GOTO (this->name, priv, unwind); + + WIND_IF_QUOTAOFF (priv->is_quota_on, off); + + GF_ASSERT (frame); + GF_VALIDATE_OR_GOTO ("quota", this, unwind); + GF_VALIDATE_OR_GOTO (this->name, fd, unwind); + + local = quota_local_new (); + if (local == NULL) { + goto unwind; + } + + frame->local = local; + local->loc.inode = inode_ref (fd->inode); + + ret = quota_inode_ctx_get (fd->inode, this, &ctx, 0); + if (ctx == NULL) { + gf_log (this->name, GF_LOG_DEBUG, "quota context is " + "NULL on inode (%s). " + "If quota is not enabled recently and crawler " + "has finished crawling, its an error", + uuid_utoa (local->loc.inode->gfid)); + } + + stub = fop_fallocate_stub(frame, quota_fallocate_helper, fd, mode, + offset, len, xdata); + if (stub == NULL) { + op_errno = ENOMEM; + goto unwind; + } + + priv = this->private; + GF_VALIDATE_OR_GOTO (this->name, priv, unwind); + + if (ctx != NULL) { + LOCK (&ctx->lock); + { + list_for_each_entry (dentry, &ctx->parents, next) { + parents++; + } + } + UNLOCK (&ctx->lock); + } + + /* + * Note that by using len as the delta we're assuming the range from + * offset to offset+len has not already been allocated. This can result + * in ENOSPC errors attempting to allocate an already allocated range. + */ + local->delta = len; + local->stub = stub; + local->link_count = parents; + + if (parents == 0) { + local->link_count = 1; + quota_check_limit (frame, fd->inode, this, NULL, NULL); + } else { + list_for_each_entry (dentry, &ctx->parents, next) { + quota_check_limit (frame, fd->inode, this, dentry->name, + dentry->par); + } + } + + return 0; + +unwind: + QUOTA_STACK_UNWIND (fallocate, frame, -1, op_errno, NULL, NULL, NULL); + return 0; + +off: + STACK_WIND_TAIL (frame, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->fallocate, fd, mode, offset, + len, xdata); + return 0; +} + +/* Logs if +* i. Usage crossed soft limit +* ii. Usage above soft limit and alert-time elapsed +*/ +void +quota_log_usage (xlator_t *this, quota_inode_ctx_t *ctx, inode_t *inode, + int64_t delta) +{ + struct timeval cur_time = {0,}; + char *usage_str = NULL; + char size_str[32] = {0}; + char *path = NULL; + int64_t cur_size = 0; + quota_priv_t *priv = NULL; + gf_boolean_t dyn_mem = _gf_true; + + priv = this->private; + if ((ctx->soft_lim <= 0) || (timerisset (&ctx->prev_log) && + !quota_timeout (&ctx->prev_log, + priv->log_timeout))) { + return; + } + + + cur_size = ctx->size + delta; + usage_str = gf_uint64_2human_readable (cur_size); + if (!usage_str) { + snprintf (size_str, sizeof (size_str), "%"PRId64, cur_size); + usage_str = (char*) size_str; + dyn_mem = _gf_false; + } + inode_path (inode, NULL, &path); + if (!path) + path = uuid_utoa (inode->gfid); + + gettimeofday (&cur_time, NULL); + /* Usage crossed/reached soft limit */ + if (DID_REACH_LIMIT (ctx->soft_lim, ctx->size, cur_size)) { + + gf_log (this->name, GF_LOG_ALERT, "Usage crossed " + "soft limit: %s used by %s", usage_str, path); + ctx->prev_log = cur_time; + } + /* Usage is above soft limit */ + else if (cur_size > ctx->soft_lim){ + gf_log (this->name, GF_LOG_ALERT, "Usage is above " + "soft limit: %s used by %s", usage_str, path); + ctx->prev_log = cur_time; + } + + if (dyn_mem) + GF_FREE (usage_str); +} + +int32_t +mem_acct_init (xlator_t *this) +{ + int ret = -1; + + if (!this) + return ret; + + ret = xlator_mem_acct_init (this, gf_quota_mt_end + 1); + + if (ret != 0) { + gf_log (this->name, GF_LOG_WARNING, "Memory accounting" + "init failed"); + return ret; + } + return ret; } int32_t +quota_forget (xlator_t *this, inode_t *inode) +{ + int32_t ret = 0; + uint64_t ctx_int = 0; + quota_inode_ctx_t *ctx = NULL; + quota_dentry_t *dentry = NULL, *tmp; + + ret = inode_ctx_del (inode, this, &ctx_int); + + if (ret < 0) { + return 0; + } + + ctx = (quota_inode_ctx_t *) (long)ctx_int; + + LOCK (&ctx->lock); + { + list_for_each_entry_safe (dentry, tmp, &ctx->parents, next) { + __quota_dentry_free (dentry); + } + } + UNLOCK (&ctx->lock); + + LOCK_DESTROY (&ctx->lock); + + GF_FREE (ctx); + + return 0; +} + +int32_t init (xlator_t *this) { int32_t ret = -1; @@ -2706,46 +4143,123 @@ init (xlator_t *this) QUOTA_ALLOC_OR_GOTO (priv, quota_priv_t, err); - INIT_LIST_HEAD (&priv->limit_head); + LOCK_INIT (&priv->lock); this->private = priv; - ret = quota_parse_options (priv, this, this->options); - - if (ret) { + GF_OPTION_INIT ("deem-statfs", priv->consider_statfs, bool, err); + GF_OPTION_INIT ("server-quota", priv->is_quota_on, bool, err); + GF_OPTION_INIT ("default-soft-limit", priv->default_soft_lim, percent, + err); + GF_OPTION_INIT ("soft-timeout", priv->soft_timeout, time, err); + GF_OPTION_INIT ("hard-timeout", priv->hard_timeout, time, err); + GF_OPTION_INIT ("alert-time", priv->log_timeout, time, err); + GF_OPTION_INIT ("volume-uuid", priv->volume_uuid, str, err); + + this->local_pool = mem_pool_new (quota_local_t, 64); + if (!this->local_pool) { + ret = -1; + gf_log (this->name, GF_LOG_ERROR, + "failed to create local_t's memory pool"); goto err; } + if (priv->is_quota_on) { + priv->rpc_clnt = quota_enforcer_init (this, this->options); + if (priv->rpc_clnt == NULL) { + ret = -1; + gf_log (this->name, GF_LOG_WARNING, + "quota enforcer rpc init failed"); + goto err; + } + } + ret = 0; err: return ret; } - int reconfigure (xlator_t *this, dict_t *options) { - int32_t ret = -1; - quota_priv_t *priv = NULL; - limits_t *limit = NULL; - limits_t *next = NULL; + int32_t ret = -1; + quota_priv_t *priv = NULL; + gf_boolean_t quota_on = _gf_false; priv = this->private; - list_for_each_entry_safe (limit, next, &priv->limit_head, limit_list) { - list_del (&limit->limit_list); + GF_OPTION_RECONF ("deem-statfs", priv->consider_statfs, options, bool, + out); + GF_OPTION_RECONF ("server-quota", quota_on, options, bool, + out); + GF_OPTION_RECONF ("default-soft-limit", priv->default_soft_lim, + options, percent, out); + GF_OPTION_RECONF ("alert-time", priv->log_timeout, options, + time, out); + GF_OPTION_RECONF ("soft-timeout", priv->soft_timeout, options, + time, out); + GF_OPTION_RECONF ("hard-timeout", priv->hard_timeout, options, + time, out); + + if (quota_on) { + priv->rpc_clnt = quota_enforcer_init (this, + this->options); + if (priv->rpc_clnt == NULL) { + ret = -1; + gf_log (this->name, GF_LOG_WARNING, + "quota enforcer rpc init failed"); + goto out; + } - GF_FREE (limit); + } else { + if (priv->rpc_clnt) { + // Quotad is shutdown when there is no started volume + // which has quota enabled. So, we should disable the + // enforcer client when quota is disabled on a volume, + // to avoid spurious reconnect attempts to a service + // (quotad), that is known to be down. + rpc_clnt_disable (priv->rpc_clnt); + } } - ret = quota_parse_options (priv, this, options); - - if (ret == -1) - GF_ASSERT (0); + priv->is_quota_on = quota_on; + ret = 0; +out: return ret; } +int32_t +quota_priv_dump (xlator_t *this) +{ + quota_priv_t *priv = NULL; + int32_t ret = -1; + + + GF_ASSERT (this); + + priv = this->private; + + gf_proc_dump_add_section ("xlators.features.quota.priv", this->name); + + ret = TRY_LOCK (&priv->lock); + if (ret) + goto out; + else { + gf_proc_dump_write("soft-timeout", "%d", priv->soft_timeout); + gf_proc_dump_write("hard-timeout", "%d", priv->hard_timeout); + gf_proc_dump_write("alert-time", "%d", priv->log_timeout); + gf_proc_dump_write("quota-on", "%d", priv->is_quota_on); + gf_proc_dump_write("statfs", "%d", priv->consider_statfs); + gf_proc_dump_write("volume-uuid", "%s", priv->volume_uuid); + gf_proc_dump_write("validation-count", "%ld", + priv->validation_count); + } + UNLOCK (&priv->lock); + +out: + return 0; +} void fini (xlator_t *this) @@ -2755,34 +4269,105 @@ fini (xlator_t *this) struct xlator_fops fops = { - .lookup = quota_lookup, - .writev = quota_writev, - .create = quota_create, - .mkdir = quota_mkdir, - .truncate = quota_truncate, - .ftruncate = quota_ftruncate, - .unlink = quota_unlink, - .symlink = quota_symlink, - .link = quota_link, - .rename = quota_rename, - .getxattr = quota_getxattr, - .fgetxattr = quota_fgetxattr, - .stat = quota_stat, - .fstat = quota_fstat, - .readlink = quota_readlink, - .readv = quota_readv, - .fsync = quota_fsync, - .setattr = quota_setattr, - .fsetattr = quota_fsetattr, + .statfs = quota_statfs, + .lookup = quota_lookup, + .writev = quota_writev, + .create = quota_create, + .mkdir = quota_mkdir, + .truncate = quota_truncate, + .ftruncate = quota_ftruncate, + .unlink = quota_unlink, + .symlink = quota_symlink, + .link = quota_link, + .rename = quota_rename, + .getxattr = quota_getxattr, + .fgetxattr = quota_fgetxattr, + .stat = quota_stat, + .fstat = quota_fstat, + .readlink = quota_readlink, + .readv = quota_readv, + .fsync = quota_fsync, + .setattr = quota_setattr, + .fsetattr = quota_fsetattr, + .mknod = quota_mknod, + .setxattr = quota_setxattr, + .fsetxattr = quota_fsetxattr, + .removexattr = quota_removexattr, + .fremovexattr = quota_fremovexattr, + .readdirp = quota_readdirp, + .fallocate = quota_fallocate, }; struct xlator_cbks cbks = { .forget = quota_forget }; +struct xlator_dumpops dumpops = { + .priv = quota_priv_dump, +}; struct volume_options options[] = { {.key = {"limit-set"}}, - {.key = {"timeout"} + {.key = {"deem-statfs"}, + .type = GF_OPTION_TYPE_BOOL, + .default_value = "off", + .description = "If set to on, it takes quota limits into" + "consideration while estimating fs size. (df command)" + " (Default is off)." + }, + {.key = {"server-quota"}, + .type = GF_OPTION_TYPE_BOOL, + .default_value = "off", + .description = "Skip the quota enforcement if the feature is" + " not turned on. This is not a user exposed option." + }, + {.key = {"default-soft-limit"}, + .type = GF_OPTION_TYPE_PERCENT, + .default_value = "80%", + }, + {.key = {"soft-timeout"}, + .type = GF_OPTION_TYPE_TIME, + .min = 0, + .max = 1800, + .default_value = "60", + .description = "quota caches the directory sizes on client. " + "soft-timeout indicates the timeout for the validity of" + " cache before soft-limit has been crossed." + }, + {.key = {"hard-timeout"}, + .type = GF_OPTION_TYPE_TIME, + .min = 0, + .max = 60, + .default_value = "5", + .description = "quota caches the directory sizes on client. " + "hard-timeout indicates the timeout for the validity of" + " cache after soft-limit has been crossed." + }, + { .key = {"username"}, + .type = GF_OPTION_TYPE_ANY, + }, + { .key = {"password"}, + .type = GF_OPTION_TYPE_ANY, + }, + { .key = {"transport-type"}, + .value = {"tcp", "socket", "ib-verbs", "unix", "ib-sdp", + "tcp/client", "ib-verbs/client", "rdma"}, + .type = GF_OPTION_TYPE_STR, + }, + { .key = {"remote-host"}, + .type = GF_OPTION_TYPE_INTERNET_ADDRESS, + }, + { .key = {"remote-port"}, + .type = GF_OPTION_TYPE_INT, + }, + { .key = {"volume-uuid"}, + .type = GF_OPTION_TYPE_STR, + .description = "uuid of the volume this brick is part of." + }, + { .key = {"alert-time"}, + .type = GF_OPTION_TYPE_TIME, + .min = 0, + .max = 7*86400, + .default_value = "86400", }, {.key = {NULL}} }; diff --git a/xlators/features/quota/src/quota.h b/xlators/features/quota/src/quota.h index 7250a5dc9..84c3257fe 100644 --- a/xlators/features/quota/src/quota.h +++ b/xlators/features/quota/src/quota.h @@ -1,50 +1,58 @@ /* - Copyright (c) 2008-2010 Gluster, Inc. <http://www.gluster.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 Affero 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 - Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see - <http://www.gnu.org/licenses/>. -*/ + Copyright (c) 2008-2012 Red Hat, Inc. <http://www.redhat.com> + This file is part of GlusterFS. + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. +*/ #ifndef _CONFIG_H #define _CONFIG_H #include "config.h" #endif +#ifndef _QUOTA_H +#define _QUOTA_H + #include "xlator.h" #include "call-stub.h" #include "defaults.h" -#include "byte-order.h" #include "common-utils.h" #include "quota-mem-types.h" +#include "glusterfs.h" +#include "compat.h" +#include "logging.h" +#include "dict.h" +#include "stack.h" +#include "common-utils.h" +#include "event.h" +#include "globals.h" +#include "rpcsvc.h" +#include "rpc-clnt.h" +#include "byte-order.h" +#include "glusterfs3-xdr.h" +#include "glusterfs3.h" +#include "xdr-generic.h" +#include "compat-errno.h" +#include "protocol-common.h" -#define QUOTA_XATTR_PREFIX "trusted." #define DIRTY "dirty" #define SIZE "size" #define CONTRIBUTION "contri" #define VAL_LENGTH 8 #define READDIR_BUF 4096 -#define QUOTA_STACK_DESTROY(_frame, _this) \ - do { \ - quota_local_t *_local = NULL; \ - _local = _frame->local; \ - _frame->local = NULL; \ - STACK_DESTROY (_frame->root); \ - quota_local_cleanup (_this, _local); \ - GF_FREE (_local); \ - } while (0) +#ifndef UUID_CANONICAL_FORM_LEN +#define UUID_CANONICAL_FORM_LEN 36 +#endif + +#define WIND_IF_QUOTAOFF(is_quota_on, label) \ + if (!is_quota_on) \ + goto label; + +#define DID_REACH_LIMIT(lim, prev_size, cur_size) \ + ((cur_size) >= (lim) && (prev_size) < (lim)) #define QUOTA_SAFE_INCREMENT(lock, var) \ do { \ @@ -60,19 +68,13 @@ UNLOCK (lock); \ } while (0) -#define QUOTA_LOCAL_ALLOC_OR_GOTO(local, type, label) \ - do { \ - QUOTA_ALLOC_OR_GOTO (local, type, label); \ - LOCK_INIT (&local->lock); \ - } while (0) - #define QUOTA_ALLOC_OR_GOTO(var, type, label) \ do { \ var = GF_CALLOC (sizeof (type), 1, \ gf_quota_mt_##type); \ if (!var) { \ gf_log ("", GF_LOG_ERROR, \ - "out of memory :("); \ + "out of memory"); \ ret = -1; \ goto label; \ } \ @@ -89,7 +91,6 @@ } \ STACK_UNWIND_STRICT (fop, frame, params); \ quota_local_cleanup (_this, _local); \ - GF_FREE (_local); \ } while (0) #define QUOTA_FREE_CONTRIBUTION_NODE(_contribution) \ @@ -101,11 +102,17 @@ #define GET_CONTRI_KEY(var, _vol_name, _gfid, _ret) \ do { \ char _gfid_unparsed[40]; \ - uuid_unparse (_gfid, _gfid_unparsed); \ - _ret = gf_asprintf (var, QUOTA_XATTR_PREFIX \ - "%s.%s." CONTRIBUTION, \ - _vol_name, _gfid_unparsed); \ - } while (0) + if (_gfid != NULL) { \ + uuid_unparse (_gfid, _gfid_unparsed); \ + _ret = gf_asprintf (var, QUOTA_XATTR_PREFIX \ + "%s.%s." CONTRIBUTION, \ + _vol_name, _gfid_unparsed); \ + } else { \ + _ret = gf_asprintf (var, QUOTA_XATTR_PREFIX \ + "%s.." CONTRIBUTION, \ + _vol_name); \ + } \ + } while (0) #define GET_CONTRI_KEY_OR_GOTO(var, _vol_name, _gfid, label) \ @@ -123,53 +130,91 @@ goto label; \ } while (0) +#define QUOTA_REG_OR_LNK_FILE(ia_type) \ + (IA_ISREG (ia_type) || IA_ISLNK (ia_type)) + + + struct quota_dentry { char *name; - ino_t par; + uuid_t par; struct list_head next; }; typedef struct quota_dentry quota_dentry_t; struct quota_inode_ctx { int64_t size; - int64_t limit; + int64_t hard_lim; + int64_t soft_lim; struct iatt buf; struct list_head parents; struct timeval tv; + struct timeval prev_log; gf_lock_t lock; }; typedef struct quota_inode_ctx quota_inode_ctx_t; +struct quota_limit { + int64_t hard_lim; + int64_t soft_lim_percent; +} __attribute__ ((packed)); +typedef struct quota_limit quota_limit_t; + +typedef void +(*quota_ancestry_built_t) (struct list_head *parents, inode_t *inode, + int32_t op_ret, int32_t op_errno, void *data); + struct quota_local { - gf_lock_t lock; - uint32_t validate_count; - uint32_t link_count; - loc_t loc; - loc_t oldloc; - loc_t newloc; - loc_t validate_loc; - int64_t delta; - int32_t op_ret; - int32_t op_errno; - int64_t size; - int64_t limit; - char just_validated; - inode_t *inode; - call_stub_t *stub; + gf_lock_t lock; + uint32_t validate_count; + uint32_t link_count; + loc_t loc; + loc_t oldloc; + loc_t newloc; + loc_t validate_loc; + int64_t delta; + int32_t op_ret; + int32_t op_errno; + int64_t size; + gf_boolean_t skip_check; + char just_validated; + fop_lookup_cbk_t validate_cbk; + inode_t *inode; + call_stub_t *stub; + struct iobref *iobref; + quota_limit_t limit; + int64_t space_available; + quota_ancestry_built_t ancestry_cbk; + void *ancestry_data; }; -typedef struct quota_local quota_local_t; +typedef struct quota_local quota_local_t; struct quota_priv { - int64_t timeout; - struct list_head limit_head; + uint32_t soft_timeout; + uint32_t hard_timeout; + uint32_t log_timeout; + double default_soft_lim; + gf_boolean_t is_quota_on; + gf_boolean_t consider_statfs; + gf_lock_t lock; + rpc_clnt_prog_t *quota_enforcer; + struct rpcsvc_program *quotad_aggregator; + struct rpc_clnt *rpc_clnt; + rpcsvc_t *rpcsvc; + inode_table_t *itable; + char *volume_uuid; + uint64_t validation_count; }; -typedef struct quota_priv quota_priv_t; +typedef struct quota_priv quota_priv_t; -struct limits { - struct list_head limit_list; - char *path; - int64_t value; -}; -typedef struct limits limits_t; +int +quota_enforcer_lookup (call_frame_t *frame, xlator_t *this, loc_t *loc, + dict_t *xdata, fop_lookup_cbk_t cbk); +struct rpc_clnt * +quota_enforcer_init (xlator_t *this, dict_t *options); + +void +quota_log_usage (xlator_t *this, quota_inode_ctx_t *ctx, inode_t *inode, + int64_t delta); -uint64_t cn = 1; +#endif diff --git a/xlators/features/quota/src/quotad-aggregator.c b/xlators/features/quota/src/quotad-aggregator.c new file mode 100644 index 000000000..5f13fd251 --- /dev/null +++ b/xlators/features/quota/src/quotad-aggregator.c @@ -0,0 +1,423 @@ +/* + Copyright (c) 2008-2012 Red Hat, Inc. <http://www.redhat.com> + This file is part of GlusterFS. + + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. +*/ + +#include "cli1-xdr.h" +#include "quota.h" +#include "quotad-helpers.h" +#include "quotad-aggregator.h" + +struct rpcsvc_program quotad_aggregator_prog; + +struct iobuf * +quotad_serialize_reply (rpcsvc_request_t *req, void *arg, struct iovec *outmsg, + xdrproc_t xdrproc) +{ + struct iobuf *iob = NULL; + ssize_t retlen = 0; + ssize_t xdr_size = 0; + + GF_VALIDATE_OR_GOTO ("server", req, ret); + + /* First, get the io buffer into which the reply in arg will + * be serialized. + */ + if (arg && xdrproc) { + xdr_size = xdr_sizeof (xdrproc, arg); + iob = iobuf_get2 (req->svc->ctx->iobuf_pool, xdr_size); + if (!iob) { + gf_log_callingfn (THIS->name, GF_LOG_ERROR, + "Failed to get iobuf"); + goto ret; + }; + + iobuf_to_iovec (iob, outmsg); + /* Use the given serializer to translate the give C structure in arg + * to XDR format which will be written into the buffer in outmsg. + */ + /* retlen is used to received the error since size_t is unsigned and we + * need -1 for error notification during encoding. + */ + + retlen = xdr_serialize_generic (*outmsg, arg, xdrproc); + if (retlen == -1) { + /* Failed to Encode 'GlusterFS' msg in RPC is not exactly + failure of RPC return values.. client should get + notified about this, so there are no missing frames */ + gf_log_callingfn ("", GF_LOG_ERROR, "Failed to encode message"); + req->rpc_err = GARBAGE_ARGS; + retlen = 0; + } + } + outmsg->iov_len = retlen; +ret: + if (retlen == -1) { + iobuf_unref (iob); + iob = NULL; + } + + return iob; +} + +int +quotad_aggregator_submit_reply (call_frame_t *frame, rpcsvc_request_t *req, + void *arg, struct iovec *payload, + int payloadcount, struct iobref *iobref, + xdrproc_t xdrproc) +{ + struct iobuf *iob = NULL; + int ret = -1; + struct iovec rsp = {0,}; + quotad_aggregator_state_t *state = NULL; + char new_iobref = 0; + + GF_VALIDATE_OR_GOTO ("server", req, ret); + + if (frame) { + state = frame->root->state; + frame->local = NULL; + } + + if (!iobref) { + iobref = iobref_new (); + if (!iobref) { + goto ret; + } + + new_iobref = 1; + } + + iob = quotad_serialize_reply (req, arg, &rsp, xdrproc); + if (!iob) { + gf_log ("", GF_LOG_ERROR, "Failed to serialize reply"); + goto ret; + } + + iobref_add (iobref, iob); + + ret = rpcsvc_submit_generic (req, &rsp, 1, payload, payloadcount, + iobref); + + iobuf_unref (iob); + + ret = 0; +ret: + if (state) { + quotad_aggregator_free_state (state); + } + + if (frame) { + if (frame->root->client) + gf_client_unref (frame->root->client); + + STACK_DESTROY (frame->root); + } + + if (new_iobref) { + iobref_unref (iobref); + } + + return ret; +} + +int +quotad_aggregator_getlimit_cbk (xlator_t *this, call_frame_t *frame, + void *lookup_rsp) +{ + gfs3_lookup_rsp *rsp = lookup_rsp; + gf_cli_rsp cli_rsp = {0,}; + dict_t *xdata = NULL; + int ret = -1; + + GF_PROTOCOL_DICT_UNSERIALIZE (frame->this, xdata, + (rsp->xdata.xdata_val), + (rsp->xdata.xdata_len), rsp->op_ret, + rsp->op_errno, out); + + ret = 0; +out: + rsp->op_ret = ret; + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "failed to unserialize " + "nameless lookup rsp"); + goto reply; + } + cli_rsp.op_ret = rsp->op_ret; + cli_rsp.op_errno = rsp->op_errno; + cli_rsp.op_errstr = ""; + if (xdata) { + GF_PROTOCOL_DICT_SERIALIZE (frame->this, xdata, + (&cli_rsp.dict.dict_val), + (cli_rsp.dict.dict_len), + cli_rsp.op_errno, reply); + } + +reply: + quotad_aggregator_submit_reply (frame, frame->local, (void*)&cli_rsp, NULL, 0, + NULL, (xdrproc_t)xdr_gf_cli_rsp); + + dict_unref (xdata); + GF_FREE (cli_rsp.dict.dict_val); + return 0; +} + +int +quotad_aggregator_getlimit (rpcsvc_request_t *req) +{ + call_frame_t *frame = NULL; + gf_cli_req cli_req = {{0}, }; + gf_cli_rsp cli_rsp = {0}; + gfs3_lookup_req args = {{0,},}; + gfs3_lookup_rsp rsp = {0,}; + quotad_aggregator_state_t *state = NULL; + xlator_t *this = NULL; + dict_t *dict = NULL; + int ret = -1, op_errno = 0; + char *gfid_str = NULL; + uuid_t gfid = {0}; + + GF_VALIDATE_OR_GOTO ("quotad-aggregator", req, err); + + this = THIS; + + ret = xdr_to_generic (req->msg[0], &cli_req, (xdrproc_t)xdr_gf_cli_req); + if (ret < 0) { + //failed to decode msg; + gf_log ("", GF_LOG_ERROR, "xdr decoding error"); + req->rpc_err = GARBAGE_ARGS; + goto err; + } + + if (cli_req.dict.dict_len) { + dict = dict_new (); + ret = dict_unserialize (cli_req.dict.dict_val, + cli_req.dict.dict_len, &dict); + if (ret < 0) { + gf_log (this->name, GF_LOG_ERROR, "Failed to " + "unserialize req-buffer to dictionary"); + goto err; + } + } + + ret = dict_get_str (dict, "gfid", &gfid_str); + if (ret) { + goto err; + } + + uuid_parse ((const char*)gfid_str, gfid); + + frame = quotad_aggregator_get_frame_from_req (req); + if (frame == NULL) { + rsp.op_errno = ENOMEM; + goto err; + } + state = frame->root->state; + state->xdata = dict; + ret = dict_set_int32 (state->xdata, QUOTA_LIMIT_KEY, 42); + if (ret) + goto err; + + ret = dict_set_int32 (state->xdata, QUOTA_SIZE_KEY, 42); + if (ret) + goto err; + + ret = dict_set_int32 (state->xdata, GET_ANCESTRY_PATH_KEY, 42); + if (ret) + goto err; + + memcpy (&args.gfid, &gfid, 16); + + args.bname = alloca (req->msg[0].iov_len); + args.xdata.xdata_val = alloca (req->msg[0].iov_len); + + ret = qd_nameless_lookup (this, frame, &args, state->xdata, + quotad_aggregator_getlimit_cbk); + if (ret) { + rsp.op_errno = ret; + goto err; + } + + return ret; + +err: + cli_rsp.op_ret = -1; + cli_rsp.op_errno = op_errno; + cli_rsp.op_errstr = ""; + + quotad_aggregator_getlimit_cbk (this, frame, &cli_rsp); + dict_unref (dict); + + return ret; +} + +int +quotad_aggregator_lookup_cbk (xlator_t *this, call_frame_t *frame, + void *rsp) +{ + quotad_aggregator_submit_reply (frame, frame->local, rsp, NULL, 0, NULL, + (xdrproc_t)xdr_gfs3_lookup_rsp); + + return 0; +} + + +int +quotad_aggregator_lookup (rpcsvc_request_t *req) +{ + call_frame_t *frame = NULL; + gfs3_lookup_req args = {{0,},}; + int ret = -1, op_errno = 0; + gfs3_lookup_rsp rsp = {0,}; + quotad_aggregator_state_t *state = NULL; + xlator_t *this = NULL; + + GF_VALIDATE_OR_GOTO ("quotad-aggregator", req, err); + + this = THIS; + + args.bname = alloca (req->msg[0].iov_len); + args.xdata.xdata_val = alloca (req->msg[0].iov_len); + + ret = xdr_to_generic (req->msg[0], &args, + (xdrproc_t)xdr_gfs3_lookup_req); + if (ret < 0) { + rsp.op_errno = EINVAL; + goto err; + } + + frame = quotad_aggregator_get_frame_from_req (req); + if (frame == NULL) { + rsp.op_errno = ENOMEM; + goto err; + } + + state = frame->root->state; + + GF_PROTOCOL_DICT_UNSERIALIZE (this, state->xdata, + (args.xdata.xdata_val), + (args.xdata.xdata_len), ret, + op_errno, err); + + + ret = qd_nameless_lookup (this, frame, &args, state->xdata, + quotad_aggregator_lookup_cbk); + if (ret) { + rsp.op_errno = ret; + goto err; + } + + return ret; + +err: + rsp.op_ret = -1; + rsp.op_errno = op_errno; + + quotad_aggregator_lookup_cbk (this, frame, &rsp); + return ret; +} + +int +quotad_aggregator_rpc_notify (rpcsvc_t *rpc, void *xl, rpcsvc_event_t event, + void *data) +{ + if (!xl || !data) { + gf_log_callingfn ("server", GF_LOG_WARNING, + "Calling rpc_notify without initializing"); + goto out; + } + + switch (event) { + case RPCSVC_EVENT_ACCEPT: + break; + + case RPCSVC_EVENT_DISCONNECT: + break; + + default: + break; + } + +out: + return 0; +} + +int +quotad_aggregator_init (xlator_t *this) +{ + quota_priv_t *priv = NULL; + int ret = -1; + + priv = this->private; + + ret = dict_set_str (this->options, "transport.address-family", "unix"); + if (ret) + goto out; + + ret = dict_set_str (this->options, "transport-type", "socket"); + if (ret) + goto out; + + ret = dict_set_str (this->options, "transport.socket.listen-path", + "/tmp/quotad.socket"); + if (ret) + goto out; + + /* RPC related */ + priv->rpcsvc = rpcsvc_init (this, this->ctx, this->options, 0); + if (priv->rpcsvc == NULL) { + gf_log (this->name, GF_LOG_WARNING, + "creation of rpcsvc failed"); + ret = -1; + goto out; + } + + ret = rpcsvc_create_listeners (priv->rpcsvc, this->options, + this->name); + if (ret < 1) { + gf_log (this->name, GF_LOG_WARNING, + "creation of listener failed"); + ret = -1; + goto out; + } + + priv->quotad_aggregator = "ad_aggregator_prog; + quotad_aggregator_prog.options = this->options; + + ret = rpcsvc_program_register (priv->rpcsvc, "ad_aggregator_prog); + if (ret) { + gf_log (this->name, GF_LOG_WARNING, + "registration of program (name:%s, prognum:%d, " + "progver:%d) failed", quotad_aggregator_prog.progname, + quotad_aggregator_prog.prognum, + quotad_aggregator_prog.progver); + goto out; + } + + ret = 0; +out: + return ret; +} + +rpcsvc_actor_t quotad_aggregator_actors[] = { + [GF_AGGREGATOR_NULL] = {"NULL", GF_AGGREGATOR_NULL, NULL, NULL, 0, + DRC_NA}, + [GF_AGGREGATOR_LOOKUP] = {"LOOKUP", GF_AGGREGATOR_NULL, + quotad_aggregator_lookup, NULL, 0, DRC_NA}, + [GF_AGGREGATOR_GETLIMIT] = {"GETLIMIT", GF_AGGREGATOR_GETLIMIT, + quotad_aggregator_getlimit, NULL, 0}, +}; + + +struct rpcsvc_program quotad_aggregator_prog = { + .progname = "GlusterFS 3.3", + .prognum = GLUSTER_AGGREGATOR_PROGRAM, + .progver = GLUSTER_AGGREGATOR_VERSION, + .numactors = GF_AGGREGATOR_MAXVALUE, + .actors = quotad_aggregator_actors +}; diff --git a/xlators/features/quota/src/quotad-aggregator.h b/xlators/features/quota/src/quotad-aggregator.h new file mode 100644 index 000000000..5ddea5b3c --- /dev/null +++ b/xlators/features/quota/src/quotad-aggregator.h @@ -0,0 +1,37 @@ +/* + Copyright (c) 2008-2012 Red Hat, Inc. <http://www.redhat.com> + This file is part of GlusterFS. + + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. +*/ + +#ifndef _QUOTAD_AGGREGATOR_H +#define _QUOTAD_AGGREGATOR_H + +#include "quota.h" +#include "stack.h" +#include "glusterfs3-xdr.h" +#include "inode.h" + +typedef struct { + void *pool; + xlator_t *this; + xlator_t *active_subvol; + inode_table_t *itable; + loc_t loc; + dict_t *xdata; +} quotad_aggregator_state_t; + +typedef int (*quotad_aggregator_lookup_cbk_t) (xlator_t *this, + call_frame_t *frame, + void *rsp); +int +qd_nameless_lookup (xlator_t *this, call_frame_t *frame, gfs3_lookup_req *req, + dict_t *xdata, quotad_aggregator_lookup_cbk_t lookup_cbk); +int +quotad_aggregator_init (xlator_t *this); + +#endif diff --git a/xlators/features/quota/src/quotad-helpers.c b/xlators/features/quota/src/quotad-helpers.c new file mode 100644 index 000000000..fd3099114 --- /dev/null +++ b/xlators/features/quota/src/quotad-helpers.c @@ -0,0 +1,113 @@ +/* + Copyright (c) 2008-2012 Red Hat, Inc. <http://www.redhat.com> + This file is part of GlusterFS. + + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. +*/ + +#include "quotad-helpers.h" + +quotad_aggregator_state_t * +get_quotad_aggregator_state (xlator_t *this, rpcsvc_request_t *req) +{ + quotad_aggregator_state_t *state = NULL; + xlator_t *active_subvol = NULL; + quota_priv_t *priv = NULL; + + state = (void *)GF_CALLOC (1, sizeof (*state), + gf_quota_mt_aggregator_state_t); + if (!state) + return NULL; + + state->this = THIS; + priv = this->private; + + LOCK (&priv->lock); + { + active_subvol = state->active_subvol = FIRST_CHILD (this); + } + UNLOCK (&priv->lock); + + if (active_subvol->itable == NULL) + active_subvol->itable = inode_table_new (4096, active_subvol); + + state->itable = active_subvol->itable; + + state->pool = this->ctx->pool; + + return state; +} + +void +quotad_aggregator_free_state (quotad_aggregator_state_t *state) +{ + if (state->xdata) + dict_unref (state->xdata); + + GF_FREE (state); +} + +call_frame_t * +quotad_aggregator_alloc_frame (rpcsvc_request_t *req) +{ + call_frame_t *frame = NULL; + quotad_aggregator_state_t *state = NULL; + xlator_t *this = NULL; + + GF_VALIDATE_OR_GOTO ("server", req, out); + GF_VALIDATE_OR_GOTO ("server", req->trans, out); + GF_VALIDATE_OR_GOTO ("server", req->svc, out); + GF_VALIDATE_OR_GOTO ("server", req->svc->ctx, out); + + this = req->svc->mydata; + + frame = create_frame (this, req->svc->ctx->pool); + if (!frame) + goto out; + + state = get_quotad_aggregator_state (this, req); + if (!state) + goto out; + + frame->root->state = state; + frame->root->unique = 0; + + frame->this = this; +out: + return frame; +} + +call_frame_t * +quotad_aggregator_get_frame_from_req (rpcsvc_request_t *req) +{ + call_frame_t *frame = NULL; + client_t *client = NULL; + + GF_VALIDATE_OR_GOTO ("server", req, out); + + frame = quotad_aggregator_alloc_frame (req); + if (!frame) + goto out; + + client = req->trans->xl_private; + + frame->root->op = req->procnum; + + frame->root->unique = req->xid; + + frame->root->uid = req->uid; + frame->root->gid = req->gid; + frame->root->pid = req->pid; + + gf_client_ref (client); + frame->root->client = client; + + frame->root->lk_owner = req->lk_owner; + + frame->local = req; +out: + return frame; +} diff --git a/xlators/features/quota/src/quotad-helpers.h b/xlators/features/quota/src/quotad-helpers.h new file mode 100644 index 000000000..a10fb7fa8 --- /dev/null +++ b/xlators/features/quota/src/quotad-helpers.h @@ -0,0 +1,24 @@ +/* + Copyright (c) 2008-2012 Red Hat, Inc. <http://www.redhat.com> + This file is part of GlusterFS. + + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. +*/ + +#ifndef QUOTAD_HELPERS_H +#define QUOTAD_HELPERS_H + +#include "rpcsvc.h" +#include "quota.h" +#include "quotad-aggregator.h" + +void +quotad_aggregator_free_state (quotad_aggregator_state_t *state); + +call_frame_t * +quotad_aggregator_get_frame_from_req (rpcsvc_request_t *req); + +#endif diff --git a/xlators/features/quota/src/quotad.c b/xlators/features/quota/src/quotad.c new file mode 100644 index 000000000..243b943e9 --- /dev/null +++ b/xlators/features/quota/src/quotad.c @@ -0,0 +1,210 @@ +/* + Copyright (c) 2013 Red Hat, Inc. <http://www.redhat.com> + This file is part of GlusterFS. + + This file is licensed to you under your choice of the GNU Lesser + General Public License, version 3 or any later version (LGPLv3 or + later), or the GNU General Public License, version 2 (GPLv2), in all + cases as published by the Free Software Foundation. +*/ +#include "quota.h" +#include "quotad-aggregator.h" +#include "common-utils.h" + +int32_t +mem_acct_init (xlator_t *this) +{ + int ret = -1; + + if (!this) + return ret; + + ret = xlator_mem_acct_init (this, gf_quota_mt_end + 1); + + if (0 != ret) { + gf_log (this->name, GF_LOG_WARNING, "Memory accounting " + "init failed"); + return ret; + } + + return ret; +} + +int32_t +qd_lookup_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, inode_t *inode, + struct iatt *buf, dict_t *xdata, struct iatt *postparent) +{ + quotad_aggregator_lookup_cbk_t lookup_cbk = NULL; + gfs3_lookup_rsp rsp = {0, }; + + lookup_cbk = cookie; + + rsp.op_ret = op_ret; + rsp.op_errno = op_errno; + + gf_stat_from_iatt (&rsp.postparent, postparent); + + GF_PROTOCOL_DICT_SERIALIZE (this, xdata, (&rsp.xdata.xdata_val), + rsp.xdata.xdata_len, rsp.op_errno, out); + + gf_stat_from_iatt (&rsp.stat, buf); + +out: + lookup_cbk (this, frame, &rsp); + + GF_FREE (rsp.xdata.xdata_val); + + inode_unref (inode); + + return 0; +} + +xlator_t * +qd_find_subvol (xlator_t *this, char *volume_uuid) +{ + xlator_list_t *child = NULL; + xlator_t *subvol = NULL; + char key[1024]; + char *optstr = NULL; + + if (!this || !volume_uuid) + goto out; + + for (child = this->children; child; child = child->next) { + snprintf(key, 1024, "%s.volume-id", child->xlator->name); + if (dict_get_str(this->options, key, &optstr) < 0) + continue; + + if (strcmp (optstr, volume_uuid) == 0) { + subvol = child->xlator; + break; + } + } + +out: + return subvol; +} + +int +qd_nameless_lookup (xlator_t *this, call_frame_t *frame, gfs3_lookup_req *req, + dict_t *xdata, quotad_aggregator_lookup_cbk_t lookup_cbk) +{ + gfs3_lookup_rsp rsp = {0, }; + int op_errno = 0, ret = -1; + loc_t loc = {0, }; + quotad_aggregator_state_t *state = NULL; + quota_priv_t *priv = NULL; + xlator_t *subvol = NULL; + char *volume_uuid = NULL; + + priv = this->private; + state = frame->root->state; + + frame->root->op = GF_FOP_LOOKUP; + + loc.inode = inode_new (state->itable); + if (loc.inode == NULL) { + op_errno = ENOMEM; + goto out; + } + + memcpy (loc.gfid, req->gfid, 16); + + ret = dict_get_str (xdata, "volume-uuid", &volume_uuid); + if (ret < 0) { + op_errno = EINVAL; + goto out; + } + + subvol = qd_find_subvol (this, volume_uuid); + if (subvol == NULL) { + op_errno = EINVAL; + goto out; + } + + STACK_WIND_COOKIE (frame, qd_lookup_cbk, lookup_cbk, subvol, + subvol->fops->lookup, &loc, xdata); + return 0; + +out: + rsp.op_ret = -1; + rsp.op_errno = op_errno; + + lookup_cbk (this, frame, &rsp); + + inode_unref (loc.inode); + return 0; +} + +int +qd_reconfigure (xlator_t *this, dict_t *options) +{ + /* As of now quotad is restarted upon alteration of volfile */ + return 0; +} + +void +qd_fini (xlator_t *this) +{ + return; +} + +int32_t +qd_init (xlator_t *this) +{ + int32_t ret = -1; + quota_priv_t *priv = NULL; + + if (NULL == this->children) { + gf_log (this->name, GF_LOG_ERROR, + "FATAL: quota (%s) not configured for min of 1 child", + this->name); + ret = -1; + goto err; + } + + QUOTA_ALLOC_OR_GOTO (priv, quota_priv_t, err); + LOCK_INIT (&priv->lock); + + this->private = priv; + + ret = quotad_aggregator_init (this); + if (ret < 0) + goto err; + + ret = 0; +err: + if (ret) { + GF_FREE (priv); + } + return ret; +} + +class_methods_t class_methods = { + .init = qd_init, + .fini = qd_fini, + .reconfigure = qd_reconfigure, +}; + +struct xlator_fops fops = { +}; + +struct xlator_cbks cbks = { +}; + +struct volume_options options[] = { + { .key = {"transport-type"}, + .value = {"rpc", "rpc-over-rdma", "tcp", "socket", "ib-verbs", + "unix", "ib-sdp", "tcp/server", "ib-verbs/server", "rdma", + "rdma*([ \t]),*([ \t])socket", + "rdma*([ \t]),*([ \t])tcp", + "tcp*([ \t]),*([ \t])rdma", + "socket*([ \t]),*([ \t])rdma"}, + .type = GF_OPTION_TYPE_STR + }, + { .key = {"transport.*"}, + .type = GF_OPTION_TYPE_ANY, + }, + {.key = {NULL}} +}; |
