From 4db0bc5977eb3e2d5bc0e3666bb6de5719d153a4 Mon Sep 17 00:00:00 2001 From: Mohammed Junaid Ahmed Date: Tue, 15 Mar 2011 22:25:55 +0000 Subject: features/marker: QUOTA related changes in marker translator. Signed-off-by: Junaid Signed-off-by: Vijay Bellur BUG: 2473 (Support for volume and directory level quota) URL: http://bugs.gluster.com/cgi-bin/bugzilla3/show_bug.cgi?id=2473 --- xlators/features/marker/src/marker-quota.c | 1815 ++++++++++++++++++++++++++++ 1 file changed, 1815 insertions(+) create mode 100644 xlators/features/marker/src/marker-quota.c (limited to 'xlators/features/marker/src/marker-quota.c') diff --git a/xlators/features/marker/src/marker-quota.c b/xlators/features/marker/src/marker-quota.c new file mode 100644 index 00000000000..f7b397a5a84 --- /dev/null +++ b/xlators/features/marker/src/marker-quota.c @@ -0,0 +1,1815 @@ +/*Copyright (c) 2008-2010 Gluster, Inc. + This file is part of GlusterFS. + + GlusterFS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3 of the License, + or (at your option) any later version. + + GlusterFS is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see + . +*/ + +#ifndef _CONFIG_H +#define _CONFIG_H +#include "config.h" +#endif + +#include "dict.h" +#include "xlator.h" +#include "defaults.h" +#include "libxlator.h" +#include "common-utils.h" +#include "byte-order.h" +#include "marker-quota.h" +#include "marker-quota-helper.h" + +int32_t +loc_fill_from_name (xlator_t *this, loc_t *newloc, loc_t *oldloc, uint64_t ino, char *name) +{ + int32_t ret = 0; + int32_t len = 0; + char *path = NULL; + + + newloc->ino = ino; + + newloc->inode = inode_new (oldloc->inode->table); + + if (!newloc->inode) { + ret = -1; + goto out; + } + + newloc->parent = inode_ref (oldloc->inode); + + len = strlen (oldloc->path); + + if (oldloc->path [len - 1] == '/') + ret = gf_asprintf ((char **) &path, "%s%s", + oldloc->path, name); + else + ret = gf_asprintf ((char **) &path, "%s/%s", + oldloc->path, name); + + if (ret < 0) + goto out; + + newloc->path = path; + + newloc->name = strrchr (newloc->path, '/'); + + if (newloc->name) + newloc->name++; + + gf_log (this->name, GF_LOG_INFO, "path = %s name =%s",newloc->path, newloc->name); +out: + return ret; +} + +int32_t +dirty_inode_updation_done (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno) +{ + quota_local_t *local = NULL; + + local = frame->local; + + if (!local->err) + QUOTA_SAFE_DECREMENT (&local->lock, local->ref); + else + frame->local = NULL; + + QUOTA_STACK_DESTROY (frame, this); + + return 0; +} + +int32_t +release_lock_on_dirty_inode (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno) +{ + struct gf_flock lock; + quota_local_t *local = NULL; + + if (op_ret == -1) { + local->err = -1; + + dirty_inode_updation_done (frame, NULL, this, 0, 0); + + return 0; + } + + local = frame->local; + + if (op_ret == 0) + local->ctx->dirty = 0; + + lock.l_type = F_UNLCK; + lock.l_whence = SEEK_SET; + lock.l_start = 0; + lock.l_len = 0; + lock.l_pid = 0; + + STACK_WIND (frame, + dirty_inode_updation_done, + FIRST_CHILD(this), + FIRST_CHILD(this)->fops->inodelk, + this->name, &local->loc, F_SETLKW, &lock); + + return 0; +} + +int32_t +mark_inode_undirty (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, dict_t *dict) +{ + int32_t ret = -1; + int64_t *size = NULL; + dict_t *newdict = NULL; + quota_local_t *local = NULL; + marker_conf_t *priv = NULL; + + if (op_ret == -1) + goto err; + + priv = (marker_conf_t *) this->private; + + local = (quota_local_t *) frame->local; + + if (!dict) + goto wind; + + ret = dict_get_bin (dict, priv->size_key, (void **) &size); + if (ret) + goto wind; + + local->ctx->size = ntoh64 (*size); + +wind: + newdict = dict_new (); + if (!newdict) + goto err; + + ret = dict_set_int8 (newdict, priv->dirty_key, 0); + if (ret) + goto err; + + STACK_WIND (frame, release_lock_on_dirty_inode, + FIRST_CHILD(this), + FIRST_CHILD(this)->fops->setxattr, + &local->loc, newdict, 0); + ret = 0; + +err: + if (op_ret == -1 || ret == -1) { + local->err = -1; + + release_lock_on_dirty_inode (frame, NULL, this, 0, 0); + } + + if (newdict) + dict_unref (newdict); + + return 0; +} + +int32_t +update_size_xattr (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) +{ + int32_t ret = -1; + dict_t *new_dict = NULL; + int64_t *size = NULL; + int64_t *delta = NULL; + quota_local_t *local = NULL; + marker_conf_t *priv = NULL; + + if (op_ret == -1) + goto err; + + priv = this->private; + + if (!dict) + goto err; + + local = frame->local; + + ret = dict_get_bin (dict, priv->size_key, (void **) &size); + if (!size) + goto err; + + QUOTA_ALLOC_OR_GOTO (delta, int64_t, ret, err); + + *delta = ntoh64 (ntoh64 (*size) - local->sum); + + gf_log (this->name, GF_LOG_DEBUG, "calculated size = %ld, original size = %ld" + "path = %s diff = %ld", local->sum, ntoh64 (*size), + local->loc.path, ntoh64 (*delta)); + + new_dict = dict_new (); + if (!new_dict); + + ret = dict_set_bin (new_dict, priv->size_key, delta, 8); + if (ret) + goto err; + + STACK_WIND (frame, mark_inode_undirty, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->xattrop, &local->loc, + GF_XATTROP_ADD_ARRAY64, new_dict); + + ret = 0; + +err: + if (op_ret == -1 || ret == -1) { + local->err = -1; + + release_lock_on_dirty_inode (frame, NULL, this, 0, 0); + } + + if (new_dict) + dict_unref (dict); + + return 0; +} + +int32_t +get_dirty_inode_size (call_frame_t *frame, xlator_t *this) +{ + int32_t ret = -1; + dict_t *dict = NULL; + quota_local_t *local = NULL; + marker_conf_t *priv = NULL; + + local = (quota_local_t *) frame->local; + + priv = (marker_conf_t *) this->private; + + dict = dict_new (); + if (!dict) { + ret = -1; + goto err; + } + + ret = dict_set_int64 (dict, priv->size_key, 0); + if (ret) + goto err; + + STACK_WIND (frame, update_size_xattr, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->lookup, &local->loc, dict); + ret =0; + +err: + if (ret) { + local->err = -1; + + release_lock_on_dirty_inode (frame, NULL, this, 0, 0); + } + + return 0; +} + +int32_t +get_child_contribution (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) +{ + int32_t ret = -1; + char contri_key [512] = {0, }; + int64_t *contri = NULL; + quota_local_t *local = NULL; + + local = frame->local; + + frame->local = NULL; + + QUOTA_STACK_DESTROY (frame, this); + + if (op_ret == -1) { + gf_log (this->name, GF_LOG_ERROR, "%s", strerror (op_errno)); + + local->err = -2; + + release_lock_on_dirty_inode (local->frame, NULL, this, 0, 0); + + goto out; + } + + if (local->err) + goto out; + + GET_CONTRI_KEY (contri_key, local->loc.inode->gfid, ret); + if (ret < 0) + goto out; + + if (!dict) + goto out; + + if (dict_get_bin (dict, contri_key, (void **) &contri) == 0) + local->sum += ntoh64 (*contri); + +out: + LOCK (&local->lock); + { + local->dentry_child_count--; + } + UNLOCK (&local->lock); + + if (local->dentry_child_count == 0) { + if (local->err) { + QUOTA_SAFE_DECREMENT (&local->lock, local->ref); + + quota_local_unref (this, local); + } else + quota_dirty_inode_readdir (local->frame, NULL, this, + 0, 0, NULL); + } + + return 0; +} + +int32_t +quota_readdir_cbk (call_frame_t *frame, + void *cookie, + xlator_t *this, + int32_t op_ret, + int32_t op_errno, + gf_dirent_t *entries) +{ + char contri_key [512] = {0, }; + loc_t loc; + int32_t ret = 0; + off_t offset = 0; + int32_t count = 0; + dict_t *dict = NULL; + quota_local_t *local = NULL; + gf_dirent_t *entry = NULL; + call_frame_t *newframe = NULL; + + local = frame->local; + + if (op_ret == -1) { + gf_log (this->name, GF_LOG_DEBUG, + "readdir failed %s", strerror (op_errno)); + local->err = -1; + + release_lock_on_dirty_inode (frame, NULL, this, 0, 0); + + return 0; + } else if (op_ret == 0) { + get_dirty_inode_size (frame, this); + + return 0; + } + + local->dentry_child_count = 0; + + list_for_each_entry (entry, (&entries->list), list) { + gf_log (this->name, GF_LOG_INFO, "entry = %s", entry->d_name); + + if ((!strcmp (entry->d_name, ".")) || (!strcmp (entry->d_name, ".."))) { + gf_log (this->name, GF_LOG_INFO, "entry = %s", entry->d_name); + continue; + } + count++; + } + + local->frame = frame; + + list_for_each_entry (entry, (&entries->list), list) { + gf_log (this->name, GF_LOG_INFO, "entry = %s", entry->d_name); + + if ((!strcmp (entry->d_name, ".")) || (!strcmp (entry->d_name, ".."))) { + gf_log (this->name, GF_LOG_INFO, "entry = %s", entry->d_name); + offset = entry->d_off; + continue; + } + + ret = loc_fill_from_name (this, &loc, &local->loc, + entry->d_ino, entry->d_name); + if (ret < 0) + goto out; + + newframe = copy_frame (frame); + if (!newframe) { + ret = -1; + goto out; + } + + newframe->local = local; + + dict = dict_new (); + if (!dict) { + ret = -1; + goto out; + } + + GET_CONTRI_KEY (contri_key, local->loc.inode->gfid, ret); + if (ret < 0) + goto out; + + ret = dict_set_int64 (dict, contri_key, 0); + if (ret) + goto out; + + QUOTA_SAFE_INCREMENT (&local->lock, local->dentry_child_count); + + STACK_WIND (newframe, + get_child_contribution, + FIRST_CHILD(this), + FIRST_CHILD(this)->fops->lookup, + &loc, dict); + + offset = entry->d_off; + + loc_wipe (&loc); + + out: + if (dict) { + dict_unref (dict); + dict = NULL; + } + + if (ret) { + LOCK (&local->lock); + { + if (local->dentry_child_count == 0) + local->err = -1; + else + local->err = -2; + } + UNLOCK (&local->lock); + + if (newframe) { + newframe->local = NULL; + + QUOTA_STACK_DESTROY (newframe, this); + } + + break; + } + } + gf_log (this->name, GF_LOG_INFO, "offset before =%lu",local->d_off); + local->d_off +=offset; + gf_log (this->name, GF_LOG_INFO, "offset after = %lu",local->d_off); + + if (ret) + release_lock_on_dirty_inode (frame, NULL, this, 0, 0); + + else if (count == 0 ) + get_dirty_inode_size (frame, this); + + return 0; +} + +int32_t +quota_dirty_inode_readdir (call_frame_t *frame, + void *cookie, + xlator_t *this, + int32_t op_ret, + int32_t op_errno, + fd_t *fd) +{ + quota_local_t *local = NULL; + + if (op_ret == -1) { + local->err = -1; + release_lock_on_dirty_inode (frame, NULL, this, 0, 0); + return 0; + } + + local = frame->local; + + if (local->fd == NULL) + local->fd = fd_ref (fd); + + STACK_WIND (frame, + quota_readdir_cbk, + FIRST_CHILD(this), + FIRST_CHILD(this)->fops->readdir, + local->fd, READDIR_BUF, local->d_off); + + return 0; +} + +int32_t +check_if_still_dirty (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) +{ + int8_t dirty = -1; + int32_t ret = -1; + fd_t *fd = NULL; + quota_local_t *local = NULL; + marker_conf_t *priv = NULL; + + local = frame->local; + + if (op_ret == -1) { + gf_log (this->name, GF_LOG_ERROR, "failed to get " + "the dirty xattr for %s", local->loc.path); + goto err; + } + + priv = this->private; + + if (!dict) { + ret = -1; + goto err; + } + + ret = dict_get_int8 (dict, priv->dirty_key, &dirty); + if (ret) + goto err; + + //the inode is not dirty anymore + if (dirty == 0) { + release_lock_on_dirty_inode (frame, NULL, this, 0, 0); + + return 0; + } + + fd = fd_create (local->loc.inode, frame->root->pid); + + local->d_off = 0; + + STACK_WIND(frame, + quota_dirty_inode_readdir, + FIRST_CHILD(this), + FIRST_CHILD(this)->fops->opendir, + &local->loc, fd); + + ret = 0; + +err: + if (op_ret == -1 || ret == -1) { + local->err = -1; + release_lock_on_dirty_inode (frame, NULL, this, 0, 0); + } + + return 0; +} + +int32_t +get_dirty_xattr (call_frame_t *frame, void *cookie, + xlator_t *this, int32_t op_ret, int32_t op_errno) +{ + int32_t ret = -1; + dict_t *xattr_req = NULL; + quota_local_t *local = NULL; + marker_conf_t *priv = NULL; + + if (op_ret == -1) { + dirty_inode_updation_done (frame, NULL, this, 0, 0); + return 0; + } + + priv = (marker_conf_t *) this->private; + + local = frame->local; + + xattr_req = dict_new (); + if (xattr_req == NULL) { + ret = -1; + goto err; + } + + ret = dict_set_int8 (xattr_req, priv->dirty_key, 0); + if (ret) + goto err; + + STACK_WIND (frame, + check_if_still_dirty, + FIRST_CHILD(this), + FIRST_CHILD(this)->fops->lookup, + &local->loc, + xattr_req); + ret = 0; + +err: + if (ret) { + local->err = -1; + release_lock_on_dirty_inode(frame, NULL, this, 0, 0); + } + + if (xattr_req) + dict_unref (xattr_req); + + return 0; +} + +int32_t +update_dirty_inode (xlator_t *this, + loc_t *loc, + quota_inode_ctx_t *ctx, + inode_contribution_t *contribution) +{ + int32_t ret = -1; + quota_local_t *local = NULL; + struct gf_flock lock; + call_frame_t *frame = NULL; + + frame = create_frame (this, this->ctx->pool); + if (frame == NULL) { + ret = -1; + goto out; + } + + local = quota_local_new (); + if (local == NULL) + goto fr_destroy; + + frame->local = local; + + ret = loc_copy (&local->loc, loc); + if (ret < 0) + goto fr_destroy; + + local->ctx = ctx; + + local->contri = contribution; + + frame->root->lk_owner = cn++; + + lock.l_type = F_WRLCK; + lock.l_whence = SEEK_SET; + lock.l_start = 0; + lock.l_len = 0; + + STACK_WIND (frame, + get_dirty_xattr, + FIRST_CHILD(this), + FIRST_CHILD(this)->fops->inodelk, + this->name, &local->loc, F_SETLKW, &lock); + +fr_destroy: + QUOTA_STACK_DESTROY (frame, this); +out: + + return 0; +} + + +int32_t +quota_inode_creation_done (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno) +{ + quota_local_t *local = NULL; + + if (frame == NULL) + return 0; + + local = frame->local; + + QUOTA_STACK_DESTROY (frame, this); + + return 0; +} + +int32_t +create_dirty_xattr (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, dict_t *dict) +{ + int32_t ret = -1; + dict_t *newdict = NULL; + quota_local_t *local = NULL; + marker_conf_t *priv = NULL; + + if (op_ret == -1 && op_errno == ENOTCONN) + goto err; + + local = frame->local; + + priv = (marker_conf_t *) this->private; + + if (local->loc.inode->ia_type == IA_IFDIR) { + newdict = dict_new (); + if (!newdict) + goto err; + + ret = dict_set_int8 (newdict, priv->dirty_key, 0); + if (ret == -1) + goto err; + + STACK_WIND (frame, quota_inode_creation_done, + FIRST_CHILD(this), + FIRST_CHILD(this)->fops->setxattr, + &local->loc, newdict, 0); + } else + quota_inode_creation_done (frame, NULL, this, 0, 0); + + ret = 0; + +err: + if (ret == -1) + quota_inode_creation_done (frame, NULL, this, -1, 0); + + if (newdict) + dict_unref (newdict); + + return 0; +} + + +int32_t +quota_set_inode_xattr (xlator_t *this, loc_t *loc) +{ + int32_t ret = 0; + int64_t *value = NULL; + int64_t *size = NULL; + dict_t *dict = NULL; + char key[512] = {0, }; + call_frame_t *frame = NULL; + quota_local_t *local = NULL; + marker_conf_t *priv = NULL; + quota_inode_ctx_t *ctx = NULL; + inode_contribution_t *contri = NULL; + + if (loc == NULL || this == NULL) + return 0; + + priv = (marker_conf_t *) this->private; + + ret = quota_inode_ctx_get (loc->inode, this, &ctx); + if (ret < 0) { + ctx = quota_inode_ctx_new (loc->inode, this); + if (ctx == NULL) { + gf_log (this->name, GF_LOG_WARNING, + "quota_inode_ctx_new failed"); + ret = -1; + goto out; + } + } + + dict = dict_new (); + if (!dict) + goto out; + + if (loc->inode->ia_type == IA_IFDIR) { + QUOTA_ALLOC_OR_GOTO (size, int64_t, ret, err); + ret = dict_set_bin (dict, priv->size_key, size, 8); + if (ret < 0) + goto free_size; + } + + //if '/' then dont set contribution xattr + if (strcmp (loc->path, "/") == 0) + goto wind; + + contri = add_new_contribution_node (this, ctx, loc); + if (contri == NULL) + goto err; + + QUOTA_ALLOC_OR_GOTO (value, int64_t, ret, err); + GET_CONTRI_KEY (key, loc->parent->gfid, ret); + + ret = dict_set_bin (dict, key, value, 8); + if (ret < 0) + goto free_value; + +wind: + frame = create_frame (this, this->ctx->pool); + if (!frame) { + ret = -1; + goto err; + } + + local = quota_local_new (); + if (local == NULL) + goto free_size; + + local->ctx = ctx; + + local->contri = contri; + + ret = loc_copy (&local->loc, loc); + if (ret < 0) + quota_local_unref (this, local); + + frame->local = local; + + STACK_WIND (frame, create_dirty_xattr, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->xattrop, &local->loc, + GF_XATTROP_ADD_ARRAY64, dict); + ret = 0; + +free_size: + if (ret < 0) + GF_FREE (size); + +free_value: + if (ret < 0) + GF_FREE (value); + +err: + dict_unref (dict); + +out: + if (ret < 0) + quota_inode_creation_done (NULL, NULL, this, -1, 0); + + return 0; +} + + +int32_t +get_parent_inode_local (xlator_t *this, quota_local_t *local) +{ + uint32_t ret; + quota_inode_ctx_t *ctx = NULL; + + loc_wipe (&local->loc); + + loc_copy (&local->loc, &local->parent_loc); + + loc_wipe (&local->parent_loc); + + quota_inode_loc_fill (NULL, local->loc.parent, &local->parent_loc); + + ret = quota_inode_ctx_get (local->loc.inode, this, &ctx); + if (ret < 0) + return -1; + + local->ctx = ctx; + + local->contri = (inode_contribution_t *) ctx->contribution_head.next; + + return 0; +} + + +int32_t +xattr_updation_done (call_frame_t *frame, + void *cookie, + xlator_t *this, + int32_t op_ret, + int32_t op_errno, + dict_t *dict) +{ + QUOTA_STACK_DESTROY (frame, this); + return 0; +} + + +int32_t +quota_inodelk_cbk (call_frame_t *frame, void *cookie, + xlator_t *this, int32_t op_ret, int32_t op_errno) +{ + int32_t ret = 0; + quota_local_t *local = NULL; + + trap (); + + local = frame->local; + + if (op_ret == -1 || local->err) { + gf_log (this->name, GF_LOG_INFO, "lock setting failed"); + xattr_updation_done (frame, NULL, this, 0, 0, NULL); + + return 0; + } + + gf_log (this->name, GF_LOG_DEBUG, + "inodelk released on %s", local->parent_loc.path); + + if (strcmp (local->parent_loc.path, "/") == 0) { + xattr_updation_done (frame, NULL, this, 0, 0, NULL); + } else { + ret = get_parent_inode_local (this, local); + if (ret < 0) { + xattr_updation_done (frame, NULL, this, 0, 0, NULL); + goto out; + } + + get_lock_on_parent (frame, this); + } +out: + return 0; +} + + +//now release lock on the parent inode +int32_t +quota_release_parent_lock (call_frame_t *frame, void *cookie, + xlator_t *this, int32_t op_ret, + int32_t op_errno) +{ + int32_t ret = 0; + struct gf_flock lock; + quota_local_t *local = NULL; + quota_inode_ctx_t *ctx = NULL; + + trap (); + + local = frame->local; + + ret = quota_inode_ctx_get (local->parent_loc.inode, this, &ctx); + if (ret < 0) + goto wind; + + LOCK (&ctx->lock); + { + ctx->dirty = 0; + } + UNLOCK (&ctx->lock); + +wind: + lock.l_type = F_UNLCK; + lock.l_whence = SEEK_SET; + lock.l_start = 0; + lock.l_len = 0; + lock.l_pid = 0; + + STACK_WIND (frame, + quota_inodelk_cbk, + FIRST_CHILD(this), + FIRST_CHILD(this)->fops->inodelk, + this->name, &local->parent_loc, + F_SETLKW, &lock); + + return 0; +} + + +int32_t +quota_mark_undirty (call_frame_t *frame, + void *cookie, + xlator_t *this, + int32_t op_ret, + int32_t op_errno, + dict_t *dict) +{ + int32_t ret = -1; + int64_t *size = NULL; + dict_t *newdict = NULL; + quota_local_t *local = NULL; + quota_inode_ctx_t *ctx = NULL; + marker_conf_t *priv = NULL; + + trap (); + + local = frame->local; + + if (op_ret == -1) { + gf_log (this->name, GF_LOG_WARNING, "%s occurred while" + " updating the size of %s", strerror (op_errno), + local->parent_loc.path); + + goto err; + } + + priv = this->private; + + //update the size of the parent inode + if (dict != NULL) { + ret = quota_inode_ctx_get (local->parent_loc.inode, this, &ctx); + if (ret < 0) + goto err; + + ret = dict_get_bin (dict, priv->size_key, (void **) &size); + if (ret < 0) + goto err; + + LOCK (&ctx->lock); + { + if (size) + ctx->size = ntoh64 (*size); + } + UNLOCK (&ctx->lock); + } + + newdict = dict_new (); + + if (!newdict) + goto err; + + ret = dict_set_int8 (newdict, priv->dirty_key, 0); + + if (ret == -1) + goto err; + + STACK_WIND (frame, quota_release_parent_lock, + FIRST_CHILD(this), + FIRST_CHILD(this)->fops->setxattr, + &local->parent_loc, newdict, 0); + + ret = 0; +err: + if (op_ret == -1 || ret == -1) { + local->err = 1; + + quota_release_parent_lock (frame, NULL, this, 0, 0); + } + + if (newdict) + dict_unref (newdict); + + return 0; +} + + +int32_t +quota_update_parent_size (call_frame_t *frame, + void *cookie, + xlator_t *this, + int32_t op_ret, + int32_t op_errno, + dict_t *dict) +{ + int64_t *size = NULL; + int32_t ret = -1; + dict_t *newdict = NULL; + marker_conf_t *priv = NULL; + quota_local_t *local = NULL; + quota_inode_ctx_t *ctx = NULL; + + trap (); + + local = frame->local; + + if (op_ret == -1) { + gf_log (this->name, GF_LOG_ERROR, "%s", strerror (op_errno)); + + goto err; + } + + priv = this->private; + + if (dict == NULL) + goto err; + + ret = quota_inode_ctx_get (local->parent_loc.inode, this, &ctx); + if (ret < 0) + goto err; + + newdict = dict_new (); + if (!newdict) { + ret = -1; + goto err; + } + + QUOTA_ALLOC_OR_GOTO (size, int64_t, ret, err); + + *size = ntoh64 (local->delta); + + ret = dict_set_bin (newdict, priv->size_key, size, 8); + if (ret < 0) + goto err; + + STACK_WIND (frame, + quota_mark_undirty, + FIRST_CHILD(this), + FIRST_CHILD(this)->fops->xattrop, + &local->parent_loc, + GF_XATTROP_ADD_ARRAY64, + newdict); + ret = 0; +err: + if (op_ret == -1 || ret < 0) { + local->err = 1; + quota_release_parent_lock (frame, NULL, this, 0, 0); + } + + if (dict) + dict_unref (newdict); + + return 0; +} + +int32_t +quota_update_inode_contribution (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) +{ + int32_t ret = -1; + int64_t *size = NULL; + int64_t *contri = NULL; + int64_t *delta = NULL; + char contri_key [512] = {0, }; + dict_t *newdict = NULL; + quota_local_t *local = NULL; + quota_inode_ctx_t *ctx = NULL; + marker_conf_t *priv = NULL; + inode_contribution_t *contribution = NULL; + + trap (); + + local = frame->local; + + if (op_ret == -1) { + gf_log (this->name, GF_LOG_ERROR, "failed to get size and " + "contribution with %s error", strerror (op_errno)); + goto err; + } + + priv = this->private; + + ctx = local->ctx; + contribution = local->contri; + + //prepare to update size & contribution of the inode + GET_CONTRI_KEY (contri_key, contribution->gfid, ret); + if (ret == -1) + goto err; + + LOCK (&ctx->lock); + { + if (local->loc.inode->ia_type == IA_IFDIR ) { + ret = dict_get_bin (dict, priv->size_key, + (void **) &size); + if (ret < 0) + goto unlock; + + ctx->size = ntoh64 (*size); + } else + ctx->size = buf->ia_size; + + ret = dict_get_bin (dict, contri_key, (void **) &contri); + if (ret < 0) + contribution->contribution = 0; + else + contribution->contribution = ntoh64 (*contri); + + ret = 0; + } +unlock: + UNLOCK (&ctx->lock); + + if (ret < 0) + goto err; + + newdict = dict_new (); + if (newdict == NULL) { + ret = -1; + goto err; + } + + local->delta = ctx->size - contribution->contribution; + + QUOTA_ALLOC_OR_GOTO (delta, int64_t, ret, err); + + *delta = hton64 (local->delta); + + ret = dict_set_bin (newdict, contri_key, delta, 8); + if (ret < 0) { + ret = -1; + goto err; + } + + STACK_WIND (frame, + quota_update_parent_size, + FIRST_CHILD(this), + FIRST_CHILD(this)->fops->xattrop, + &local->loc, + GF_XATTROP_ADD_ARRAY64, + newdict); + ret = 0; + +err: + if (op_ret == -1 || ret < 0) { + local->err = 1; + + quota_release_parent_lock (frame, NULL, this, 0, 0); + } + + if (newdict) + dict_unref (newdict); + + return 0; +} + +int32_t +quota_fetch_child_size_and_contri (call_frame_t *frame, void *cookie, + xlator_t *this, int32_t op_ret, + int32_t op_errno) +{ + int32_t ret = -1; + char contri_key [512] = {0, }; + dict_t *newdict = NULL; + quota_local_t *local = NULL; + marker_conf_t *priv = NULL; + quota_inode_ctx_t *ctx = NULL; + + trap (); + + local = frame->local; + + if (op_ret == -1) { + gf_log (this->name, GF_LOG_ERROR, + "%s couldnt mark dirty", local->parent_loc.path); + goto err; + } + + gf_log (this->name, GF_LOG_DEBUG, "%s marked dirty", local->parent_loc.path); + + priv = this->private; + + //update parent ctx + ret = quota_inode_ctx_get (local->parent_loc.inode, this, &ctx); + if (ret == -1) + goto err; + + LOCK (&ctx->lock); + { + ctx->dirty = 1; + } + UNLOCK (&ctx->lock); + + newdict = dict_new (); + if (newdict == NULL) + goto err; + + if (local->loc.inode->ia_type == IA_IFDIR) { + ret = dict_set_int64 (newdict, priv->size_key, 0); + } + + GET_CONTRI_KEY (contri_key, local->contri->gfid, ret); + if (ret < 0) + goto err; + + ret = dict_set_int64 (newdict, contri_key, 0); + + STACK_WIND (frame, quota_update_inode_contribution, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->lookup, &local->loc, newdict); + + ret = 0; + +err: + if (op_ret == -1 || ret == -1) { + local->err = 1; + + quota_release_parent_lock (frame, NULL, this, 0, 0); + } + + if (newdict) + dict_unref (newdict); + + return 0; +} + +int32_t +quota_markdirty (call_frame_t *frame, void *cookie, + xlator_t *this, int32_t op_ret, int32_t op_errno) +{ + int32_t ret = -1; + dict_t *dict = NULL; + quota_local_t *local = NULL; + marker_conf_t *priv = NULL; + + local = frame->local; + + if (op_ret == -1){ + gf_log (this->name, GF_LOG_ERROR, + "lock setting failed %s", strerror (op_errno)); + + local->err = 1; + + quota_inodelk_cbk (frame, NULL, this, 0, 0); + + return 0; + } + + gf_log (this->name, GF_LOG_TRACE, + "inodelk succeeded on %s", local->parent_loc.path); + + priv = this->private; + + dict = dict_new (); + if (!dict) { + ret = -1; + goto err; + } + + ret = dict_set_int8 (dict, priv->dirty_key, 1); + if (ret == -1) + goto err; + + STACK_WIND (frame, quota_fetch_child_size_and_contri, + FIRST_CHILD(this), + FIRST_CHILD(this)->fops->setxattr, + &local->parent_loc, dict, 0); + + ret = 0; +err: + if (ret == -1) { + local->err = 1; + + quota_release_parent_lock (frame, NULL, this, 0, 0); + } + + if (dict) + dict_unref (dict); + + return 0; +} + + +int32_t +get_lock_on_parent (call_frame_t *frame, xlator_t *this) +{ + struct gf_flock lock; + quota_local_t *local = NULL; + + GF_VALIDATE_OR_GOTO ("marker", frame, fr_destroy); + + local = frame->local; + + lock.l_len = 0; + lock.l_start = 0; + lock.l_type = F_WRLCK; + lock.l_whence = SEEK_SET; + + STACK_WIND (frame, + quota_markdirty, + FIRST_CHILD(this), + FIRST_CHILD(this)->fops->inodelk, + this->name, &local->parent_loc, F_SETLKW, &lock); + + return 0; + +fr_destroy: + QUOTA_STACK_DESTROY (frame, this); + + return 0; +} + + +int +start_quota_txn (xlator_t *this, loc_t *loc, + quota_inode_ctx_t *ctx, + inode_contribution_t *contri) +{ + int32_t ret = -1; + call_frame_t *frame = NULL; + quota_local_t *local = NULL; + + frame = create_frame (this, this->ctx->pool); + if (frame == NULL) + goto err; + + frame->root->lk_owner = cn++; + + local = quota_local_new (); + if (local == NULL) + goto fr_destroy; + + frame->local = local; + + ret = loc_copy (&local->loc, loc); + if (ret < 0) + goto local_unref; + + ret = quota_inode_loc_fill (NULL, local->loc.parent, + &local->parent_loc); + if (ret < 0) + goto local_unref; + + local->ctx = ctx; + local->contri = contri; + + get_lock_on_parent (frame, this); + + return 0; + +local_unref: + quota_local_unref (this, local); + +fr_destroy: + QUOTA_STACK_DESTROY (frame, this); + +err: + return -1; +} + + +int +initiate_quota_txn (xlator_t *this, loc_t *loc) +{ + int32_t ret = -1; + quota_inode_ctx_t *ctx = NULL; + inode_contribution_t *contribution = NULL; + + trap (); + + VALIDATE_OR_GOTO (loc, out); + + ret = quota_inode_ctx_get (loc->inode, this, &ctx); + if (ret == -1) { + gf_log (this->name, GF_LOG_WARNING, + "inode ctx get failed, aborting quota txn"); + ret = -1; + goto out; + } + + contribution = get_contribution_node (loc->parent, ctx); + if (contribution == NULL) + goto out; + + start_quota_txn (this, loc, ctx, contribution); +out: + return 0; +} + + +int32_t +validate_inode_size_contribution (xlator_t *this, + loc_t *loc, + quota_inode_ctx_t *ctx, + inode_contribution_t *contribution) +{ + if (ctx->size != contribution->contribution) + initiate_quota_txn (this, loc); + + return 0; +} + + +int32_t +inspect_directory_xattr (xlator_t *this, + loc_t *loc, + dict_t *dict, + struct iatt buf) +{ + int32_t ret = 0; + int8_t dirty = -1; + int64_t *size = NULL; + int64_t *contri = NULL; + char contri_key [512] = {0, }; + marker_conf_t *priv = NULL; + quota_inode_ctx_t *ctx = NULL; + inode_contribution_t *contribution = NULL; + + priv = this->private; + + ret = quota_inode_ctx_get (loc->inode, this, &ctx); + if (ret < 0) { + ctx = quota_inode_ctx_new (loc->inode, this); + if (ctx == NULL) { + gf_log (this->name, GF_LOG_WARNING, + "quota_inode_ctx_new failed"); + ret = -1; + goto out; + } + } + + ret = dict_get_bin (dict, priv->size_key, (void **) &size); + if (ret < 0) + goto out; + + ret = dict_get_int8 (dict, priv->dirty_key, &dirty); + if (ret < 0) + goto out; + + if (strcmp (loc->path, "/") != 0) { + contribution = add_new_contribution_node (this, ctx, loc); + if (contribution == NULL) { + gf_log (this->name, GF_LOG_DEBUG, + "cannot add a new contributio node"); + goto out; + } + + GET_CONTRI_KEY (contri_key, contribution->gfid, ret); + if (ret < 0) + goto out; + + ret = dict_get_bin (dict, contri_key, (void **) &contri); + if (ret < 0) + goto out; + + contribution->contribution = ntoh64 (*contri); + } + + ctx->size = ntoh64 (*size); + + ctx->dirty = dirty; + if (ctx->dirty == 1) + update_dirty_inode (this, loc, ctx, contribution); + + ret = 0; +out: + if (ret) + quota_set_inode_xattr (this, loc); + + return 0; +} + +int32_t +inspect_file_xattr (xlator_t *this, + loc_t *loc, + dict_t *dict, + struct iatt buf) +{ + int32_t ret = -1; + uint64_t contri_int = 0; + int64_t *contri_ptr = NULL; + char contri_key [512] = {0, }; + marker_conf_t *priv = NULL; + quota_inode_ctx_t *ctx = NULL; + inode_contribution_t *contribution = NULL; + + priv = this->private; + + ret = quota_inode_ctx_get (loc->inode, this, &ctx); + if (ret < 0) { + ctx = quota_inode_ctx_new (loc->inode, this); + if (ctx == NULL) { + gf_log (this->name, GF_LOG_WARNING, + "quota_inode_ctx_new failed"); + ret = -1; + goto out; + } + } + + contribution = add_new_contribution_node (this, ctx, loc); + if (contribution == NULL) + goto out; + + LOCK (&ctx->lock); + { + ctx->size = buf.ia_size; + } + UNLOCK (&ctx->lock); + + list_for_each_entry (contribution, &ctx->contribution_head, contri_list) { + GET_CONTRI_KEY (contri_key, contribution->gfid, ret); + if (ret < 0) + continue; + + ret = dict_get_bin (dict, contri_key, (void **) &contri_int); + if (ret == 0) { + contri_ptr = (int64_t *) contri_int; + + contribution->contribution = ntoh64 (*contri_ptr); + + ret = validate_inode_size_contribution + (this, loc, ctx, contribution); + } else + initiate_quota_txn (this, loc); + } + +out: + return ret; +} + +int32_t +quota_xattr_state (xlator_t *this, + loc_t *loc, + dict_t *dict, + struct iatt buf) +{ + if (buf.ia_type == IA_IFREG || + buf.ia_type == IA_IFLNK) { + k ++; + inspect_file_xattr (this, loc, dict, buf); + } else if (buf.ia_type == IA_IFDIR) + inspect_directory_xattr (this, loc, dict, buf); + + return 0; +} + +int32_t +quota_req_xattr (xlator_t *this, + loc_t *loc, + dict_t *dict) +{ + int32_t ret = -1; + marker_conf_t *priv = NULL; + + GF_VALIDATE_OR_GOTO ("marker", this, out); + GF_VALIDATE_OR_GOTO ("marker", loc, out); + GF_VALIDATE_OR_GOTO ("marker", dict, out); + + priv = this->private; + + //if not "/" then request contribution + if (strcmp (loc->path, "/") == 0) + goto set_size; + + ret = dict_set_contribution (this, dict, loc); + if (ret == -1) + goto out; + +set_size: + ret = dict_set_uint64 (dict, priv->size_key, 0); + if (ret < 0) { + ret = -1; + goto out; + } + + ret = dict_set_int8 (dict, priv->dirty_key, 0); + if (ret < 0) { + ret = -1; + goto out; + } + + ret = 0; + +out: + return ret; +} + + +int32_t +quota_removexattr_cbk (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno) +{ + QUOTA_STACK_DESTROY (frame, this); + + return 0; +} + +int32_t +quota_inode_remove_done (call_frame_t *frame, void *cookie, xlator_t *this, + int32_t op_ret, int32_t op_errno, dict_t *dict) +{ + int32_t ret = 0; + char contri_key [512] = {0, }; + quota_local_t *local = NULL; + + local = (quota_local_t *) frame->local; + + if (local->hl_count > 1) { + GET_CONTRI_KEY (contri_key, local->contri->gfid, ret); + + STACK_WIND (frame, quota_removexattr_cbk, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->removexattr, + &local->loc, contri_key); + ret = 0; + } + + if (strcmp (local->parent_loc.path, "/") != 0) { + get_parent_inode_local (this, local); + + start_quota_txn (this, &local->loc, local->ctx, local->contri); + } + + quota_local_unref (this, local); + + return 0; +} + +int32_t +reduce_parent_size (xlator_t *this, loc_t *loc) +{ + int32_t ret = -1; + int64_t *size = NULL; + dict_t *dict = NULL; + call_frame_t *frame = NULL; + marker_conf_t *priv = NULL; + quota_local_t *local = NULL; + quota_inode_ctx_t *ctx = NULL; + inode_contribution_t *contribution = NULL; + + GF_VALIDATE_OR_GOTO ("marker", this, out); + GF_VALIDATE_OR_GOTO ("marker", loc, out); + + priv = this->private; + + ret = quota_inode_ctx_get (loc->inode, this, &ctx); + if (ret < 0) + goto out; + + contribution = get_contribution_node (loc->parent, ctx); + if (contribution == NULL) + goto out; + + local = quota_local_new (); + if (local == NULL) { + ret = -1; + goto out; + } + + ret = quota_inode_loc_fill ((const char *) loc->parent->gfid, + loc->parent, &local->parent_loc); + if (ret < 0) + goto free_local; + + dict = dict_new (); + if (dict == NULL) { + ret = -1; + goto free_local; + } + + QUOTA_ALLOC_OR_GOTO (size, int64_t, ret, free_local); + + *size = hton64 (-contribution->contribution); + + ret = dict_set_bin (dict, priv->size_key, size, 8); + if (ret < 0) + goto free_size; + + frame = create_frame (this, this->ctx->pool); + if (!frame) { + ret = -1; + goto free_size; + } + + frame->local = local; + + STACK_WIND (frame, quota_inode_remove_done, FIRST_CHILD(this), + FIRST_CHILD(this)->fops->xattrop, &local->parent_loc, + GF_XATTROP_ADD_ARRAY64, dict); + ret = 0; + +free_size: + if (ret < 0) + GF_FREE (size); +free_local: + if (ret < 0) + quota_local_unref (this, local); +out: + dict_unref (dict); + + return ret; +} + + +int32_t +init_quota_priv (xlator_t *this) +{ + marker_conf_t *priv = NULL; + + priv = this->private; + + strcpy (volname, "quota"); + + gf_asprintf (&priv->size_key, QUOTA_XATTR_PREFIX + ".%s.size", volname); + + gf_asprintf (&priv->dirty_key, QUOTA_XATTR_PREFIX + ".%s.dirty", volname); + + return 0; +} + + +int32_t +quota_rename_update_newpath (xlator_t *this, loc_t *loc, inode_t *inode) +{ + int32_t ret = -1; + quota_inode_ctx_t *ctx = NULL; + inode_contribution_t *contribution = NULL; + + GF_VALIDATE_OR_GOTO ("marker", this, out); + GF_VALIDATE_OR_GOTO ("marker", loc, out); + GF_VALIDATE_OR_GOTO ("marker", inode, out); + + if (loc->inode == NULL) + loc->inode = inode_ref (inode); + + ret = quota_inode_ctx_get (loc->inode, this, &ctx); + if (ret < 0) + goto out; + + contribution = add_new_contribution_node (this, ctx, loc); + if (contribution == NULL) { + ret = -1; + goto out; + } + + initiate_quota_txn (this, loc); +out: + return ret; +} + +int32_t +quota_forget (xlator_t *this, quota_inode_ctx_t *ctx) +{ + inode_contribution_t *contri = NULL; + inode_contribution_t *next = NULL; + + GF_VALIDATE_OR_GOTO ("marker", this, out); + GF_VALIDATE_OR_GOTO ("marker", ctx, out); + + list_for_each_entry_safe (contri, next, &ctx->contribution_head, + contri_list) { + list_del (&contri->contri_list); + GF_FREE (contri); + } + + LOCK_DESTROY (&ctx->lock); + GF_FREE (ctx); +out: + return 0; +} -- cgit