summaryrefslogtreecommitdiffstats
path: root/xlators/features/snapview-client/src/snapview-client.c
diff options
context:
space:
mode:
authorRaghavendra Bhat <raghavendra@redhat.com>2014-05-07 20:13:43 +0530
committerVijay Bellur <vbellur@redhat.com>2014-05-29 09:25:46 -0700
commitcc0378d39f4082f51d5ef6e02b3007fe9e78cb31 (patch)
treef5c82bece9cf1a2fd79685ef2d89bcd2a5b8428f /xlators/features/snapview-client/src/snapview-client.c
parent58b9edee87bba3ffe812cf15f171926be017575b (diff)
user servicable snapshots
Change-Id: Idbf27dbe088e646a8ab81cedc5818413795895ea Signed-off-by: Raghavendra Bhat <raghavendra@redhat.com> Signed-off-by: Anand Subramanian <anands@redhat.com> Signed-off-by: Raghavendra Bhat <raghavendra@redhat.com> Reviewed-on: http://review.gluster.org/7700 Tested-by: Gluster Build System <jenkins@build.gluster.com> Reviewed-by: Vijay Bellur <vbellur@redhat.com>
Diffstat (limited to 'xlators/features/snapview-client/src/snapview-client.c')
-rw-r--r--xlators/features/snapview-client/src/snapview-client.c1687
1 files changed, 1687 insertions, 0 deletions
diff --git a/xlators/features/snapview-client/src/snapview-client.c b/xlators/features/snapview-client/src/snapview-client.c
new file mode 100644
index 00000000000..a86339901b9
--- /dev/null
+++ b/xlators/features/snapview-client/src/snapview-client.c
@@ -0,0 +1,1687 @@
+/*
+ Copyright (c) 2014 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
+
+#include "snapview-client.h"
+#include "inode.h"
+#include "byte-order.h"
+
+
+void
+svc_local_free (svc_local_t *local)
+{
+ if (local)
+ loc_wipe (&local->loc);
+}
+
+xlator_t *
+svc_get_subvolume (xlator_t *this, int inode_type)
+{
+ xlator_t *subvolume = NULL;
+
+ GF_VALIDATE_OR_GOTO ("snapview-client", this, out);
+
+ if (inode_type == VIRTUAL_INODE)
+ subvolume = SECOND_CHILD (this);
+ else
+ subvolume = FIRST_CHILD (this);
+
+out:
+ return subvolume;
+}
+
+int32_t
+__svc_inode_ctx_set (xlator_t *this, inode_t *inode, int inode_type)
+{
+ uint64_t value = 0;
+ int32_t ret = -1;
+
+ GF_VALIDATE_OR_GOTO ("snapview-client", this, out);
+ GF_VALIDATE_OR_GOTO (this->name, inode, out);
+
+ value = inode_type;
+
+ ret = __inode_ctx_set (inode, this, &value);
+
+out:
+ return ret;
+}
+
+int
+__svc_inode_ctx_get (xlator_t *this, inode_t *inode, int *inode_type)
+{
+ uint64_t value = 0;
+ int ret = -1;
+
+ GF_VALIDATE_OR_GOTO ("snapview-client", this, out);
+ GF_VALIDATE_OR_GOTO (this->name, inode, out);
+
+ ret = __inode_ctx_get (inode, this, &value);
+ if (ret < 0)
+ goto out;
+
+ *inode_type = (int)(value);
+
+out:
+ return ret;
+}
+
+int
+svc_inode_ctx_get (xlator_t *this, inode_t *inode, int *inode_type)
+{
+ int ret = -1;
+
+ GF_VALIDATE_OR_GOTO ("snapview-client", this, out);
+ GF_VALIDATE_OR_GOTO (this->name, inode, out);
+
+ LOCK (&inode->lock);
+ {
+ ret = __svc_inode_ctx_get (this, inode, inode_type);
+ }
+ UNLOCK (&inode->lock);
+
+out:
+ return ret;
+}
+
+int32_t
+svc_inode_ctx_set (xlator_t *this, inode_t *inode, int inode_type)
+{
+ int32_t ret = -1;
+
+ GF_VALIDATE_OR_GOTO ("snapview-client", this, out);
+ GF_VALIDATE_OR_GOTO (this->name, inode, out);
+
+ LOCK (&inode->lock);
+ {
+ ret = __svc_inode_ctx_set (this, inode, inode_type);
+ }
+ UNLOCK (&inode->lock);
+
+out:
+ return ret;
+}
+
+int32_t
+svc_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)
+{
+ svc_local_t *local = NULL;
+ inode_t *parent = NULL;
+ xlator_t *subvolume = NULL;
+ gf_boolean_t do_unwind = _gf_true;
+ int inode_type = -1;
+ int parent_type = -1;
+ int ret = -1;
+
+ local = frame->local;
+ subvolume = local->subvolume;
+ if (!subvolume) {
+ gf_log_callingfn (this->name, GF_LOG_ERROR, "path: %s, "
+ "gfid: %s ", local->loc.path,
+ inode?uuid_utoa (inode->gfid):"");
+ GF_ASSERT (0);
+ }
+
+ /* There is a possibility that, the client process just came online
+ and does not have the inode on which the lookup came. In that case,
+ the fresh inode created from fuse for the lookup fop, wont have
+ the inode context set without which svc cannot decide where to
+ STACK_WIND to. So by default it decides to send the fop to the
+ regular subvolume (i.e first child of the xlator). If lookup fails
+ on the regular volume, then there is a possibility that the lookup
+ is happening on a virtual inode (i.e history data residing in snaps).
+ So if lookup fails with ENOENT and the inode context is not there,
+ then send the lookup to the 2nd child of svc.
+ */
+ ret = svc_inode_ctx_get (this, inode, &inode_type);
+ if (op_ret) {
+ if (op_errno == ENOENT && (ret < 0) &&
+ !uuid_is_null (local->loc.gfid) &&
+ !__is_root_gfid (local->loc.gfid)) {
+ if (subvolume == FIRST_CHILD (this)) {
+ subvolume = SECOND_CHILD (this);
+ STACK_WIND (frame, svc_lookup_cbk, subvolume,
+ subvolume->fops->lookup,
+ &local->loc, xdata);
+ do_unwind = _gf_false;
+ }
+ }
+ goto out;
+ }
+
+ if (local->loc.parent)
+ parent = inode_ref (local->loc.parent);
+ else {
+ parent = inode_parent (inode, NULL, NULL);
+ if (!parent && !uuid_is_null (local->loc.pargfid)) {
+ parent = inode_find (inode->table,
+ local->loc.pargfid);
+ }
+ }
+
+ if (!__is_root_gfid (buf->ia_gfid) && parent) {
+ ret = svc_inode_ctx_get (this, parent, &parent_type);
+ if (ret < 0) {
+ op_ret = -1;
+ op_errno = EINVAL;
+ goto out;
+ }
+ }
+
+ if (subvolume == FIRST_CHILD (this))
+ inode_type = NORMAL_INODE;
+ else
+ inode_type = VIRTUAL_INODE;
+
+ ret = svc_inode_ctx_set (this, inode, inode_type);
+ if (ret)
+ gf_log (this->name, GF_LOG_ERROR, "failed to set inode type"
+ "into the context");
+
+out:
+ if (do_unwind) {
+ SVC_STACK_UNWIND (lookup, frame, op_ret, op_errno, inode, buf,
+ xdata, postparent);
+ }
+
+ if (parent)
+ inode_unref (parent);
+
+ return 0;
+}
+
+int32_t
+svc_lookup (call_frame_t *frame, xlator_t *this, loc_t *loc,
+ dict_t *xdata)
+{
+ int32_t ret = -1;
+ svc_local_t *local = NULL;
+ xlator_t *subvolume = NULL;
+ int op_ret = -1;
+ int op_errno = EINVAL;
+ inode_t *parent = NULL;
+ svc_private_t *priv = NULL;
+ dict_t *new_xdata = NULL;
+ int inode_type = -1;
+ int parent_type = -1;
+ gf_boolean_t wind = _gf_false;
+
+ GF_VALIDATE_OR_GOTO ("svc", this, out);
+ GF_VALIDATE_OR_GOTO (this->name, this->private, out);
+ GF_VALIDATE_OR_GOTO (this->name, frame, out);
+ GF_VALIDATE_OR_GOTO (this->name, loc, out);
+ GF_VALIDATE_OR_GOTO (this->name, loc->inode, out);
+
+ priv = this->private;
+
+ ret = svc_inode_ctx_get (this, loc->inode, &inode_type);
+ if (!__is_root_gfid (loc->gfid)) {
+ if (loc->parent) {
+ parent = inode_ref (loc->parent);
+ ret = svc_inode_ctx_get (this, loc->parent,
+ &parent_type);
+ } else {
+ parent = inode_parent (loc->inode, loc->pargfid, NULL);
+ if (parent)
+ ret = svc_inode_ctx_get (this, parent,
+ &parent_type);
+ }
+ }
+
+ local = mem_get0 (this->local_pool);
+ if (!local) {
+ gf_log (this->name, GF_LOG_ERROR, "failed to allocate local");
+ op_ret = -1;
+ op_errno = ENOMEM;
+ goto out;
+ }
+
+ frame->local = local;
+ loc_copy (&local->loc, loc);
+
+ if (__is_root_gfid (loc->inode->gfid)) {
+ subvolume = FIRST_CHILD (this);
+ GF_ASSERT (subvolume);
+ local->subvolume = subvolume;
+ wind = _gf_true;
+ goto out;
+ }
+
+ /* nfs sends nameless lookups directly using the gfid. In that case
+ loc->name will be NULL. So check if loc->name is NULL. If so, then
+ try to get the subvolume using inode context. But if the inode has
+ not been looked up yet, then send the lookup call to the first
+ subvolume.
+ */
+
+ if (!loc->name) {
+ if (uuid_is_null (loc->inode->gfid)) {
+ subvolume = FIRST_CHILD (this);
+ local->subvolume = subvolume;
+ wind = _gf_true;
+ goto out;
+ } else {
+ if (inode_type >= 0)
+ subvolume = svc_get_subvolume (this,
+ inode_type);
+ else
+ subvolume = FIRST_CHILD (this);
+ local->subvolume = subvolume;
+ wind = _gf_true;
+ goto out;
+ }
+ }
+
+ if (strcmp (loc->name, priv->path)) {
+ if (parent_type == NORMAL_INODE) {
+ subvolume = FIRST_CHILD (this);
+ local->subvolume = subvolume;
+ } else {
+ subvolume = SECOND_CHILD (this);
+ local->subvolume = subvolume;
+ }
+ } else {
+ if (parent_type == NORMAL_INODE) {
+ subvolume = SECOND_CHILD (this);
+ local->subvolume = subvolume;
+ /* Indication of whether the lookup is happening on the
+ entry point or not, to the snapview-server.
+ */
+ SVC_ENTRY_POINT_SET (this, xdata, op_ret, op_errno,
+ new_xdata, priv, ret, out);
+ } else {
+ /* Either error can be sent to application as
+ the entry point directory can exist only within
+ real directories and here the parent is a virtual
+ directory or send the call all the way to svs and
+ let it send the error back. For now it is sending
+ the error to application itself. (Saves the
+ network latency)
+ */
+ op_ret = -1;
+ op_errno = ENOENT;
+ goto out;
+ }
+ }
+
+ wind = _gf_true;
+
+out:
+ if (wind)
+ STACK_WIND (frame, svc_lookup_cbk,
+ subvolume, subvolume->fops->lookup, loc, xdata);
+ else
+ SVC_STACK_UNWIND (lookup, frame, op_ret, op_errno, NULL,
+ NULL, NULL, NULL);
+ if (new_xdata)
+ dict_unref (new_xdata);
+
+ if (parent)
+ inode_unref (parent);
+
+ return 0;
+}
+
+/* should all the fops be handled like lookup is supposed to be
+ handled? i.e just based on inode type decide where the call should
+ be sent and in the call back update the contexts.
+*/
+int32_t
+svc_stat (call_frame_t *frame, xlator_t *this, loc_t *loc,
+ dict_t *xdata)
+{
+ int32_t ret = -1;
+ int inode_type = -1;
+ xlator_t *subvolume = NULL;
+ int32_t op_ret = -1;
+ int32_t op_errno = EINVAL;
+ gf_boolean_t wind = _gf_false;
+
+ GF_VALIDATE_OR_GOTO ("svc", this, out);
+ GF_VALIDATE_OR_GOTO (this->name, frame, out);
+ GF_VALIDATE_OR_GOTO (this->name, loc, out);
+ GF_VALIDATE_OR_GOTO (this->name, loc->inode, out);
+
+ SVC_GET_SUBVOL_FROM_CTX (this, op_ret, op_errno, inode_type, ret,
+ loc->inode, subvolume, out);
+
+ STACK_WIND_TAIL (frame,subvolume, subvolume->fops->stat, loc, xdata);
+
+ wind = _gf_true;
+
+out:
+ if (!wind)
+ SVC_STACK_UNWIND (stat, frame, op_ret, op_errno,
+ NULL, NULL);
+ return 0;
+}
+
+int32_t
+svc_fstat (call_frame_t *frame, xlator_t *this, fd_t *fd, dict_t *xdata)
+{
+ int32_t ret = -1;
+ int inode_type = -1;
+ xlator_t *subvolume = NULL;
+ int32_t op_ret = -1;
+ int32_t op_errno = EINVAL;
+ gf_boolean_t wind = _gf_false;
+
+ GF_VALIDATE_OR_GOTO ("svc", this, out);
+ GF_VALIDATE_OR_GOTO (this->name, frame, out);
+ GF_VALIDATE_OR_GOTO (this->name, fd, out);
+ GF_VALIDATE_OR_GOTO (this->name, fd->inode, out);
+
+ SVC_GET_SUBVOL_FROM_CTX (this, op_ret, op_errno, inode_type, ret,
+ fd->inode, subvolume, out);
+
+ STACK_WIND_TAIL (frame, subvolume, subvolume->fops->fstat, fd, xdata);
+
+ wind = _gf_true;
+
+out:
+ if (!wind)
+ SVC_STACK_UNWIND (fstat, frame, op_ret, op_errno, NULL, NULL);
+
+ return ret;
+}
+
+/* If the inode represents a directory which is actually
+ present in a snapshot, then opendir on that directory
+ should be sent to the snap-view-server which opens
+ the directory in the corresponding graph.
+ In fact any opendir call on a virtual directory
+ should be sent to svs. Because if it fakes success
+ here, then later when readdir on that fd comes, there
+ will not be any corresponding fd opened on svs and
+ svc has to do things that open-behind is doing.
+*/
+int32_t
+svc_opendir (call_frame_t *frame, xlator_t *this, loc_t *loc, fd_t *fd,
+ dict_t *xdata)
+{
+ int32_t ret = -1;
+ int inode_type = -1;
+ xlator_t *subvolume = NULL;
+ int op_ret = -1;
+ int op_errno = EINVAL;
+ gf_boolean_t wind = _gf_false;
+
+ GF_VALIDATE_OR_GOTO ("svc", this, out);
+ GF_VALIDATE_OR_GOTO (this->name, frame, out);
+ GF_VALIDATE_OR_GOTO (this->name, loc, out);
+ GF_VALIDATE_OR_GOTO (this->name, loc->inode, out);
+ GF_VALIDATE_OR_GOTO (this->name, fd, out);
+
+ SVC_GET_SUBVOL_FROM_CTX (this, op_ret, op_errno, inode_type, ret,
+ loc->inode, subvolume, out);
+
+ STACK_WIND_TAIL (frame, subvolume, subvolume->fops->opendir, loc, fd,
+ xdata);
+
+ wind = _gf_true;
+
+out:
+ if (!wind)
+ SVC_STACK_UNWIND (opendir, frame, op_ret, op_errno, NULL, NULL);
+
+ return 0;
+}
+
+int32_t
+svc_setattr (call_frame_t *frame, xlator_t *this, loc_t *loc,
+ struct iatt *stbuf, int32_t valid, dict_t *xdata)
+{
+ int32_t ret = -1;
+ int inode_type = -1;
+ int op_ret = -1;
+ int op_errno = EINVAL;
+ gf_boolean_t wind = _gf_false;
+
+ GF_VALIDATE_OR_GOTO ("svc", this, out);
+ GF_VALIDATE_OR_GOTO (this->name, frame, out);
+ GF_VALIDATE_OR_GOTO (this->name, loc, out);
+ GF_VALIDATE_OR_GOTO (this->name, loc->inode, out);
+
+ ret = svc_inode_ctx_get (this, loc->inode, &inode_type);
+ if (ret < 0) {
+ op_ret = -1;
+ op_errno = EINVAL;
+ gf_log (this->name, GF_LOG_ERROR, "failed to get the inode "
+ "context for %s (gfid: %s)", loc->path,
+ uuid_utoa (loc->inode->gfid));
+ goto out;
+ }
+
+ if (inode_type == NORMAL_INODE) {
+ STACK_WIND_TAIL (frame, FIRST_CHILD (this),
+ FIRST_CHILD (this)->fops->setattr, loc, stbuf,
+ valid, xdata);
+ } else {
+ op_ret = -1;
+ op_errno = EPERM;
+ goto out;
+ }
+
+ wind = _gf_true;
+
+out:
+ if (!wind)
+ SVC_STACK_UNWIND (setattr, frame, op_ret, op_errno,
+ NULL, NULL, NULL);
+ return 0;
+}
+
+int32_t
+svc_fsetattr (call_frame_t *frame, xlator_t *this, fd_t *fd, struct iatt *stbuf,
+ int32_t valid, dict_t *xdata)
+{
+ int32_t ret = -1;
+ int inode_type = -1;
+ int op_ret = -1;
+ int op_errno = EINVAL;
+ gf_boolean_t wind = _gf_false;
+
+ GF_VALIDATE_OR_GOTO ("svc", this, out);
+ GF_VALIDATE_OR_GOTO (this->name, frame, out);
+ GF_VALIDATE_OR_GOTO (this->name, fd, out);
+ GF_VALIDATE_OR_GOTO (this->name, fd->inode, out);
+
+ ret = svc_inode_ctx_get (this, fd->inode, &inode_type);
+ if (ret < 0) {
+ gf_log (this->name, GF_LOG_ERROR, "failed to get the inode "
+ "context for %s", uuid_utoa (fd->inode->gfid));
+ op_ret = -1;
+ op_errno = EINVAL;
+ goto out;
+ }
+
+ if (inode_type == NORMAL_INODE) {
+ STACK_WIND_TAIL (frame, FIRST_CHILD (this),
+ FIRST_CHILD (this)->fops->fsetattr, fd, stbuf,
+ valid, xdata);
+ } else {
+ op_ret = -1;
+ op_errno = EPERM;
+ goto out;
+ }
+
+ wind = _gf_true;
+
+out:
+ if (!wind)
+ SVC_STACK_UNWIND (fsetattr, frame, op_ret, op_errno,
+ NULL, NULL, NULL);
+ return 0;
+}
+
+int32_t
+svc_getxattr (call_frame_t *frame, xlator_t *this, loc_t *loc, const char *name,
+ dict_t *xdata)
+{
+ int32_t ret = -1;
+ int inode_type = -1;
+ xlator_t *subvolume = NULL;
+ int op_ret = -1;
+ int op_errno = EINVAL;
+ gf_boolean_t wind = _gf_false;
+
+ GF_VALIDATE_OR_GOTO ("svc", this, out);
+ GF_VALIDATE_OR_GOTO (this->name, frame, out);
+ GF_VALIDATE_OR_GOTO (this->name, loc, out);
+ GF_VALIDATE_OR_GOTO (this->name, loc->inode, out);
+
+ SVC_GET_SUBVOL_FROM_CTX (this, op_ret, op_errno, inode_type, ret,
+ loc->inode, subvolume, out);
+
+ STACK_WIND_TAIL (frame, subvolume, subvolume->fops->getxattr, loc, name,
+ xdata);
+
+ wind = _gf_true;
+
+out:
+ if (!wind)
+ SVC_STACK_UNWIND (getxattr, frame, op_ret, op_errno,
+ NULL, NULL);
+
+ return 0;
+}
+
+int32_t
+svc_fgetxattr (call_frame_t *frame, xlator_t *this, fd_t *fd, const char *name,
+ dict_t *xdata)
+{
+ int32_t ret = -1;
+ int inode_type = -1;
+ xlator_t *subvolume = NULL;
+ gf_boolean_t wind = _gf_false;
+ int op_ret = -1;
+ int op_errno = EINVAL;
+
+ GF_VALIDATE_OR_GOTO ("svc", this, out);
+ GF_VALIDATE_OR_GOTO (this->name, frame, out);
+ GF_VALIDATE_OR_GOTO (this->name, fd, out);
+ GF_VALIDATE_OR_GOTO (this->name, fd->inode, out);
+
+ SVC_GET_SUBVOL_FROM_CTX (this, op_ret, op_errno, inode_type, ret,
+ fd->inode, subvolume, out);
+
+ STACK_WIND_TAIL (frame, subvolume,
+ subvolume->fops->fgetxattr, fd, name, xdata);
+
+ wind = _gf_true;
+
+out:
+ if (!wind)
+ SVC_STACK_UNWIND (fgetxattr, frame, op_ret, op_errno,
+ NULL, NULL);
+ return 0;
+}
+
+int32_t
+svc_setxattr (call_frame_t *frame, xlator_t *this, loc_t *loc, dict_t *dict,
+ int32_t flags, dict_t *xdata)
+{
+ int32_t ret = -1;
+ int inode_type = -1;
+ int op_ret = -1;
+ int op_errno = EINVAL;
+ gf_boolean_t wind = _gf_false;
+
+ GF_VALIDATE_OR_GOTO ("svc", this, out);
+ GF_VALIDATE_OR_GOTO (this->name, frame, out);
+ GF_VALIDATE_OR_GOTO (this->name, loc, out);
+ GF_VALIDATE_OR_GOTO (this->name, loc->inode, out);
+
+ ret = svc_inode_ctx_get (this, loc->inode, &inode_type);
+ if (ret < 0) {
+ gf_log (this->name, GF_LOG_ERROR, "failed to get inode context "
+ "for %s (gfid: %s)", loc->name,
+ uuid_utoa (loc->inode->gfid));
+ op_ret = -1;
+ op_errno = EINVAL;
+ goto out;
+ }
+
+ if (inode_type == NORMAL_INODE) {
+ STACK_WIND_TAIL (frame, FIRST_CHILD (this),
+ FIRST_CHILD (this)->fops->setxattr, loc, dict,
+ flags, xdata);
+ } else {
+ op_ret = -1;
+ op_errno = EPERM;
+ goto out;
+ }
+
+ wind = _gf_true;
+
+out:
+ if (!wind)
+ SVC_STACK_UNWIND (setxattr, frame, op_ret, op_errno,
+ NULL);
+
+ return 0;
+}
+
+int32_t
+svc_fsetxattr (call_frame_t *frame, xlator_t *this, fd_t *fd, dict_t *dict,
+ int32_t flags, dict_t *xdata)
+{
+ int32_t ret = -1;
+ int inode_type = -1;
+ int op_ret = -1;
+ int op_errno = EINVAL;
+ gf_boolean_t wind = _gf_false;
+
+ GF_VALIDATE_OR_GOTO ("svc", this, out);
+ GF_VALIDATE_OR_GOTO (this->name, frame, out);
+ GF_VALIDATE_OR_GOTO (this->name, fd, out);
+ GF_VALIDATE_OR_GOTO (this->name, fd->inode, out);
+
+ ret = svc_inode_ctx_get (this, fd->inode, &inode_type);
+ if (ret < 0) {
+ gf_log (this->name, GF_LOG_ERROR, "failed to get inode context "
+ "for %s", uuid_utoa (fd->inode->gfid));
+ op_ret = -1;
+ op_errno = EINVAL;
+ goto out;
+ }
+
+ if (inode_type == NORMAL_INODE) {
+ STACK_WIND_TAIL (frame, FIRST_CHILD (this),
+ FIRST_CHILD (this)->fops->fsetxattr, fd, dict,
+ flags, xdata);
+ } else {
+ op_ret = -1;
+ op_errno = EPERM;
+ goto out;
+ }
+
+ wind = _gf_true;
+
+out:
+ if (!wind)
+ STACK_UNWIND_STRICT (fsetxattr, frame, op_ret, op_errno,
+ NULL);
+
+ return 0;
+}
+
+int32_t
+svc_rmdir (call_frame_t *frame, xlator_t *this, loc_t *loc, int flags,
+ dict_t *xdata)
+{
+ int inode_type = -1;
+ int ret = -1;
+ int op_ret = -1;
+ int op_errno = EINVAL;
+ gf_boolean_t wind = _gf_false;
+
+ GF_VALIDATE_OR_GOTO ("svc", this, out);
+ GF_VALIDATE_OR_GOTO (this->name, frame, out);
+ GF_VALIDATE_OR_GOTO (this->name, loc, out);
+ GF_VALIDATE_OR_GOTO (this->name, loc->inode, out);
+
+ ret = svc_inode_ctx_get (this, loc->inode, &inode_type);
+ if (ret < 0) {
+ gf_log (this->name, GF_LOG_ERROR, "failed to get the inode "
+ "context for %s (gfid: %s)", loc->name,
+ uuid_utoa (loc->inode->gfid));
+ op_ret = -1;
+ op_errno = EINVAL;
+ goto out;
+ }
+
+ if (inode_type == NORMAL_INODE) {
+ STACK_WIND_TAIL (frame, FIRST_CHILD (this),
+ FIRST_CHILD (this)->fops->rmdir, loc, flags,
+ xdata);
+ } else {
+ op_ret = -1;
+ op_errno = EPERM;
+ goto out;
+ }
+
+ wind = _gf_true;
+
+out:
+ if (!wind)
+ SVC_STACK_UNWIND (rmdir, frame, op_ret, op_errno,
+ NULL, NULL, NULL);
+ return 0;
+}
+
+int32_t
+svc_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, dict_t *xdata)
+{
+ int inode_type = -1;
+ int ret = -1;
+
+ if (op_ret < 0)
+ goto out;
+
+ inode_type = NORMAL_INODE;
+ ret = svc_inode_ctx_set (this, inode, inode_type);
+ if (ret)
+ gf_log (this->name, GF_LOG_ERROR, "failed to set inode "
+ "context");
+
+
+out:
+ SVC_STACK_UNWIND (mkdir, frame, op_ret, op_errno, inode,
+ buf, preparent, postparent, xdata);
+ return 0;
+}
+
+int32_t
+svc_mkdir (call_frame_t *frame, xlator_t *this, loc_t *loc, mode_t mode,
+ mode_t umask, dict_t *xdata)
+{
+ int parent_type = -1;
+ int ret = -1;
+ int op_ret = -1;
+ int op_errno = EINVAL;
+ svc_private_t *priv = NULL;
+ gf_boolean_t wind = _gf_false;
+
+ GF_VALIDATE_OR_GOTO ("svc", this, out);
+ GF_VALIDATE_OR_GOTO (this->name, this->private, out);
+ GF_VALIDATE_OR_GOTO (this->name, frame, out);
+ GF_VALIDATE_OR_GOTO (this->name, loc, out);
+ GF_VALIDATE_OR_GOTO (this->name, loc->inode, out);
+
+ priv = this->private;
+
+ ret = svc_inode_ctx_get (this, loc->parent, &parent_type);
+ if (ret < 0) {
+ gf_log (this->name, GF_LOG_ERROR, "failed to get the inode "
+ "context for %s", uuid_utoa (loc->parent->gfid));
+ op_ret = -1;
+ op_errno = EINVAL;
+ goto out;
+ }
+
+ if (strcmp (loc->name, priv->path) && parent_type == NORMAL_INODE) {
+ STACK_WIND (frame, svc_mkdir_cbk, FIRST_CHILD (this),
+ FIRST_CHILD (this)->fops->mkdir, loc, mode,
+ umask, xdata);
+ } else {
+ op_ret = -1;
+ op_errno = EPERM;
+ goto out;
+ }
+
+ wind = _gf_true;
+
+out:
+ if (!wind)
+ SVC_STACK_UNWIND (mkdir, frame, op_ret, op_errno, NULL, NULL,
+ NULL, NULL, NULL);
+ return 0;
+}
+
+int32_t
+svc_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 inode_type = -1;
+ int ret = -1;
+
+ if (op_ret < 0)
+ goto out;
+
+ inode_type = NORMAL_INODE;
+ ret = svc_inode_ctx_set (this, inode, inode_type);
+ if (ret)
+ gf_log (this->name, GF_LOG_ERROR, "failed to set inode "
+ "context");
+
+out:
+ SVC_STACK_UNWIND (mknod, frame, op_ret, op_errno, inode,
+ buf, preparent, postparent, xdata);
+ return 0;
+}
+
+int32_t
+svc_mknod (call_frame_t *frame, xlator_t *this, loc_t *loc, mode_t mode,
+ dev_t rdev, mode_t umask, dict_t *xdata)
+{
+ int parent_type = -1;
+ int ret = -1;
+ int op_ret = -1;
+ int op_errno = EINVAL;
+ svc_private_t *priv = NULL;
+ gf_boolean_t wind = _gf_false;
+
+ GF_VALIDATE_OR_GOTO ("svc", this, out);
+ GF_VALIDATE_OR_GOTO (this->name, this->private, out);
+ GF_VALIDATE_OR_GOTO (this->name, frame, out);
+ GF_VALIDATE_OR_GOTO (this->name, loc, out);
+ GF_VALIDATE_OR_GOTO (this->name, loc->inode, out);
+
+ priv = this->private;
+
+ ret = svc_inode_ctx_get (this, loc->parent, &parent_type);
+ if (ret < 0) {
+ gf_log (this->name, GF_LOG_ERROR, "failed to get the inode "
+ "context for %s", uuid_utoa (loc->parent->gfid));
+ op_ret = -1;
+ op_errno = EINVAL;
+ goto out;
+ }
+
+ if (strcmp (loc->name, priv->path) && parent_type == NORMAL_INODE) {
+ STACK_WIND (frame, svc_mknod_cbk, FIRST_CHILD (this),
+ FIRST_CHILD (this)->fops->mknod, loc, mode,
+ rdev, umask, xdata);
+ } else {
+ op_ret = -1;
+ op_errno = EPERM;
+ goto out;
+ }
+
+ wind = _gf_true;
+
+out:
+ if (!wind)
+ SVC_STACK_UNWIND (mknod, frame, op_ret, op_errno, NULL, NULL,
+ NULL, NULL, NULL);
+ return 0;
+}
+
+/* If the flags of the open call contain O_WRONLY or O_RDWR and the inode is
+ a virtual inode, then unwind the call back with EPERM. Otherwise simply
+ STACK_WIND the call to the first child of svc xlator.
+*/
+int32_t
+svc_open (call_frame_t *frame, xlator_t *this, loc_t *loc, int32_t flags,
+ fd_t *fd, dict_t *xdata)
+{
+ xlator_t *subvolume = NULL;
+ int inode_type = -1;
+ int op_ret = -1;
+ int op_errno = EINVAL;
+ int ret = -1;
+ gf_boolean_t wind = _gf_false;
+
+ GF_VALIDATE_OR_GOTO ("svc", this, out);
+ GF_VALIDATE_OR_GOTO (this->name, frame, out);
+ GF_VALIDATE_OR_GOTO (this->name, loc, out);
+ GF_VALIDATE_OR_GOTO (this->name, loc->inode, out);
+ GF_VALIDATE_OR_GOTO (this->name, fd, out);
+
+ /* Another way is to STACK_WIND to normal subvolume, if inode
+ type is not there in the context. If the file actually resides
+ in snapshots, then ENOENT would be returned. Needs more analysis.
+ */
+ SVC_GET_SUBVOL_FROM_CTX (this, op_ret, op_errno, inode_type, ret,
+ loc->inode, subvolume, out);
+
+ if (((flags & O_ACCMODE) == O_WRONLY) ||
+ ((flags & O_ACCMODE) == O_RDWR)) {
+ if (subvolume != FIRST_CHILD (this)) {
+ op_ret = -1;
+ op_errno = EINVAL;
+ goto out;
+ }
+ }
+
+ STACK_WIND_TAIL (frame, subvolume, subvolume->fops->open, loc,
+ flags, fd, xdata);
+
+ wind = _gf_true;
+
+out:
+ if (!wind)
+ SVC_STACK_UNWIND (open, frame, op_ret, op_errno, NULL,
+ NULL);
+ return 0;
+}
+
+int32_t
+svc_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 *stbuf, struct iatt *preparent,
+ struct iatt *postparent, dict_t *xdata)
+{
+ int inode_type = -1;
+ int ret = -1;
+
+ if (op_ret < 0)
+ goto out;
+
+ inode_type = NORMAL_INODE;
+ ret = svc_inode_ctx_set (this, inode, inode_type);
+ if (ret)
+ gf_log (this->name, GF_LOG_ERROR, "failed to set inode "
+ "context");
+
+out:
+ SVC_STACK_UNWIND (create, frame, op_ret, op_errno, fd,
+ inode, stbuf, preparent, postparent, xdata);
+
+ return 0;
+}
+
+int32_t
+svc_create (call_frame_t *frame, xlator_t *this,
+ loc_t *loc, int32_t flags, mode_t mode,
+ mode_t umask, fd_t *fd, dict_t *xdata)
+{
+ int parent_type = -1;
+ int ret = -1;
+ int op_ret = -1;
+ int op_errno = EINVAL;
+ svc_private_t *priv = NULL;
+ gf_boolean_t wind = _gf_false;
+
+ GF_VALIDATE_OR_GOTO ("svc", this, out);
+ GF_VALIDATE_OR_GOTO (this->name, this->private, out);
+ GF_VALIDATE_OR_GOTO (this->name, frame, out);
+ GF_VALIDATE_OR_GOTO (this->name, loc, out);
+ GF_VALIDATE_OR_GOTO (this->name, loc->inode, out);
+ GF_VALIDATE_OR_GOTO (this->name, fd, out);
+
+ priv = this->private;
+
+ ret = svc_inode_ctx_get (this, loc->parent, &parent_type);
+ if (ret < 0) {
+ gf_log (this->name, GF_LOG_ERROR, "failed to get the inode "
+ "context for %s", uuid_utoa (loc->parent->gfid));
+ op_ret = -1;
+ op_errno = EINVAL;
+ goto out;
+ }
+
+ if (strcmp (loc->name, priv->path) && parent_type == NORMAL_INODE) {
+ STACK_WIND (frame, svc_create_cbk, FIRST_CHILD (this),
+ FIRST_CHILD (this)->fops->create, loc, flags,
+ mode, umask, fd, xdata);
+ } else {
+ op_ret = -1;
+ op_errno = EPERM;
+ goto out;
+ }
+
+ wind = _gf_true;
+
+out:
+ if (!wind)
+ SVC_STACK_UNWIND (create, frame, op_ret, op_errno,
+ NULL, NULL, NULL, NULL, NULL, NULL);
+ return 0;
+}
+
+int32_t
+svc_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, dict_t *xdata)
+{
+ int inode_type = -1;
+ int ret = -1;
+
+ if (op_ret < 0)
+ goto out;
+
+ inode_type = NORMAL_INODE;
+ ret = svc_inode_ctx_set (this, inode, inode_type);
+ if (ret)
+ gf_log (this->name, GF_LOG_ERROR, "failed to set inode "
+ "context");
+
+out:
+ SVC_STACK_UNWIND (symlink, frame, op_ret, op_errno, inode,
+ buf, preparent, postparent, xdata);
+
+ return 0;
+}
+
+int32_t
+svc_symlink (call_frame_t *frame, xlator_t *this, const char *linkpath,
+ loc_t *loc, mode_t umask, dict_t *xdata)
+{
+ int parent_type = -1;
+ int op_ret = -1;
+ int op_errno = EINVAL;
+ int ret = -1;
+ svc_private_t *priv = NULL;
+ gf_boolean_t wind = _gf_false;
+
+ GF_VALIDATE_OR_GOTO ("svc", this, out);
+ GF_VALIDATE_OR_GOTO (this->name, frame, out);
+ GF_VALIDATE_OR_GOTO (this->name, this->private, out);
+ GF_VALIDATE_OR_GOTO (this->name, loc, out);
+ GF_VALIDATE_OR_GOTO (this->name, loc->inode, out);
+
+ priv = this->private;
+
+ ret = svc_inode_ctx_get (this, loc->parent, &parent_type);
+ if (ret < 0) {
+ gf_log (this->name, GF_LOG_ERROR, "failed to get the inode "
+ "context for %s", uuid_utoa (loc->parent->gfid));
+ op_ret = -1;
+ op_errno = EINVAL;
+ goto out;
+ }
+
+ if (strcmp (loc->name, priv->path) && parent_type == NORMAL_INODE) {
+ STACK_WIND (frame, svc_symlink_cbk, FIRST_CHILD (this),
+ FIRST_CHILD (this)->fops->symlink, linkpath, loc,
+ umask, xdata);
+ } else {
+ op_ret = -1;
+ op_errno = EPERM;
+ goto out;
+ }
+
+ wind = _gf_true;
+
+out:
+ if (!wind)
+ SVC_STACK_UNWIND (symlink, frame, op_ret, op_errno,
+ NULL, NULL, NULL, NULL, NULL);
+ return 0;
+}
+
+int32_t
+svc_unlink (call_frame_t *frame, xlator_t *this, loc_t *loc, int flags,
+ dict_t *xdata)
+{
+ int inode_type = -1;
+ int op_ret = -1;
+ int op_errno = EINVAL;
+ int ret = -1;
+ gf_boolean_t wind = _gf_false;
+
+ GF_VALIDATE_OR_GOTO ("svc", this, out);
+ GF_VALIDATE_OR_GOTO (this->name, frame, out);
+ GF_VALIDATE_OR_GOTO (this->name, loc, out);
+ GF_VALIDATE_OR_GOTO (this->name, loc->inode, out);
+
+ ret = svc_inode_ctx_get (this, loc->inode, &inode_type);
+ if (ret < 0) {
+ gf_log (this->name, GF_LOG_ERROR, "failed to get the inode "
+ "context for %s", uuid_utoa (loc->parent->gfid));
+ op_ret = -1;
+ op_errno = EINVAL;
+ goto out;
+ }
+
+ if (inode_type == NORMAL_INODE) {
+ STACK_WIND_TAIL (frame, FIRST_CHILD (this),
+ FIRST_CHILD (this)->fops->unlink, loc, flags,
+ xdata);
+ } else {
+ op_ret = -1;
+ op_errno = EPERM;
+ goto out;
+ }
+
+ wind = _gf_true;
+
+out:
+ if (!wind)
+ SVC_STACK_UNWIND (unlink, frame, op_ret, op_errno, NULL, NULL,
+ NULL);
+ return 0;
+}
+
+int32_t
+svc_readv (call_frame_t *frame, xlator_t *this, fd_t *fd, size_t size,
+ off_t offset, uint32_t flags, dict_t *xdata)
+{
+ int inode_type = -1;
+ xlator_t *subvolume = NULL;
+ int ret = -1;
+ int op_ret = -1;
+ int op_errno = EINVAL;
+ gf_boolean_t wind = _gf_false;
+
+ GF_VALIDATE_OR_GOTO ("svc", this, out);
+ GF_VALIDATE_OR_GOTO (this->name, frame, out);
+ GF_VALIDATE_OR_GOTO (this->name, fd, out);
+ GF_VALIDATE_OR_GOTO (this->name, fd->inode, out);
+
+ SVC_GET_SUBVOL_FROM_CTX (this, op_ret, op_errno, inode_type, ret,
+ fd->inode, subvolume, out);
+
+ STACK_WIND_TAIL (frame, subvolume, subvolume->fops->readv,
+ fd, size, offset, flags, xdata);
+
+ wind = _gf_true;
+
+out:
+ if (!wind)
+ SVC_STACK_UNWIND (readv, frame, op_ret, op_errno, NULL, 0, NULL,
+ NULL, NULL);
+ return 0;
+}
+
+int32_t
+svc_readlink (call_frame_t *frame, xlator_t *this,
+ loc_t *loc, size_t size, dict_t *xdata)
+{
+ int inode_type = -1;
+ xlator_t *subvolume = NULL;
+ int ret = -1;
+ int op_ret = -1;
+ int op_errno = EINVAL;
+ gf_boolean_t wind = _gf_false;
+
+ GF_VALIDATE_OR_GOTO ("svc", this, out);
+ GF_VALIDATE_OR_GOTO (this->name, frame, out);
+ GF_VALIDATE_OR_GOTO (this->name, loc, out);
+ GF_VALIDATE_OR_GOTO (this->name, loc->inode, out);
+
+ SVC_GET_SUBVOL_FROM_CTX (this, op_ret, op_errno, inode_type, ret,
+ loc->inode, subvolume, out);
+
+ STACK_WIND_TAIL (frame, subvolume, subvolume->fops->readlink, loc, size,
+ xdata);
+
+ wind = _gf_true;
+
+out:
+ if (!wind)
+ STACK_UNWIND_STRICT (readlink, frame, op_ret, op_errno, NULL, NULL,
+ NULL);
+ return 0;
+}
+
+int32_t
+svc_access (call_frame_t *frame, xlator_t *this, loc_t *loc, int32_t mask,
+ dict_t *xdata)
+{
+ int ret = -1;
+ int inode_type = -1;
+ xlator_t *subvolume = NULL;
+ int op_ret = -1;
+ int op_errno = EINVAL;
+ gf_boolean_t wind = _gf_false;
+
+ GF_VALIDATE_OR_GOTO ("svc", this, out);
+ GF_VALIDATE_OR_GOTO (this->name, frame, out);
+ GF_VALIDATE_OR_GOTO (this->name, loc, out);
+ GF_VALIDATE_OR_GOTO (this->name, loc->inode, out);
+
+ SVC_GET_SUBVOL_FROM_CTX (this, op_ret, op_errno, inode_type, ret,
+ loc->inode, subvolume, out);
+
+ STACK_WIND_TAIL (frame, subvolume, subvolume->fops->access, loc, mask,
+ xdata);
+
+ wind = _gf_true;
+
+out:
+ if (!wind)
+ SVC_STACK_UNWIND (access, frame, op_ret, op_errno, NULL);
+
+ return 0;
+}
+
+int32_t
+svc_readdir (call_frame_t *frame, xlator_t *this, fd_t *fd,
+ size_t size, off_t off,
+ dict_t *xdata)
+{
+ int inode_type = -1;
+ xlator_t *subvolume = NULL;
+ int ret = -1;
+ int op_ret = -1;
+ int op_errno = EINVAL;
+ gf_boolean_t wind = _gf_false;
+
+ GF_VALIDATE_OR_GOTO ("svc", this, out);
+ GF_VALIDATE_OR_GOTO (this->name, frame, out);
+ GF_VALIDATE_OR_GOTO (this->name, fd, out);
+ GF_VALIDATE_OR_GOTO (this->name, fd->inode, out);
+
+ SVC_GET_SUBVOL_FROM_CTX (this, op_ret, op_errno, inode_type, ret,
+ fd->inode, subvolume, out);
+
+ STACK_WIND_TAIL (frame, subvolume, subvolume->fops->readdir, fd, size,
+ off, xdata);
+
+ wind = _gf_true;
+
+out:
+ if (!wind)
+ SVC_STACK_UNWIND (readdir, frame, op_ret, op_errno, NULL,
+ NULL);
+ return 0;
+}
+
+int32_t
+svc_readdirp_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)
+{
+ gf_dirent_t *entry = NULL;
+ svc_local_t *local = NULL;
+ gf_boolean_t real = _gf_true;
+ int inode_type = -1;
+ int ret = -1;
+
+ if (op_ret < 0)
+ goto out;
+
+ local = frame->local;
+ frame->local = NULL;
+
+ if (local->subvolume == FIRST_CHILD (this))
+ real = _gf_true;
+ else
+ real = _gf_false;
+
+ list_for_each_entry (entry, &entries->list, list) {
+ if (!entry->inode)
+ continue;
+
+ if (real)
+ inode_type = NORMAL_INODE;
+ else
+ inode_type = VIRTUAL_INODE;
+
+ ret = svc_inode_ctx_set (this, entry->inode, inode_type);
+ if (ret)
+ gf_log (this->name, GF_LOG_ERROR, "failed to set inode "
+ "context");
+ }
+
+
+out:
+ SVC_STACK_UNWIND (readdirp, frame, op_ret, op_errno, entries, xdata);
+
+ return 0;
+}
+
+int32_t
+svc_readdirp (call_frame_t *frame, xlator_t *this, fd_t *fd,
+ size_t size, off_t off,
+ dict_t *xdata)
+{
+ int inode_type = -1;
+ xlator_t *subvolume = NULL;
+ svc_local_t *local = NULL;
+ int ret = -1;
+ int op_ret = -1;
+ int op_errno = EINVAL;
+ gf_boolean_t wind = _gf_false;
+
+ GF_VALIDATE_OR_GOTO ("svc", this, out);
+ GF_VALIDATE_OR_GOTO (this->name, frame, out);
+ GF_VALIDATE_OR_GOTO (this->name, fd, out);
+ GF_VALIDATE_OR_GOTO (this->name, fd->inode, out);
+
+ local = mem_get0 (this->local_pool);
+ if (!local) {
+ gf_log (this->name, GF_LOG_ERROR, "failed to allocate local");
+ goto out;
+ }
+
+ SVC_GET_SUBVOL_FROM_CTX (this, op_ret, op_errno, inode_type, ret,
+ fd->inode, subvolume, out);
+
+ local->subvolume = subvolume;
+ frame->local = local;
+
+ STACK_WIND (frame, svc_readdirp_cbk, subvolume,
+ subvolume->fops->readdirp, fd, size, off, xdata);
+
+ wind = _gf_true;
+
+out:
+ if (!wind)
+ SVC_STACK_UNWIND (readdirp, frame, op_ret, op_errno, NULL, NULL);
+
+ return 0;
+}
+
+/* Renaming the entries from or to snapshots is not allowed as the snapshots
+ are read-only.
+*/
+int32_t
+svc_rename (call_frame_t *frame, xlator_t *this, loc_t *oldloc,
+ loc_t *newloc, dict_t *xdata)
+{
+ int src_inode_type = -1;
+ int dst_inode_type = -1;
+ int dst_parent_type = -1;
+ int32_t op_ret = -1;
+ int32_t op_errno = 0;
+ int32_t ret = -1;
+ gf_boolean_t wind = _gf_false;
+
+ GF_VALIDATE_OR_GOTO ("svc", this, out);
+ GF_VALIDATE_OR_GOTO (this->name, frame, out);
+ GF_VALIDATE_OR_GOTO (this->name, oldloc, out);
+ GF_VALIDATE_OR_GOTO (this->name, oldloc->inode, out);
+ GF_VALIDATE_OR_GOTO (this->name, newloc, out);
+
+ ret = svc_inode_ctx_get (this, oldloc->inode, &src_inode_type);
+ if (ret < 0) {
+ gf_log (this->name, GF_LOG_ERROR, "failed to get the inode "
+ "context for the inode %s",
+ uuid_utoa (oldloc->inode->gfid));
+ op_ret = -1;
+ op_errno = EINVAL;
+ goto out;
+ }
+
+ if (src_inode_type == VIRTUAL_INODE) {
+ gf_log (this->name, GF_LOG_ERROR, "rename happening on a entry"
+ " %s residing in snapshot", oldloc->name);
+ op_ret = -1;
+ op_errno = EPERM;
+ goto out;
+ }
+
+ if (newloc->inode) {
+ ret = svc_inode_ctx_get (this, newloc->inode, &dst_inode_type);
+ if (!ret && dst_inode_type == VIRTUAL_INODE) {
+ gf_log (this->name, GF_LOG_ERROR, "rename of %s "
+ "happening to a entry %s residing in snapshot",
+ oldloc->name, newloc->name);
+ op_ret = -1;
+ op_errno = EPERM;
+ goto out;
+ }
+ }
+
+ if (dst_inode_type < 0) {
+ ret = svc_inode_ctx_get (this, newloc->parent,
+ &dst_parent_type);
+ if (!ret && dst_parent_type == VIRTUAL_INODE) {
+ gf_log (this->name, GF_LOG_ERROR, "rename of %s "
+ "happening to a entry %s residing in snapshot",
+ oldloc->name, newloc->name);
+ op_ret = -1;
+ op_errno = EPERM;
+ goto out;
+ }
+ }
+
+ STACK_WIND_TAIL (frame, FIRST_CHILD (this),
+ FIRST_CHILD (this)->fops->rename, oldloc, newloc,
+ xdata);
+
+ wind = _gf_true;
+
+out:
+ if (!wind)
+ SVC_STACK_UNWIND (rename, frame, op_ret, op_errno, NULL,
+ NULL, NULL, NULL, NULL, NULL);
+ return 0;
+}
+
+/* Creating hardlinks for the files from the snapshot is not allowed as it
+ will be equivalent of creating hardlinks across different filesystems.
+ And so is vise versa.
+*/
+int32_t
+svc_link (call_frame_t *frame, xlator_t *this, loc_t *oldloc, loc_t *newloc,
+ dict_t *xdata)
+{
+ int src_inode_type = -1;
+ int dst_parent_type = -1;
+ int32_t op_ret = -1;
+ int32_t op_errno = 0;
+ int32_t ret = -1;
+ gf_boolean_t wind = _gf_false;
+
+ GF_VALIDATE_OR_GOTO ("svc", this, out);
+ GF_VALIDATE_OR_GOTO (this->name, frame, out);
+ GF_VALIDATE_OR_GOTO (this->name, oldloc, out);
+ GF_VALIDATE_OR_GOTO (this->name, oldloc->inode, out);
+ GF_VALIDATE_OR_GOTO (this->name, newloc, out);
+
+ ret = svc_inode_ctx_get (this, oldloc->inode, &src_inode_type);
+ if (!ret && src_inode_type == VIRTUAL_INODE) {
+ gf_log (this->name, GF_LOG_ERROR, "rename happening on a entry"
+ " %s residing in snapshot", oldloc->name);
+ op_ret = -1;
+ op_errno = EPERM;
+ goto out;
+ }
+
+ ret = svc_inode_ctx_get (this, newloc->parent, &dst_parent_type);
+ if (!ret && dst_parent_type == VIRTUAL_INODE) {
+ gf_log (this->name, GF_LOG_ERROR, "rename of %s "
+ "happening to a entry %s residing in snapshot",
+ oldloc->name, newloc->name);
+ op_ret = -1;
+ op_errno = EPERM;
+ goto out;
+ }
+
+ STACK_WIND_TAIL (frame, FIRST_CHILD (this),
+ FIRST_CHILD (this)->fops->link, oldloc, newloc, xdata);
+
+ wind = _gf_true;
+
+out:
+ if (!wind)
+ SVC_STACK_UNWIND (link, frame, op_ret, op_errno,
+ NULL, NULL, NULL, NULL, NULL);
+ return 0;
+}
+
+int32_t
+svc_removexattr (call_frame_t *frame, xlator_t *this, loc_t *loc,
+ const char *name, dict_t *xdata)
+{
+ int ret = -1;
+ int inode_type = -1;
+ int op_ret = -1;
+ int op_errno = EINVAL;
+ gf_boolean_t wind = _gf_false;
+
+ GF_VALIDATE_OR_GOTO ("svc", this, out);
+ GF_VALIDATE_OR_GOTO (this->name, frame, out);
+ GF_VALIDATE_OR_GOTO (this->name, loc, out);
+ GF_VALIDATE_OR_GOTO (this->name, loc->inode, out);
+
+ ret = svc_inode_ctx_get (this, loc->inode, &inode_type);
+ if (ret < 0) {
+ gf_log (this->name, GF_LOG_ERROR, "failed to get te inode "
+ "context for %s (gfid: %s)", loc->path,
+ uuid_utoa (loc->inode->gfid));
+ op_ret = -1;
+ op_errno = EINVAL;
+ goto out;
+ }
+
+ if (inode_type == NORMAL_INODE) {
+ STACK_WIND_TAIL (frame, FIRST_CHILD (this),
+ FIRST_CHILD (this)->fops->removexattr, loc,
+ name, xdata);
+ } else {
+ op_ret = -1;
+ op_errno = EPERM;
+ goto out;
+ }
+
+ wind = _gf_true;
+
+out:
+ if (!wind)
+ SVC_STACK_UNWIND (removexattr, frame, op_ret, op_errno,
+ NULL);
+
+ return 0;
+}
+
+int32_t
+svc_flush (call_frame_t *frame, xlator_t *this,
+ fd_t *fd, dict_t *xdata)
+{
+ int32_t op_ret = -1;
+ int32_t op_errno = 0;
+ int ret = -1;
+ int inode_type = -1;
+ xlator_t *subvolume = NULL;
+ gf_boolean_t wind = _gf_false;
+
+ GF_VALIDATE_OR_GOTO ("svc", this, out);
+ GF_VALIDATE_OR_GOTO (this->name, frame, out);
+ GF_VALIDATE_OR_GOTO (this->name, fd, out);
+ GF_VALIDATE_OR_GOTO (this->name, fd->inode, out);
+
+ SVC_GET_SUBVOL_FROM_CTX (this, op_ret, op_errno, inode_type, ret,
+ fd->inode, subvolume, out);
+
+ STACK_WIND_TAIL (frame, subvolume, subvolume->fops->flush, fd, xdata);
+
+ wind = _gf_true;
+
+out:
+ if (!wind)
+ SVC_STACK_UNWIND (flush, frame, op_ret, op_errno, NULL);
+
+ return 0;
+}
+
+int32_t
+svc_forget (xlator_t *this, inode_t *inode)
+{
+ int ret = -1;
+ uint64_t value = 0;
+
+ GF_VALIDATE_OR_GOTO ("svc", this, out);
+ GF_VALIDATE_OR_GOTO (this->name, inode, out);
+
+ ret = inode_ctx_del (inode, this, &value);
+ if (ret) {
+ gf_log (this->name, GF_LOG_ERROR, "failed to delete inode "
+ "context for %s", uuid_utoa (inode->gfid));
+ goto out;
+ }
+
+out:
+ return 0;
+}
+
+int
+reconfigure (xlator_t *this, dict_t *options)
+{
+ svc_private_t *priv = NULL;
+
+ priv = this->private;
+
+ GF_OPTION_RECONF ("snapshot-directory", priv->path, options, str, out);
+
+out:
+ return 0;
+}
+
+int32_t
+mem_acct_init (xlator_t *this)
+{
+ int32_t ret = -1;
+
+ if (!this)
+ return ret;
+
+ ret = xlator_mem_acct_init (this, gf_svc_mt_end + 1);
+
+ if (ret != 0) {
+ gf_log (this->name, GF_LOG_WARNING, "Memory accounting"
+ " init failed");
+ return ret;
+ }
+
+ return ret;
+}
+
+int32_t
+init (xlator_t *this)
+{
+ svc_private_t *private = NULL;
+ int ret = -1;
+ int children = 0;
+ xlator_list_t *xl = NULL;
+
+ if (!this->children) {
+ gf_log (this->name, GF_LOG_ERROR,
+ "configured without any child");
+ goto out;
+ }
+
+ xl = this->children;
+ while (xl) {
+ children++;
+ xl = xl->next;
+ }
+
+ if (children != 2) {
+ gf_log (this->name, GF_LOG_ERROR, "snap-view-client has got "
+ "%d subvolumes. It can have only 2 subvolumes.",
+ children);
+ goto out;
+ }
+
+ /* This can be the top of graph in certain cases */
+ if (!this->parents) {
+ gf_log (this->name, GF_LOG_DEBUG,
+ "dangling volume. check volfile ");
+ }
+
+ private = GF_CALLOC (1, sizeof (*private), gf_svc_mt_svc_private_t);
+ if (!private)
+ goto out;
+
+ GF_OPTION_INIT ("snapshot-directory", private->path, str, out);
+
+ this->private = private;
+ this->local_pool = mem_pool_new (svc_local_t, 128);
+ if (!this->local_pool) {
+ gf_log (this->name, GF_LOG_ERROR, "could not get mem pool for "
+ "frame->local");
+ goto out;
+ }
+
+ ret = 0;
+
+out:
+ if (ret)
+ GF_FREE (private);
+
+ return ret;
+}
+
+void
+fini (xlator_t *this)
+{
+ svc_private_t *priv = NULL;
+
+ if (!this)
+ return;
+
+ priv = this->private;
+ if (!priv)
+ return;
+
+ this->private = NULL;
+
+ GF_FREE (priv->path);
+ GF_FREE (priv);
+
+ return;
+}
+
+struct xlator_fops fops = {
+ .lookup = svc_lookup,
+ .opendir = svc_opendir,
+ .stat = svc_stat,
+ .fstat = svc_fstat,
+ .rmdir = svc_rmdir,
+ .rename = svc_rename,
+ .mkdir = svc_mkdir,
+ .open = svc_open,
+ .unlink = svc_unlink,
+ .setattr = svc_setattr,
+ .getxattr = svc_getxattr,
+ .setxattr = svc_setxattr,
+ .fsetxattr = svc_fsetxattr,
+ .readv = svc_readv,
+ .readdir = svc_readdir,
+ .readdirp = svc_readdirp,
+ .create = svc_create,
+ .readlink = svc_readlink,
+ .mknod = svc_mknod,
+ .symlink = svc_symlink,
+ .flush = svc_flush,
+ .link = svc_link,
+ .access = svc_access,
+ .removexattr = svc_removexattr,
+};
+
+struct xlator_cbks cbks = {
+ .forget = svc_forget,
+};
+
+struct volume_options options[] = {
+ { .key = {"snapshot-directory"},
+ .type = GF_OPTION_TYPE_STR,
+ .default_value = ".snaps",
+ },
+ { .key = {NULL} },
+};