diff options
author | Raghavendra Bhat <raghavendra@redhat.com> | 2014-05-07 20:13:43 +0530 |
---|---|---|
committer | Vijay Bellur <vbellur@redhat.com> | 2014-05-29 09:25:46 -0700 |
commit | cc0378d39f4082f51d5ef6e02b3007fe9e78cb31 (patch) | |
tree | f5c82bece9cf1a2fd79685ef2d89bcd2a5b8428f /xlators | |
parent | 58b9edee87bba3ffe812cf15f171926be017575b (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')
-rw-r--r-- | xlators/features/Makefile.am | 2 | ||||
-rw-r--r-- | xlators/features/quota/src/quota.c | 3 | ||||
-rw-r--r-- | xlators/features/snapview-client/Makefile.am | 1 | ||||
-rw-r--r-- | xlators/features/snapview-client/src/Makefile.am | 15 | ||||
-rw-r--r-- | xlators/features/snapview-client/src/snapview-client-mem-types.h | 23 | ||||
-rw-r--r-- | xlators/features/snapview-client/src/snapview-client.c | 1687 | ||||
-rw-r--r-- | xlators/features/snapview-client/src/snapview-client.h | 110 | ||||
-rw-r--r-- | xlators/features/snapview-server/Makefile.am | 1 | ||||
-rw-r--r-- | xlators/features/snapview-server/src/Makefile.am | 22 | ||||
-rw-r--r-- | xlators/features/snapview-server/src/snapview-server-mem-types.h | 25 | ||||
-rw-r--r-- | xlators/features/snapview-server/src/snapview-server.c | 2395 | ||||
-rw-r--r-- | xlators/features/snapview-server/src/snapview-server.h | 88 | ||||
-rw-r--r-- | xlators/mount/fuse/src/fuse-resolve.c | 5 | ||||
-rw-r--r-- | xlators/protocol/server/src/server-resolve.c | 31 | ||||
-rw-r--r-- | xlators/protocol/server/src/server-rpc-fops.c | 5 |
15 files changed, 4404 insertions, 9 deletions
diff --git a/xlators/features/Makefile.am b/xlators/features/Makefile.am index 1fdd474c20a..8093441a043 100644 --- a/xlators/features/Makefile.am +++ b/xlators/features/Makefile.am @@ -1,4 +1,4 @@ SUBDIRS = locks quota read-only mac-compat quiesce marker index barrier \ - protect compress changelog gfid-access $(GLUPY_SUBDIR) qemu-block # trash path-converter # filter + protect compress changelog gfid-access $(GLUPY_SUBDIR) qemu-block snapview-client snapview-server # trash path-converter # filter CLEANFILES = diff --git a/xlators/features/quota/src/quota.c b/xlators/features/quota/src/quota.c index 913741e0fda..c3e730bd1fd 100644 --- a/xlators/features/quota/src/quota.c +++ b/xlators/features/quota/src/quota.c @@ -1013,7 +1013,8 @@ quota_fill_inodectx (xlator_t *this, inode_t *inode, dict_t *dict, goto unlock; } - if (loc->name == NULL) + /* do nothing if it is a nameless lookup */ + if (loc->name == NULL || !loc->parent) goto unlock; list_for_each_entry (dentry, &ctx->parents, next) { diff --git a/xlators/features/snapview-client/Makefile.am b/xlators/features/snapview-client/Makefile.am new file mode 100644 index 00000000000..af437a64d6d --- /dev/null +++ b/xlators/features/snapview-client/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = src diff --git a/xlators/features/snapview-client/src/Makefile.am b/xlators/features/snapview-client/src/Makefile.am new file mode 100644 index 00000000000..8a3f6fe4ea2 --- /dev/null +++ b/xlators/features/snapview-client/src/Makefile.am @@ -0,0 +1,15 @@ +xlator_LTLIBRARIES = snapview-client.la +xlatordir = $(libdir)/glusterfs/$(PACKAGE_VERSION)/xlator/features + +snapview_client_la_LDFLAGS = -module -avoid-version + +snapview_client_la_SOURCES = snapview-client.c +snapview_client_la_LIBADD = $(top_builddir)/libglusterfs/src/libglusterfs.la + +noinst_HEADERS = snapview-client.h snapview-client-mem-types.h + +AM_CPPFLAGS = $(GF_CPPFLAGS) -I$(top_srcdir)/libglusterfs/src + +AM_CFLAGS = -Wall $(GF_CFLAGS) + +CLEANFILES = diff --git a/xlators/features/snapview-client/src/snapview-client-mem-types.h b/xlators/features/snapview-client/src/snapview-client-mem-types.h new file mode 100644 index 00000000000..b6fa0ef7cfb --- /dev/null +++ b/xlators/features/snapview-client/src/snapview-client-mem-types.h @@ -0,0 +1,23 @@ +/* + 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 _SVC_MEM_TYPES_H +#define _SVC_MEM_TYPES_H + +#include "mem-types.h" + +enum svc_mem_types { + gf_svc_mt_svc_private_t = gf_common_mt_end + 1, + gf_svc_mt_svc_local_t, + gf_svc_mt_svc_inode_t, + gf_svc_mt_end +}; + +#endif 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} }, +}; diff --git a/xlators/features/snapview-client/src/snapview-client.h b/xlators/features/snapview-client/src/snapview-client.h new file mode 100644 index 00000000000..000490393c1 --- /dev/null +++ b/xlators/features/snapview-client/src/snapview-client.h @@ -0,0 +1,110 @@ + /* + 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 __SNAP_VIEW_CLIENT_H__ +#define __SNAP_VIEW_CLIENT_H__ + +#ifndef _CONFIG_H +#define _CONFIG_H +#include "config.h" +#endif + +#include "glusterfs.h" +#include "logging.h" +#include "dict.h" +#include "xlator.h" +#include "defaults.h" +#include "snapview-client-mem-types.h" + +struct __svc_local { + loc_t loc; + xlator_t *subvolume; +}; +typedef struct __svc_local svc_local_t; + +void +svc_local_free (svc_local_t *local); + +#define SVC_STACK_UNWIND(fop, frame, params ...) do { \ + svc_local_t *__local = NULL; \ + if (frame) { \ + __local = frame->local; \ + frame->local = NULL; \ + } \ + STACK_UNWIND_STRICT (fop, frame, params); \ + svc_local_free (__local); \ + } while (0) + +#define SVC_ENTRY_POINT_SET(this, xdata, op_ret, op_errno, new_xdata, \ + priv, ret, label) \ + do { \ + if (!xdata) { \ + xdata = new_xdata = dict_new (); \ + if (!new_xdata) { \ + gf_log (this->name, GF_LOG_ERROR, \ + "failed to allocate new dict"); \ + op_ret = -1; \ + op_errno = ENOMEM; \ + goto label; \ + } \ + } \ + ret = dict_set_str (xdata, "entry-point", "true"); \ + if (ret) { \ + gf_log (this->name, GF_LOG_ERROR, \ + "failed to set dict"); \ + op_ret = -1; \ + op_errno = ENOMEM; \ + goto label; \ + } \ + } while (0); + +#define SVC_GET_SUBVOL_FROM_CTX(this, op_ret, op_errno, inode_type, ret, \ + inode, subvolume, label) \ + do { \ + ret = svc_inode_ctx_get (this, inode, &inode_type); \ + if (ret < 0) { \ + gf_log (this->name, GF_LOG_ERROR, \ + "inode context not found for gfid %s", \ + uuid_utoa (inode->gfid)); \ + op_ret = -1; \ + op_errno = EINVAL; \ + goto label; \ + } \ + \ + subvolume = svc_get_subvolume (this, inode_type); \ + } while (0); + +struct svc_private { + char *path; //might be helpful for samba +}; +typedef struct svc_private svc_private_t; + +typedef enum { + NORMAL_INODE = 1, + VIRTUAL_INODE +} inode_type_t; + +void svc_local_free (svc_local_t *local); + +xlator_t * +svc_get_subvolume (xlator_t *this, int inode_type); + +int +__svc_inode_ctx_get (xlator_t *this, inode_t *inode, int *inode_type); + +int +svc_inode_ctx_get (xlator_t *this, inode_t *inode, int *inode_type); + +int32_t +svc_inode_ctx_set (xlator_t *this, inode_t *inode, int inode_type); + +void +svc_local_free (svc_local_t *local); + +#endif /* __SNAP_VIEW_CLIENT_H__ */ diff --git a/xlators/features/snapview-server/Makefile.am b/xlators/features/snapview-server/Makefile.am new file mode 100644 index 00000000000..af437a64d6d --- /dev/null +++ b/xlators/features/snapview-server/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = src diff --git a/xlators/features/snapview-server/src/Makefile.am b/xlators/features/snapview-server/src/Makefile.am new file mode 100644 index 00000000000..0966ae4cc56 --- /dev/null +++ b/xlators/features/snapview-server/src/Makefile.am @@ -0,0 +1,22 @@ +xlator_LTLIBRARIES = snapview-server.la +xlatordir = $(libdir)/glusterfs/$(PACKAGE_VERSION)/xlator/features + +snapview_server_la_LDFLAGS = -module -avoid-version + +snapview_server_la_SOURCES = snapview-server.c +snapview_server_la_LIBADD = $(top_builddir)/libglusterfs/src/libglusterfs.la\ + $(top_builddir)/api/src/libgfapi.la\ + $(RLLIBS) $(top_builddir)/rpc/xdr/src/libgfxdr.la \ + $(top_builddir)/rpc/rpc-lib/src/libgfrpc.la + +noinst_HEADERS = snapview-server.h snapview-server-mem-types.h + +AM_CPPFLAGS = $(GF_CPPFLAGS) -I$(top_srcdir)/libglusterfs/src \ + -I$(top_srcdir)/api/src \ + -I$(top_srcdir)/rpc/rpc-lib/src \ + -I$(top_srcdir)/rpc/xdr/src \ + -DDATADIR=\"$(localstatedir)\" + +AM_CFLAGS = -Wall $(GF_CFLAGS) + +CLEANFILES = diff --git a/xlators/features/snapview-server/src/snapview-server-mem-types.h b/xlators/features/snapview-server/src/snapview-server-mem-types.h new file mode 100644 index 00000000000..6820b5e16f1 --- /dev/null +++ b/xlators/features/snapview-server/src/snapview-server-mem-types.h @@ -0,0 +1,25 @@ +/* + 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 __SNAP_VIEW_MEM_TYPES_H +#define __SNAP_VIEW_MEM_TYPES_H + +#include "mem-types.h" + +enum snapview_mem_types { + gf_svs_mt_priv_t = gf_common_mt_end + 1, + gf_svs_mt_svs_inode_t, + gf_svs_mt_dirents_t, + gf_svs_mt_svs_fd_t, + gf_svs_mt_end +}; + +#endif + diff --git a/xlators/features/snapview-server/src/snapview-server.c b/xlators/features/snapview-server/src/snapview-server.c new file mode 100644 index 00000000000..655372347fb --- /dev/null +++ b/xlators/features/snapview-server/src/snapview-server.c @@ -0,0 +1,2395 @@ +/* + 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-server.h" +#include "snapview-server-mem-types.h" + +/* + * Helper functions + */ + +int +__svs_inode_ctx_set (xlator_t *this, inode_t *inode, svs_inode_t *svs_inode) +{ + uint64_t value = 0; + int ret = -1; + + GF_VALIDATE_OR_GOTO ("snapview-server", this, out); + GF_VALIDATE_OR_GOTO (this->name, inode, out); + GF_VALIDATE_OR_GOTO (this->name, svs_inode, out); + + value = (uint64_t)(long) svs_inode; + + ret = __inode_ctx_set (inode, this, &value); + +out: + return ret; +} + + +svs_inode_t * +__svs_inode_ctx_get (xlator_t *this, inode_t *inode) +{ + svs_inode_t *svs_inode = NULL; + uint64_t value = 0; + int ret = -1; + + GF_VALIDATE_OR_GOTO ("snapview-server", this, out); + GF_VALIDATE_OR_GOTO (this->name, inode, out); + + ret = __inode_ctx_get (inode, this, &value); + if (ret) + goto out; + + svs_inode = (svs_inode_t *) ((long) value); + +out: + return svs_inode; +} + + +svs_inode_t * +svs_inode_ctx_get (xlator_t *this, inode_t *inode) +{ + svs_inode_t *svs_inode = NULL; + + GF_VALIDATE_OR_GOTO ("snapview-server", this, out); + GF_VALIDATE_OR_GOTO (this->name, inode, out); + + LOCK (&inode->lock); + { + svs_inode = __svs_inode_ctx_get (this, inode); + } + UNLOCK (&inode->lock); + +out: + return svs_inode; +} + +int32_t +svs_inode_ctx_set (xlator_t *this, inode_t *inode, svs_inode_t *svs_inode) +{ + int32_t ret = -1; + + GF_VALIDATE_OR_GOTO ("snapview-server", this, out); + GF_VALIDATE_OR_GOTO (this->name, inode, out); + GF_VALIDATE_OR_GOTO (this->name, svs_inode, out); + + LOCK (&inode->lock); + { + ret = __svs_inode_ctx_set (this, inode, svs_inode); + } + UNLOCK (&inode->lock); + +out: + return ret; +} + +svs_inode_t * +svs_inode_new () +{ + svs_inode_t *svs_inode = NULL; + + svs_inode = GF_CALLOC (1, sizeof (*svs_inode), gf_svs_mt_svs_inode_t); + + return svs_inode; +} + +svs_inode_t * +svs_inode_ctx_get_or_new (xlator_t *this, inode_t *inode) +{ + svs_inode_t *svs_inode = NULL; + int ret = -1; + + GF_VALIDATE_OR_GOTO ("snapview-server", this, out); + GF_VALIDATE_OR_GOTO (this->name, inode, out); + + LOCK (&inode->lock); + { + svs_inode = __svs_inode_ctx_get (this, inode); + if (!svs_inode) { + svs_inode = svs_inode_new (this, inode); + if (svs_inode) { + ret = __svs_inode_ctx_set (this, inode, + svs_inode); + if (ret) { + GF_FREE (svs_inode); + svs_inode = NULL; + } + } + } + } + UNLOCK (&inode->lock); + +out: + return svs_inode; +} + +svs_fd_t * +svs_fd_new () +{ + svs_fd_t *svs_fd = NULL; + + svs_fd = GF_CALLOC (1, sizeof (*svs_fd), gf_svs_mt_svs_fd_t); + + return svs_fd; +} + +int +__svs_fd_ctx_set (xlator_t *this, fd_t *fd, svs_fd_t *svs_fd) +{ + uint64_t value = 0; + int ret = -1; + + GF_VALIDATE_OR_GOTO ("snapview-server", this, out); + GF_VALIDATE_OR_GOTO (this->name, fd, out); + GF_VALIDATE_OR_GOTO (this->name, svs_fd, out); + + value = (uint64_t)(long) svs_fd; + + ret = __fd_ctx_set (fd, this, value); + +out: + return ret; +} + + +svs_fd_t * +__svs_fd_ctx_get (xlator_t *this, fd_t *fd) +{ + svs_fd_t *svs_fd = NULL; + uint64_t value = 0; + int ret = -1; + + GF_VALIDATE_OR_GOTO ("snapview-server", this, out); + GF_VALIDATE_OR_GOTO (this->name, fd, out); + + ret = __fd_ctx_get (fd, this, &value); + if (ret) + return NULL; + + svs_fd = (svs_fd_t *) ((long) value); + +out: + return svs_fd; +} + + +svs_fd_t * +svs_fd_ctx_get (xlator_t *this, fd_t *fd) +{ + svs_fd_t *svs_fd = NULL; + + GF_VALIDATE_OR_GOTO ("snapview-server", this, out); + GF_VALIDATE_OR_GOTO (this->name, fd, out); + + LOCK (&fd->lock); + { + svs_fd = __svs_fd_ctx_get (this, fd); + } + UNLOCK (&fd->lock); + +out: + return svs_fd; +} + +int32_t +svs_fd_ctx_set (xlator_t *this, fd_t *fd, svs_fd_t *svs_fd) +{ + int32_t ret = -1; + + GF_VALIDATE_OR_GOTO ("snapview-server", this, out); + GF_VALIDATE_OR_GOTO (this->name, fd, out); + GF_VALIDATE_OR_GOTO (this->name, svs_fd, out); + + LOCK (&fd->lock); + { + ret = __svs_fd_ctx_set (this, fd, svs_fd); + } + UNLOCK (&fd->lock); + +out: + return ret; +} + +svs_fd_t * +__svs_fd_ctx_get_or_new (xlator_t *this, fd_t *fd) +{ + svs_fd_t *svs_fd = NULL; + int ret = -1; + glfs_t *fs = NULL; + glfs_object_t *object = NULL; + svs_inode_t *inode_ctx = NULL; + glfs_fd_t *glfd = NULL; + inode_t *inode = NULL; + + GF_VALIDATE_OR_GOTO ("snapview-server", this, out); + GF_VALIDATE_OR_GOTO (this->name, fd, out); + + inode = fd->inode; + svs_fd = __svs_fd_ctx_get (this, fd); + if (svs_fd) { + ret = 0; + goto out; + } + + svs_fd = svs_fd_new (this, fd); + if (!svs_fd) { + gf_log (this->name, GF_LOG_ERROR, "failed to allocate new fd " + "context for gfid %s", uuid_utoa (inode->gfid)); + goto out; + } + + if (fd_is_anonymous (fd)) { + inode_ctx = svs_inode_ctx_get (this, inode); + if (!inode_ctx) { + gf_log (this->name, GF_LOG_ERROR, "failed to get inode " + "context for %s", uuid_utoa (inode->gfid)); + goto out; + } + + fs = inode_ctx->fs; + object = inode_ctx->object; + + if (inode->ia_type == IA_IFDIR) { + glfd = glfs_h_opendir (fs, object); + if (!glfd) { + gf_log (this->name, GF_LOG_ERROR, "failed to " + "open the directory %s", + uuid_utoa (inode->gfid)); + goto out; + } + } + + if (inode->ia_type == IA_IFREG) { + glfd = glfs_h_open (fs, object, O_RDONLY|O_LARGEFILE); + if (!glfd) { + gf_log (this->name, GF_LOG_ERROR, "failed to " + "open the file %s", + uuid_utoa (inode->gfid)); + goto out; + } + } + + svs_fd->fd = glfd; + } + + ret = __svs_fd_ctx_set (this, fd, svs_fd); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "failed to set fd context " + "for gfid %s", uuid_utoa (inode->gfid)); + if (svs_fd->fd) { + if (inode->ia_type == IA_IFDIR) { + ret = glfs_closedir (svs_fd->fd); + if (ret) + gf_log (this->name, GF_LOG_ERROR, + "failed to close the fd for %s", + uuid_utoa (inode->gfid)); + } + if (inode->ia_type == IA_IFREG) { + ret = glfs_close (svs_fd->fd); + if (ret) + gf_log (this->name, GF_LOG_ERROR, + "failed to close the fd for %s", + uuid_utoa (inode->gfid)); + } + } + ret = -1; + } + +out: + if (ret) { + GF_FREE (svs_fd); + svs_fd = NULL; + } + + return svs_fd; +} + +svs_fd_t * +svs_fd_ctx_get_or_new (xlator_t *this, fd_t *fd) +{ + svs_fd_t *svs_fd = NULL; + + GF_VALIDATE_OR_GOTO ("snapview-server", this, out); + GF_VALIDATE_OR_GOTO (this->name, fd, out); + + LOCK (&fd->lock); + { + svs_fd = __svs_fd_ctx_get_or_new (this, fd); + } + UNLOCK (&fd->lock); + +out: + return svs_fd; +} + +void +svs_fill_ino_from_gfid (struct iatt *buf) +{ + uint64_t temp_ino = 0; + int j = 0; + int i = 0; + xlator_t *this = NULL; + + this = THIS; + + GF_VALIDATE_OR_GOTO ("snapview-server", this, out); + GF_VALIDATE_OR_GOTO (this->name, buf, out); + + /* consider least significant 8 bytes of value out of gfid */ + if (uuid_is_null (buf->ia_gfid)) { + buf->ia_ino = -1; + goto out; + } + for (i = 15; i > (15 - 8); i--) { + temp_ino += (uint64_t)(buf->ia_gfid[i]) << j; + j += 8; + } + buf->ia_ino = temp_ino; +out: + return; +} + +void +svs_iatt_fill (uuid_t gfid, struct iatt *buf) +{ + struct timeval tv = {0, }; + xlator_t *this = NULL; + + this = THIS; + + GF_VALIDATE_OR_GOTO ("snapview-server", this, out); + GF_VALIDATE_OR_GOTO (this->name, buf, out); + + buf->ia_type = IA_IFDIR; + buf->ia_uid = 0; + buf->ia_gid = 0; + buf->ia_size = 0; + buf->ia_nlink = 2; + buf->ia_blocks = 8; + buf->ia_size = 4096; + + uuid_copy (buf->ia_gfid, gfid); + svs_fill_ino_from_gfid (buf); + + buf->ia_prot = ia_prot_from_st_mode (0755); + + gettimeofday (&tv, 0); + + buf->ia_mtime = buf->ia_atime = buf->ia_ctime = tv.tv_sec; + buf->ia_mtime_nsec = buf->ia_atime_nsec = buf->ia_ctime_nsec = + (tv.tv_usec * 1000); + +out: + return; +} + +snap_dirent_t * +svs_get_snap_dirent (xlator_t *this, const char *name) +{ + svs_private_t *private = NULL; + int i = 0; + snap_dirent_t *dirents = NULL; + snap_dirent_t *tmp_dirent = NULL; + snap_dirent_t *dirent = NULL; + + GF_VALIDATE_OR_GOTO ("snapview-server", this, out); + GF_VALIDATE_OR_GOTO (this->name, this->private, out); + GF_VALIDATE_OR_GOTO (this->name, name, out); + + private = this->private; + + dirents = private->dirents; + + tmp_dirent = dirents; + for (i = 0; i < private->num_snaps; i++) { + if (!strcmp (tmp_dirent->name, name)) { + dirent = tmp_dirent; + break; + } + tmp_dirent++; + } + +out: + return dirent; +} + +glfs_t * +svs_initialise_snapshot_volume (xlator_t *this, const char *name) +{ + svs_private_t *priv = NULL; + int32_t ret = -1; + snap_dirent_t *dirent = NULL; + char volname[PATH_MAX] = {0, }; + glfs_t *fs = NULL; + int loglevel = GF_LOG_INFO; + char logfile[PATH_MAX] = {0, }; + + GF_VALIDATE_OR_GOTO ("snapview-server", this, out); + GF_VALIDATE_OR_GOTO (this->name, this->private, out); + GF_VALIDATE_OR_GOTO (this->name, name, out); + + priv = this->private; + + dirent = svs_get_snap_dirent (this, name); + if (!dirent) { + gf_log (this->name, GF_LOG_ERROR, "snap entry for name %s " + "not found", name); + goto out; + } + + if (dirent->fs) { + ret = 0; + fs = dirent->fs; + goto out; + } + + snprintf (volname, sizeof (volname), "/snaps/%s/%s", + dirent->name, dirent->uuid); + + fs = glfs_new (volname); + if (!fs) { + gf_log (this->name, GF_LOG_ERROR, + "glfs instance for snap volume %s " + "failed", dirent->name); + goto out; + } + + ret = glfs_set_volfile_server (fs, "tcp", "localhost", + 24007); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "setting the " + "volfile srever for snap volume %s " + "failed", dirent->name); + goto out; + } + + ret = glfs_init (fs); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "initing the " + "fs for %s failed", dirent->name); + goto out; + } + + snprintf (logfile, sizeof (logfile), + DEFAULT_SVD_LOG_FILE_DIRECTORY "/%s-%s.log", name, dirent->uuid); + + ret = glfs_set_logging(fs, logfile, loglevel); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "failed to set the " + "log file path"); + goto out; + } + + ret = 0; + +out: + if (ret && fs) { + glfs_fini (fs); + fs = NULL; + } + + if (fs) + dirent->fs = fs; + + return fs; +} + +snap_dirent_t * +svs_get_latest_snap_entry (xlator_t *this) +{ + svs_private_t *priv = NULL; + snap_dirent_t *dirents = NULL; + snap_dirent_t *dirent = NULL; + snap_dirent_t *tmp_dirent = NULL; + + GF_VALIDATE_OR_GOTO ("svs", this, out); + + priv = this->private; + + dirents = priv->dirents; + + if (priv->num_snaps) { + tmp_dirent = &dirents[priv->num_snaps - 1]; + dirent = tmp_dirent; + } + +out: + return dirent; +} + +glfs_t * +svs_get_latest_snapshot (xlator_t *this) +{ + glfs_t *fs = NULL; + snap_dirent_t *dirent = NULL; + + GF_VALIDATE_OR_GOTO ("svs", this, out); + + dirent = svs_get_latest_snap_entry (this); + + if (dirent) + fs = dirent->fs; + +out: + return fs; +} + +int32_t +svs_lookup_entry_point (xlator_t *this, loc_t *loc, inode_t *parent, + struct iatt *buf, struct iatt *postparent, + int32_t *op_errno) +{ + uuid_t gfid; + svs_inode_t *inode_ctx = NULL; + int op_ret = -1; + + GF_VALIDATE_OR_GOTO ("snapview-server", this, out); + GF_VALIDATE_OR_GOTO (this->name, loc, out); + GF_VALIDATE_OR_GOTO (this->name, loc->inode, out); + GF_VALIDATE_OR_GOTO (this->name, buf, out); + GF_VALIDATE_OR_GOTO (this->name, postparent, out); + + if (uuid_is_null (loc->inode->gfid)) { + uuid_generate (gfid); + svs_iatt_fill (gfid, buf); + + /* Here the inode context of the entry point directory + is filled with just the type of the inode and the gfid + of the parent from where the entry point was entered. + The glfs object and the fs instance will be NULL. + */ + if (parent) + svs_iatt_fill (parent->gfid, postparent); + else { + svs_iatt_fill (buf->ia_gfid, postparent); + } + + inode_ctx = svs_inode_ctx_get_or_new (this, loc->inode); + if (!inode_ctx) { + gf_log (this->name, GF_LOG_ERROR, "failed to " + "allocate inode context for entry point " + "directory"); + op_ret = -1; + *op_errno = ENOMEM; + goto out; + } + uuid_copy (inode_ctx->pargfid, loc->pargfid); + memcpy (&inode_ctx->buf, buf, sizeof (*buf)); + inode_ctx->type = SNAP_VIEW_ENTRY_POINT_INODE; + } else { + if (inode_ctx) { + memcpy (buf, &inode_ctx->buf, sizeof (*buf)); + svs_iatt_fill (inode_ctx->pargfid, postparent); + } else { + svs_iatt_fill (loc->inode->gfid, buf); + if (parent) + svs_iatt_fill (parent->gfid, + postparent); + else { + svs_iatt_fill (loc->inode->gfid, + postparent); + } + } + } + op_ret = 0; + goto out; + +out: + return op_ret; +} + +/* When lookup comes from client and the protocol/server tries to resolve + the pargfid via just sending the gfid as part of lookup, if the inode + for the parent gfid is not found. But since that gfid has not yet been + looked up yet, inode will not be having inode context and parent is not + there (as it is the parent of the entry that is being resolved). So + without parent and inode context, svs cannot know which snapshot + to look into. In such cases, the amguity is handled by looking + into the latest snapshot. If the directory is there in the latest + snapshot, lookup is successful, otherwise it is a failure. So for + any directory created after taking the latest snapshot, entry into + snapshot world is denied. i.e you have to be part of snapshot world + to enter it. If the gfid is not found there, then unwind with + ESTALE + This gets executed mainly in the situation where the snapshot entry + point is entered from a non-root directory and that non-root directory's + inode (or gfid) is not yet looked up. And in each case when a gfid has to + be looked up (without any inode contex and parent context present), last + snapshot is referred and a random gfid is not generated. +*/ +int32_t +svs_lookup_gfid (xlator_t *this, loc_t *loc, struct iatt *buf, + struct iatt *postparent, int32_t *op_errno) +{ + int32_t op_ret = -1; + unsigned char handle_obj[GFAPI_HANDLE_LENGTH] = {0, }; + glfs_t *fs = NULL; + glfs_object_t *object = NULL; + struct stat statbuf = {0, }; + svs_inode_t *inode_ctx = NULL; + + GF_VALIDATE_OR_GOTO ("snapview-server", this, out); + GF_VALIDATE_OR_GOTO (this->name, loc, out); + GF_VALIDATE_OR_GOTO (this->name, loc->inode, out); + GF_VALIDATE_OR_GOTO (this->name, buf, out); + GF_VALIDATE_OR_GOTO (this->name, postparent, out); + + if (uuid_is_null (loc->gfid) && uuid_is_null (loc->inode->gfid)) { + gf_log (this->name, GF_LOG_ERROR, "gfid is NULL"); + goto out; + } + + if (!uuid_is_null (loc->inode->gfid)) + memcpy (handle_obj, loc->inode->gfid, + GFAPI_HANDLE_LENGTH); + else + memcpy (handle_obj, loc->gfid, + GFAPI_HANDLE_LENGTH); + + fs = svs_get_latest_snapshot (this); + if (!fs) { + gf_log (this->name, GF_LOG_ERROR, "failed to get the latest " + "snapshot"); + op_ret = -1; + *op_errno = EINVAL; + goto out; + } + + + object = glfs_h_create_from_handle (fs, handle_obj, GFAPI_HANDLE_LENGTH, + &statbuf); + if (!object) { + gf_log (this->name, GF_LOG_ERROR, "failed to do lookup and get " + "the handle on the snapshot %s (path: %s, gfid: %s)", + loc->name, loc->path, uuid_utoa (loc->gfid)); + op_ret = -1; + *op_errno = ESTALE; + goto out; + } + + inode_ctx = svs_inode_ctx_get_or_new (this, loc->inode); + if (!inode_ctx) { + gf_log (this->name, GF_LOG_ERROR, "failed to allocate inode " + "context"); + op_ret = -1; + *op_errno = ENOMEM; + goto out; + } + + iatt_from_stat (buf, &statbuf); + if (loc->gfid) + uuid_copy (buf->ia_gfid, loc->gfid); + else + uuid_copy (buf->ia_gfid, loc->inode->gfid); + + inode_ctx->type = SNAP_VIEW_VIRTUAL_INODE; + inode_ctx->fs = fs; + inode_ctx->object = object; + memcpy (&inode_ctx->buf, buf, sizeof (*buf)); + svs_iatt_fill (buf->ia_gfid, postparent); + + op_ret = 0; + +out: + return op_ret; +} + +/* If the parent is an entry point inode, then create the handle for the + snapshot on which lookup came. i.e in reality lookup came on + the directory from which the entry point directory was entered, but + lookup is into the past. So create the handle for it by doing + the name-less lookup on the gfid (which can be obtained from + parent's context +*/ +int32_t +svs_lookup_snapshot (xlator_t *this, loc_t *loc, struct iatt *buf, + struct iatt *postparent, inode_t *parent, + svs_inode_t *parent_ctx, int32_t *op_errno) +{ + int32_t op_ret = -1; + unsigned char handle_obj[GFAPI_HANDLE_LENGTH] = {0, }; + glfs_t *fs = NULL; + glfs_object_t *object = NULL; + struct stat statbuf = {0, }; + svs_inode_t *inode_ctx = NULL; + uuid_t gfid; + + GF_VALIDATE_OR_GOTO ("snapview-server", this, out); + GF_VALIDATE_OR_GOTO (this->name, loc, out); + GF_VALIDATE_OR_GOTO (this->name, loc->inode, out); + GF_VALIDATE_OR_GOTO (this->name, buf, out); + GF_VALIDATE_OR_GOTO (this->name, postparent, out); + GF_VALIDATE_OR_GOTO (this->name, parent_ctx, out); + GF_VALIDATE_OR_GOTO (this->name, parent, out); + + fs = svs_initialise_snapshot_volume (this, loc->name); + if (!fs) { + gf_log (this->name, GF_LOG_ERROR, "failed to " + "create the fs instance for snap %s", + loc->name); + op_ret = -1; + *op_errno = ESTALE; + goto out; + } + + memcpy (handle_obj, parent_ctx->pargfid, + GFAPI_HANDLE_LENGTH); + object = glfs_h_create_from_handle (fs, handle_obj, GFAPI_HANDLE_LENGTH, + &statbuf); + if (!object) { + gf_log (this->name, GF_LOG_ERROR, "failed to do lookup and " + "get the handle on the snapshot %s", loc->name); + op_ret = -1; + *op_errno = errno; + goto out; + } + + inode_ctx = svs_inode_ctx_get_or_new (this, loc->inode); + if (!inode_ctx) { + gf_log (this->name, GF_LOG_ERROR, "failed to " + "allocate inode context"); + op_ret = -1; + *op_errno = ENOMEM; + goto out; + } + + if (uuid_is_null (loc->gfid) && + uuid_is_null (loc->inode->gfid)) + uuid_generate (gfid); + else { + if (!uuid_is_null (loc->inode->gfid)) + uuid_copy (gfid, loc->inode->gfid); + else + uuid_copy (gfid, loc->gfid); + } + iatt_from_stat (buf, &statbuf); + uuid_copy (buf->ia_gfid, gfid); + svs_fill_ino_from_gfid (buf); + inode_ctx->type = SNAP_VIEW_VIRTUAL_INODE; + inode_ctx->fs = fs; + inode_ctx->object = object; + memcpy (&inode_ctx->buf, buf, sizeof (*buf)); + svs_iatt_fill (parent->gfid, postparent); + + op_ret = 0; + +out: + return op_ret; +} + +/* Both parent and entry are from snapshot world */ +int32_t +svs_lookup_entry (xlator_t *this, loc_t *loc, struct iatt *buf, + struct iatt *postparent, inode_t *parent, + svs_inode_t *parent_ctx, int32_t *op_errno) +{ + int32_t op_ret = -1; + glfs_t *fs = NULL; + glfs_object_t *object = NULL; + struct stat statbuf = {0, }; + svs_inode_t *inode_ctx = NULL; + glfs_object_t *parent_object = NULL; + uuid_t gfid; + + GF_VALIDATE_OR_GOTO ("snapview-server", this, out); + GF_VALIDATE_OR_GOTO (this->name, loc, out); + GF_VALIDATE_OR_GOTO (this->name, loc->inode, out); + GF_VALIDATE_OR_GOTO (this->name, buf, out); + GF_VALIDATE_OR_GOTO (this->name, postparent, out); + GF_VALIDATE_OR_GOTO (this->name, parent_ctx, out); + GF_VALIDATE_OR_GOTO (this->name, parent, out); + + parent_object = parent_ctx->object; + fs = parent_ctx->fs; + + object = glfs_h_lookupat (fs, parent_object, loc->name, + &statbuf); + if (!object) { + gf_log (this->name, GF_LOG_ERROR, "failed to do lookup and " + "get the handle for entry %s (path: %s)", loc->name, + loc->path); + op_ret = -1; + *op_errno = errno; + goto out; + } + + inode_ctx = svs_inode_ctx_get_or_new (this, loc->inode); + if (!inode_ctx) { + gf_log (this->name, GF_LOG_ERROR, "failed to " + "allocate inode context"); + op_ret = -1; + *op_errno = ENOMEM; + goto out; + } + + if (uuid_is_null (loc->gfid) && + uuid_is_null (loc->inode->gfid)) + uuid_generate (gfid); + else { + if (!uuid_is_null (loc->inode->gfid)) + uuid_copy (gfid, loc->inode->gfid); + else + uuid_copy (gfid, loc->gfid); + } + + iatt_from_stat (buf, &statbuf); + uuid_copy (buf->ia_gfid, gfid); + svs_fill_ino_from_gfid (buf); + inode_ctx->type = SNAP_VIEW_VIRTUAL_INODE; + inode_ctx->fs = fs; + inode_ctx->object = object; + memcpy (&inode_ctx->buf, buf, sizeof (*buf)); + svs_iatt_fill (parent->gfid, postparent); + + op_ret = 0; + +out: + return op_ret; +} + +/* inode context is there means lookup has come on an object which was + built either as part of lookup or as part of readdirp. But in readdirp + we would not have got the handle to access the object in the gfapi + world. + So if inode context contains glfs_t instance for the right + gfapi world and glfs_object_t handle for accessing it in the gfapi + world, then unwind with success as the snapshots as of now are + read-only. + If the above condition is not met, then send lookup call again to + the gfapi world. It can happen only if both parent context and + the name of the entry are present. + + If parent is an entry point to snapshot world: + * parent is needed for getting the gfid on which lookup has to be done + (the gfid present in the inode is a virtual gfid) in the snapshot + world. + * name is required to get the right glfs_t instance on which lookup + has to be done + + If parent is a directory from snapshot world: + * parent context is needed to get the glfs_t instance and to get the + handle to parent directory in the snapshot world. + * name is needed to do the lookup on the right entry in the snapshot + world +*/ +int32_t +svs_revalidate (xlator_t *this, loc_t *loc, inode_t *parent, + svs_inode_t *inode_ctx, svs_inode_t *parent_ctx, + struct iatt *buf, struct iatt *postparent, int32_t *op_errno) +{ + int32_t op_ret = -1; + glfs_t *fs = NULL; + glfs_object_t *object = NULL; + unsigned char handle_obj[GFAPI_HANDLE_LENGTH] = {0, }; + + GF_VALIDATE_OR_GOTO ("snapview-server", this, out); + GF_VALIDATE_OR_GOTO (this->name, buf, out); + GF_VALIDATE_OR_GOTO (this->name, postparent, out); + GF_VALIDATE_OR_GOTO (this->name, inode_ctx, out); + + if (inode_ctx->type == SNAP_VIEW_ENTRY_POINT_INODE) { + svs_iatt_fill (loc->inode->gfid, buf); + if (parent) + svs_iatt_fill (parent->gfid, + postparent); + else + svs_iatt_fill (loc->inode->gfid, postparent); + op_ret = 0; + goto out; + } else { + if (inode_ctx->fs && inode_ctx->object) { + memcpy (buf, &inode_ctx->buf, sizeof (*buf)); + if (parent) + svs_iatt_fill (parent->gfid, postparent); + else + svs_iatt_fill (buf->ia_gfid, postparent); + op_ret = 0; + goto out; + } + + /* To send the lookup to gfapi world, both the name of the + entry as well as the parent context is needed. + */ + if (!loc->name || !parent_ctx) { + *op_errno = ESTALE; + goto out; + } + + if (parent_ctx->type == SNAP_VIEW_ENTRY_POINT_INODE) + op_ret = svs_lookup_snapshot (this, loc, buf, + postparent, parent, + parent_ctx, op_errno); + else + op_ret = svs_lookup_entry (this, loc, buf, postparent, + parent, parent_ctx, + op_errno); + if (op_ret) + *op_errno = ESTALE; + + goto out; + } + +out: + return op_ret; +} + +int32_t +svs_lookup (call_frame_t *frame, xlator_t *this, loc_t *loc, dict_t *xdata) +{ + struct iatt buf = {0, }; + int32_t op_ret = -1; + int32_t op_errno = EINVAL; + struct iatt postparent = {0,}; + svs_inode_t *inode_ctx = NULL; + svs_inode_t *parent_ctx = NULL; + int32_t ret = -1; + svs_private_t *private = NULL; + inode_t *parent = NULL; + glfs_t *fs = NULL; + snap_dirent_t *dirent = NULL; + gf_boolean_t entry_point = _gf_false; + + GF_VALIDATE_OR_GOTO ("svs", 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); + + private = this->private; + + if (loc->name) { + ret = dict_get_str_boolean (xdata, "entry-point", _gf_false); + if (ret == -1) { + gf_log (this->name, GF_LOG_ERROR, "failed to get the " + "entry point info"); + entry_point = _gf_false; + } else { + entry_point = ret; + } + } + + if (loc->parent) + parent = inode_ref (loc->parent); + else { + parent = inode_find (loc->inode->table, loc->pargfid); + if (!parent) + parent = inode_parent (loc->inode, NULL, NULL); + } + if (parent) + parent_ctx = svs_inode_ctx_get (this, parent); + + inode_ctx = svs_inode_ctx_get (this, loc->inode); + + /* Initialize latest snapshot, which is used for nameless lookups */ + dirent = svs_get_latest_snap_entry (this); + if (!dirent->fs) + fs = svs_initialise_snapshot_volume (this, dirent->name); + + /* lookup is on the entry point to the snapshot world */ + if (entry_point) { + op_ret = svs_lookup_entry_point (this, loc, parent, &buf, + &postparent, &op_errno); + goto out; + } + + /* revalidate */ + if (inode_ctx) { + op_ret = svs_revalidate (this, loc, parent, inode_ctx, + parent_ctx, &buf, &postparent, + &op_errno); + goto out; + } + + /* This can happen when entry point directory is entered from non-root + directory. (ex: if /mnt/glusterfs is the mount point, then entry + point (say .snaps) is entered from /mnt/glusterfs/dir/.snaps). Also + it can happen when client sends a nameless lookup on just a gfid and + the server does not have the inode in the inode table. + */ + if (!inode_ctx && !parent_ctx) { + op_ret = svs_lookup_gfid (this, loc, &buf, &postparent, + &op_errno); + goto out; + } + + if (parent_ctx) { + if (parent_ctx->type == SNAP_VIEW_ENTRY_POINT_INODE) + op_ret = svs_lookup_snapshot (this, loc, &buf, + &postparent, parent, + parent_ctx, &op_errno); + else + op_ret = svs_lookup_entry (this, loc, &buf, + &postparent, parent, + parent_ctx, &op_errno); + goto out; + } + +out: + STACK_UNWIND_STRICT (lookup, frame, op_ret, op_errno, + loc?loc->inode:NULL, &buf, xdata, &postparent); + + if (parent) + inode_unref (parent); + + return 0; +} + +int32_t +svs_opendir (call_frame_t *frame, xlator_t *this, loc_t *loc, fd_t *fd, + dict_t *xdata) +{ + int32_t ret = -1; + svs_inode_t *inode_ctx = NULL; + int32_t op_ret = -1; + int32_t op_errno = EINVAL; + svs_fd_t *svs_fd = NULL; + glfs_fd_t *glfd = NULL; + glfs_t *fs = NULL; + glfs_object_t *object = NULL; + + GF_VALIDATE_OR_GOTO ("snap-view-daemon", this, out); + GF_VALIDATE_OR_GOTO (this->name, frame, out); + GF_VALIDATE_OR_GOTO (this->name, fd, out); + GF_VALIDATE_OR_GOTO (this->name, loc, out); + GF_VALIDATE_OR_GOTO (this->name, loc->inode, out); + + inode_ctx = svs_inode_ctx_get (this, loc->inode); + if (!inode_ctx) { + gf_log (this->name, GF_LOG_ERROR, "inode context not found " + "for the inode %s", uuid_utoa (loc->inode->gfid)); + op_ret = -1; + op_errno = ESTALE; + goto out; + } + + /* Fake success is sent if the opendir is on the entry point directory + or the inode is SNAP_VIEW_ENTRY_POINT_INODE + */ + if (inode_ctx->type == SNAP_VIEW_ENTRY_POINT_INODE) { + op_ret = 0; + op_errno = 0; + goto out; + } else if (inode_ctx->type == SNAP_VIEW_VIRTUAL_INODE) { + fs = inode_ctx->fs; + object = inode_ctx->object; + glfd = glfs_h_opendir (fs, object); + if (!glfd) { + op_ret = -1; + op_errno = errno; + gf_log (this->name, GF_LOG_ERROR, "opendir on %s " + "failed (gfid: %s)", loc->name, + uuid_utoa (loc->inode->gfid)); + goto out; + } + svs_fd = svs_fd_ctx_get_or_new (this, fd); + if (!svs_fd) { + gf_log (this->name, GF_LOG_ERROR, "failed to allocate " + "fd context %s (gfid: %s)", loc->name, + uuid_utoa (fd->inode->gfid)); + op_ret = -1; + op_errno = ENOMEM; + glfs_closedir (glfd); + goto out; + } + svs_fd->fd = glfd; + + op_ret = 0; + op_errno = 0; + } + +out: + STACK_UNWIND_STRICT (opendir, frame, op_ret, op_errno, fd, NULL); + + return 0; +} + +int32_t +svs_getxattr (call_frame_t *frame, xlator_t *this, loc_t *loc, const char *name, + dict_t *xdata) +{ + svs_inode_t *inode_ctx = NULL; + int32_t op_ret = -1; + int32_t op_errno = EINVAL; + glfs_t *fs = NULL; + glfs_object_t *object = NULL; + char *value = 0; + ssize_t size = 0; + dict_t *dict = NULL; + int ret = -1; + + GF_VALIDATE_OR_GOTO ("snap-view-daemon", this, out); + GF_VALIDATE_OR_GOTO ("snap-view-daemon", frame, out); + GF_VALIDATE_OR_GOTO ("snap-view-daemon", loc, out); + GF_VALIDATE_OR_GOTO ("snap-view-daemon", loc->inode, out); + + inode_ctx = svs_inode_ctx_get (this, loc->inode); + if (!inode_ctx) { + gf_log (this->name, GF_LOG_ERROR, "inode context not found " + "for the inode %s", uuid_utoa (loc->inode->gfid)); + op_ret = -1; + op_errno = ESTALE; + goto out; + } + + /* Fake success is sent if the getxattr is on entry point directory + or the inode is SNAP_VIEW_ENTRY_POINT_INODE + */ + if (inode_ctx->type == SNAP_VIEW_ENTRY_POINT_INODE) { + op_ret = -1; + op_errno = EINVAL; + goto out; + } else if (inode_ctx->type == SNAP_VIEW_VIRTUAL_INODE) { + fs = inode_ctx->fs; + object = inode_ctx->object; + size = glfs_h_getxattrs (fs, object, name, NULL, 0); + if (!size == -1) { + gf_log (this->name, GF_LOG_ERROR, "getxattr on %s " + "failed (key: %s)", loc->name, name); + op_ret = -1; + op_errno = errno; + goto out; + } + value = GF_CALLOC (size + 1, sizeof (char), gf_common_mt_char); + if (!value) { + gf_log (this->name, GF_LOG_ERROR, "failed to allocate " + "memory for getxattr on %s (key: %s)", + loc->name, name); + op_ret = -1; + op_errno = ENOMEM; + goto out; + } + + size = glfs_h_getxattrs (fs, object, name, value, size); + if (size == -1) { + gf_log (this->name, GF_LOG_ERROR, "failed to get the " + "xattr %s for entry %s", name, loc->name); + op_ret = -1; + op_errno = errno; + goto out; + } + value[size] = '\0'; + + dict = dict_new (); + if (!dict) { + gf_log (this->name, GF_LOG_ERROR, "failed to allocate " + "dict"); + op_ret = -1; + op_errno = ENOMEM; + goto out; + } + + op_ret = dict_set_dynptr (dict, (char *)name, value, size); + if (op_ret < 0) { + op_errno = -op_ret; + gf_log (this->name, GF_LOG_ERROR, "dict set operation " + "for %s for the key %s failed.", loc->path, + name); + GF_FREE (value); + goto out; + } + + op_ret = 0; + op_errno = 0; + } + +out: + if (op_ret) + GF_FREE (value); + + STACK_UNWIND_STRICT (getxattr, frame, op_ret, op_errno, dict, NULL); + + return 0; +} + +int32_t +svs_fgetxattr (call_frame_t *frame, xlator_t *this, fd_t *fd, const char *name, + dict_t *xdata) +{ + svs_inode_t *inode_ctx = NULL; + int32_t op_ret = -1; + int32_t op_errno = EINVAL; + char *value = 0; + ssize_t size = 0; + dict_t *dict = NULL; + svs_fd_t *sfd = NULL; + glfs_fd_t *glfd = NULL; + + GF_VALIDATE_OR_GOTO ("snap-view-daemon", this, out); + GF_VALIDATE_OR_GOTO ("snap-view-daemon", frame, out); + GF_VALIDATE_OR_GOTO ("snap-view-daemon", fd, out); + GF_VALIDATE_OR_GOTO ("snap-view-daemon", fd->inode, out); + + inode_ctx = svs_inode_ctx_get (this, fd->inode); + if (!inode_ctx) { + gf_log (this->name, GF_LOG_ERROR, "inode context not found " + "for the inode %s", uuid_utoa (fd->inode->gfid)); + op_ret = -1; + op_errno = ESTALE; + goto out; + } + + sfd = svs_fd_ctx_get_or_new (this, fd); + if (!sfd) { + gf_log (this->name, GF_LOG_ERROR, "failed to get the fd " + "context for %s", uuid_utoa (fd->inode->gfid)); + op_ret = -1; + op_errno = EBADFD; + goto out; + } + + glfd = sfd->fd; + /* Fake success is sent if the getxattr is on entry point directory + or the inode is SNAP_VIEW_ENTRY_POINT_INODE + */ + if (inode_ctx->type == SNAP_VIEW_ENTRY_POINT_INODE) { + op_ret = -1; + op_errno = EINVAL; + goto out; + } + + if (inode_ctx->type == SNAP_VIEW_VIRTUAL_INODE) { + size = glfs_fgetxattr (glfd, name, NULL, 0); + if (!size == -1) { + gf_log (this->name, GF_LOG_ERROR, "getxattr on %s " + "failed (key: %s)", uuid_utoa (fd->inode->gfid), + name); + op_ret = -1; + op_errno = errno; + goto out; + } + value = GF_CALLOC (size + 1, sizeof (char), gf_common_mt_char); + if (!value) { + gf_log (this->name, GF_LOG_ERROR, "failed to allocate " + "memory for getxattr on %s (key: %s)", + uuid_utoa (fd->inode->gfid), name); + op_ret = -1; + op_errno = ENOMEM; + goto out; + } + + size = glfs_fgetxattr (glfd, name, value, size); + if (size == -1) { + gf_log (this->name, GF_LOG_ERROR, "failed to get the " + "xattr %s for inode %s", name, + uuid_utoa (fd->inode->gfid)); + op_ret = -1; + op_errno = errno; + goto out; + } + value[size] = '\0'; + + dict = dict_new (); + if (!dict) { + gf_log (this->name, GF_LOG_ERROR, "failed to allocate " + "dict"); + op_ret = -1; + op_errno = ENOMEM; + goto out; + } + + op_ret = dict_set_dynptr (dict, (char *)name, value, size); + if (op_ret < 0) { + op_errno = -op_ret; + gf_log (this->name, GF_LOG_ERROR, "dict set operation " + "for gfid %s for the key %s failed.", + uuid_utoa (fd->inode->gfid), name); + GF_FREE (value); + goto out; + } + + op_ret = 0; + op_errno = 0; + } + +out: + if (op_ret) + GF_FREE (value); + + STACK_UNWIND_STRICT (fgetxattr, frame, op_ret, op_errno, dict, NULL); + + return 0; +} + +int32_t +svs_releasedir (xlator_t *this, fd_t *fd) +{ + svs_fd_t *sfd = NULL; + uint64_t tmp_pfd = 0; + int ret = 0; + + GF_VALIDATE_OR_GOTO ("snapview-server", this, out); + GF_VALIDATE_OR_GOTO (this->name, fd, out); + + ret = fd_ctx_del (fd, this, &tmp_pfd); + if (ret < 0) { + gf_log (this->name, GF_LOG_DEBUG, + "pfd from fd=%p is NULL", fd); + goto out; + } + + sfd = (svs_fd_t *)(long)tmp_pfd; + if (sfd->fd) { + ret = glfs_closedir (sfd->fd); + if (ret) + gf_log (this->name, GF_LOG_WARNING, "failed to close " + "the glfd for directory %s", + uuid_utoa (fd->inode->gfid)); + } + +out: + return 0; +} + +int32_t +svs_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; + uint64_t value = 0; + + GF_VALIDATE_OR_GOTO ("snapview-server", this, out); + GF_VALIDATE_OR_GOTO (this->name, frame, out); + GF_VALIDATE_OR_GOTO (this->name, fd, out); + + ret = fd_ctx_get (fd, this, &value); + if (ret < 0) { + op_errno = EINVAL; + gf_log (this->name, GF_LOG_WARNING, + "pfd is NULL on fd=%p", fd); + goto out; + } + + op_ret = 0; + +out: + STACK_UNWIND_STRICT (flush, frame, op_ret, op_errno, NULL); + + return 0; +} + +int32_t +svs_release (xlator_t *this, fd_t *fd) +{ + svs_fd_t *sfd = NULL; + uint64_t tmp_pfd = 0; + int ret = 0; + + GF_VALIDATE_OR_GOTO ("snapview-server", this, out); + GF_VALIDATE_OR_GOTO (this->name, fd, out); + + ret = fd_ctx_del (fd, this, &tmp_pfd); + if (ret < 0) { + gf_log (this->name, GF_LOG_DEBUG, + "pfd from fd=%p is NULL", fd); + goto out; + } + + sfd = (svs_fd_t *)(long)tmp_pfd; + if (sfd->fd) { + ret = glfs_close (sfd->fd); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "failed to close " + "the glfd for %s", + uuid_utoa (fd->inode->gfid)); + } + } + +out: + return 0; +} + +int32_t +svs_forget (xlator_t *this, inode_t *inode) +{ + int ret = -1; + uint64_t value = 0; + svs_inode_t *inode_ctx = NULL; + + GF_VALIDATE_OR_GOTO ("snapview-server", 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 delte the inode " + "context of %s", uuid_utoa (inode->gfid)); + goto out; + } + + inode_ctx = (svs_inode_t *)value; + + if (inode_ctx->object) + glfs_h_close (inode_ctx->object); + + GF_FREE (inode_ctx); + +out: + return 0; +} + +/* As of now, the list of snapshots is obtained by reading a predefined file + (which the user has to generate using these commands) +* /usr/local/sbin/gluster snapshot info | grep -i snap | grep -i Volume | + grep -i Name | cut -d':' -f 2 > /tmp/tmp-snap-uuids + /usr/local/sbin/gluster snapshot info | grep Snapshot | cut -d':' -f 2 + > /tmp/tmp-snap_names + This is a temporary workaround which will be changed to a notification + based mechanism where snapd gets the list of snapshots from glusterd +*/ +int +svs_get_snapshot_list (xlator_t *this, svs_private_t *priv) +{ + int ret = -1; + char str_uuid[256] = {'\0'}; + char str_name[256] = {'\0'}; + int snap_count = 0; + snap_dirent_t *dirents = NULL; + FILE *fpn = NULL; + FILE *fpu = NULL; + int i = 0; + + GF_VALIDATE_OR_GOTO ("snapview-server", this, out); + GF_VALIDATE_OR_GOTO (this->name, priv, out); + + dirents = GF_CALLOC (sizeof (*dirents), SNAP_VIEW_MAX_NUM_SNAPS, + gf_svs_mt_dirents_t); + if (!dirents) { + gf_log (this->name, GF_LOG_ERROR, "failed to allocate memory"); + goto out; + /* error, bail */ + } + priv->dirents = dirents; + + fpu = fopen ("/tmp/tmp-snap-uuids", "r+"); + fpn = fopen ("/tmp/tmp-snap_names", "r+"); + + if (!fpu || !fpn) { + gf_log (this->name, GF_LOG_ERROR, "failed to open the file"); + goto out; + } + + while ((fscanf (fpu, "%s", str_uuid) != -1) && + (fscanf (fpn, "%s", str_name) != -1)) { + strncpy (dirents[i].uuid, str_uuid, strlen (str_uuid) + 1); + strncpy (dirents[i].name, str_name, strlen (str_name) + 1); + ++snap_count; + ++i; + } + priv->num_snaps = snap_count; + + fclose (fpn); + fclose (fpu); + + ret = 0; + +out: + return ret; +} + +int +svs_fill_readdir (xlator_t *this, gf_dirent_t *entries, size_t size, off_t off) +{ + gf_dirent_t *entry = NULL; + svs_private_t *priv = NULL; + int i = 0; + snap_dirent_t *dirents = NULL; + int this_size = 0; + int filled_size = 0; + int count = 0; + + GF_VALIDATE_OR_GOTO ("snap-view-daemon", this, out); + GF_VALIDATE_OR_GOTO ("snap-view-daemon", entries, out); + + priv = this->private; + /* create the dir entries */ + dirents = priv->dirents; + + for (i = off; i < priv->num_snaps; ) { + this_size = sizeof (gf_dirent_t) + + strlen (dirents[i].name) + 1; + if (this_size + filled_size > size ) + goto out; + + entry = gf_dirent_for_name (dirents[i].name); + if (!entry) { + gf_log (this->name, GF_LOG_ERROR, "failed to allocate " + "dentry for %s", dirents[i].name); + goto out; + } + + entry->d_off = i + 1; + entry->d_ino = i + 2*42; + entry->d_type = DT_DIR; + list_add_tail (&entry->list, &entries->list); + ++i; + count++; + filled_size += this_size; + } + +out: + return count; +} + +int32_t +svs_glfs_readdir (xlator_t *this, glfs_fd_t *glfd, gf_dirent_t *entries, + int32_t *op_errno, struct iatt *buf, gf_boolean_t readdirplus, + size_t size) +{ + int filled_size = 0; + int this_size = 0; + int32_t ret = -1; + int32_t count = 0; + gf_dirent_t *entry = NULL; + struct dirent *dirents = NULL; + struct dirent de = {0, }; + struct stat statbuf = {0, }; + off_t in_case = -1; + + GF_VALIDATE_OR_GOTO ("svs", this, out); + GF_VALIDATE_OR_GOTO (this->name, glfd, out); + GF_VALIDATE_OR_GOTO (this->name, entries, out); + GF_VALIDATE_OR_GOTO (this->name, buf, out); + + while (filled_size < size) { + in_case = glfs_telldir (glfd); + if (in_case == -1) { + gf_log (this->name, GF_LOG_ERROR, "telldir failed"); + break; + } + + if (readdirplus) + ret = glfs_readdirplus_r (glfd, &statbuf, &de, + &dirents); + else + ret = glfs_readdir_r (glfd, &de, &dirents); + + if (ret == 0 && dirents != NULL) { + if (readdirplus) + this_size = max (sizeof (gf_dirent_t), + sizeof (gfs3_dirplist)) + + strlen (de.d_name) + 1; + else + this_size = sizeof (gf_dirent_t) + + strlen (de.d_name) + 1; + + if (this_size + filled_size > size) { + glfs_seekdir (glfd, in_case); + break; + } + + entry = gf_dirent_for_name (de.d_name); + if (!entry) { + gf_log (this->name, GF_LOG_ERROR, + "could not create gf_dirent " + "for entry %s: (%s)", + entry->d_name, + strerror (errno)); + break; + } + entry->d_off = de.d_off; + entry->d_ino = de.d_ino; + entry->d_type = de.d_type; + iatt_from_stat (buf, &statbuf); + if (readdirplus) + entry->d_stat = *buf; + list_add_tail (&entry->list, &entries->list); + + filled_size += this_size; + count++; + } else if (ret == 0 && dirents == NULL) { + *op_errno = ENOENT; + break; + } else if (ret != 0) { + *op_errno = errno; + break; + } + dirents = NULL; + ret = -1; + } + +out: + return count; +} + +/* readdirp can be of 2 types. + 1) It can come on entry point directory where the list of snapshots + is sent as dirents. In this case, the iatt structure is filled + on the fly if the inode is not found for the entry or the inode + context is NULL. Other wise if inode is found and inode context + is there the iatt structure saved in the context is used. + 2) It can be on a directory in one of the snapshots. In this case, + the readdirp call would have sent us a iatt structure. So the same + structure is used with the exception that the gfid and the inode + numbers will be newly generated and filled in. +*/ +void +svs_readdirp_fill (xlator_t *this, inode_t *parent, svs_inode_t *parent_ctx, + gf_dirent_t *entry) +{ + inode_t *inode = NULL; + uuid_t random_gfid = {0,}; + struct iatt buf = {0, }; + svs_inode_t *inode_ctx = NULL; + + GF_VALIDATE_OR_GOTO ("snapview-server", this, out); + GF_VALIDATE_OR_GOTO (this->name, parent, out); + GF_VALIDATE_OR_GOTO (this->name, parent_ctx, out); + GF_VALIDATE_OR_GOTO (this->name, entry, out); + + inode = inode_grep (parent->table, parent, entry->d_name); + if (inode) { + entry->inode = inode; + inode_ctx = svs_inode_ctx_get (this, inode); + if (!inode_ctx) { + uuid_copy (buf.ia_gfid, inode->gfid); + svs_iatt_fill (inode->gfid, &buf); + buf.ia_type = inode->ia_type; + } else { + buf = inode_ctx->buf; + } + + entry->d_ino = buf.ia_ino; + + if (parent_ctx->type == SNAP_VIEW_ENTRY_POINT_INODE) + entry->d_stat = buf; + else { + entry->d_stat.ia_ino = buf.ia_ino; + uuid_copy (entry->d_stat.ia_gfid, buf.ia_gfid); + } + } else { + inode = inode_new (parent->table); + entry->inode = inode; + uuid_generate (random_gfid); + uuid_copy (buf.ia_gfid, random_gfid); + svs_fill_ino_from_gfid (&buf); + entry->d_ino = buf.ia_ino; + + /* If inode context allocation fails, then do not send the + inode for that particular entry as part of readdirp + response. Fuse and protocol/server will link the inodes + in readdirp only if the entry contains inode in it. + */ + inode_ctx = svs_inode_ctx_get_or_new (this, inode); + if (!inode_ctx) { + gf_log (this->name, GF_LOG_ERROR, "failed to allocate " + "inode context for %s", entry->d_name); + inode_unref (entry->inode); + entry->inode = NULL; + goto out; + } + + inode_ctx->type = SNAP_VIEW_VIRTUAL_INODE; + + if (parent_ctx->type == SNAP_VIEW_ENTRY_POINT_INODE) { + buf.ia_type = IA_IFDIR; + inode_ctx->buf = buf; + entry->d_stat = buf; + } else { + uuid_copy (entry->d_stat.ia_gfid, buf.ia_gfid); + entry->d_stat.ia_ino = buf.ia_ino; + inode_ctx->buf = entry->d_stat; + } + } + +out: + return; +} + +/* In readdirp, though new inode is created along with the generation of + new gfid, the inode context created will not contain the glfs_t instance + for the filesystem it belongs to and the handle for it in the gfapi + world. (handle is obtained only by doing the lookup call on the entry + and doing lookup on each entry received as part of readdir call is a + costly operation. So the fs and handle is NULL in the inode context + and is filled in when lookup comes on that object. +*/ +int32_t +svs_readdirp (call_frame_t *frame, xlator_t *this, fd_t *fd, size_t size, + off_t off, dict_t *dict) +{ + gf_dirent_t entries; + gf_dirent_t *entry = NULL; + struct iatt buf = {0, }; + int count = 0; + int op_ret = -1; + int op_errno = EINVAL; + svs_inode_t *inode_ctx = NULL; + svs_inode_t *parent_ctx = NULL; + svs_fd_t *svs_fd = NULL; + + GF_VALIDATE_OR_GOTO ("snap-view-daemon", this, unwind); + GF_VALIDATE_OR_GOTO (this->name, frame, unwind); + GF_VALIDATE_OR_GOTO (this->name, fd, unwind); + GF_VALIDATE_OR_GOTO (this->name, fd->inode, unwind); + + INIT_LIST_HEAD (&entries.list); + + parent_ctx = svs_inode_ctx_get (this, fd->inode); + if (!parent_ctx) { + 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 unwind; + } + + if (parent_ctx->type == SNAP_VIEW_ENTRY_POINT_INODE) { + LOCK (&fd->lock); + { + count = svs_fill_readdir (this, &entries, size, off); + } + UNLOCK (&fd->lock); + + op_ret = count; + + list_for_each_entry (entry, &entries.list, list) { + svs_readdirp_fill (this, fd->inode, parent_ctx, entry); + } + + goto unwind; + } else { + svs_fd = svs_fd_ctx_get_or_new (this, fd); + if (!svs_fd) { + gf_log (this->name, GF_LOG_ERROR, "failed to get the " + "fd context %s", uuid_utoa (fd->inode->gfid)); + op_ret = -1; + op_errno = EBADFD; + goto unwind; + } + + glfs_seekdir (svs_fd->fd, off); + + LOCK (&fd->lock); + { + count = svs_glfs_readdir (this, svs_fd->fd, &entries, + &op_errno, &buf, _gf_true, + size); + } + UNLOCK (&fd->lock); + + op_ret = count; + + list_for_each_entry (entry, &entries.list, list) { + svs_readdirp_fill (this, fd->inode, parent_ctx, entry); + } + + goto unwind; + } + +unwind: + STACK_UNWIND_STRICT (readdirp, frame, op_ret, op_errno, &entries, dict); + + gf_dirent_free (&entries); + + return 0; +} + +int32_t +svs_readdir (call_frame_t *frame, xlator_t *this, fd_t *fd, size_t size, + off_t off, dict_t *xdata) +{ + svs_private_t *priv = NULL; + gf_dirent_t entries; + int count = 0; + svs_inode_t *inode_ctx = NULL; + int op_errno = EINVAL; + int op_ret = -1; + svs_fd_t *svs_fd = NULL; + glfs_fd_t *glfd = NULL; + + GF_VALIDATE_OR_GOTO ("snap-view-server", this, unwind); + GF_VALIDATE_OR_GOTO (this->name, frame, unwind); + GF_VALIDATE_OR_GOTO (this->name, fd, unwind); + GF_VALIDATE_OR_GOTO (this->name, fd->inode, unwind); + + priv = this->private; + + inode_ctx = svs_inode_ctx_get (this, fd->inode); + if (!inode_ctx) { + gf_log (this->name, GF_LOG_ERROR, "inode context not found in " + "the inode %s", uuid_utoa (fd->inode->gfid)); + op_ret = -1; + op_errno = EINVAL; + goto unwind; + } + + if (inode_ctx->type == SNAP_VIEW_ENTRY_POINT_INODE) { + LOCK (&fd->lock); + { + count = svs_fill_readdir (this, &entries, size, off); + } + UNLOCK (&fd->lock); + } else { + svs_fd = svs_fd_ctx_get_or_new (this, fd); + if (!svs_fd) { + gf_log (this->name, GF_LOG_ERROR, "failed to get the " + "fd context %s", uuid_utoa (fd->inode->gfid)); + op_ret = -1; + op_errno = EBADFD; + goto unwind; + } + + glfd = svs_fd->fd; + + LOCK (&fd->lock); + { + count = svs_glfs_readdir (this, glfd, &entries, + &op_errno, NULL, _gf_false, + size); + } + UNLOCK (&fd->lock); + } + + op_ret = count; + +unwind: + STACK_UNWIND_STRICT (readdir, frame, op_ret, op_errno, &entries, xdata); + + gf_dirent_free (&entries); + + return 0; +} + +int32_t +svs_stat (call_frame_t *frame, xlator_t *this, loc_t *loc, dict_t *xdata) +{ + svs_private_t *priv = NULL; + struct iatt buf = {0, }; + int32_t op_errno = EINVAL; + int32_t op_ret = -1; + svs_inode_t *inode_ctx = NULL; + glfs_t *fs = NULL; + glfs_object_t *object = NULL; + struct stat stat = {0, }; + int ret = -1; + + GF_VALIDATE_OR_GOTO ("snap-view-daemon", 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); + + priv = this->private; + + /* Instead of doing the check of whether it is a entry point directory + or not by checking the name of the entry and then deciding what + to do, just check the inode context and decide what to be done. + */ + + inode_ctx = svs_inode_ctx_get (this, loc->inode); + if (!inode_ctx) { + gf_log (this->name, GF_LOG_ERROR, "inode context not found for" + " %s", uuid_utoa (loc->inode->gfid)); + op_ret = -1; + op_errno = EINVAL; + goto out; + } + + if (inode_ctx->type == SNAP_VIEW_ENTRY_POINT_INODE) { + svs_iatt_fill (loc->inode->gfid, &buf); + op_ret = 0; + } else if (inode_ctx->type == SNAP_VIEW_VIRTUAL_INODE) { + fs = inode_ctx->fs; + object = inode_ctx->object; + ret = glfs_h_stat (fs, object, &stat); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "glfs_h_stat on %s " + "(gfid: %s) failed", loc->name, + uuid_utoa (loc->inode->gfid)); + op_ret = -1; + op_errno = errno; + goto out; + } + + iatt_from_stat (&buf, &stat); + uuid_copy (buf.ia_gfid, loc->inode->gfid); + svs_fill_ino_from_gfid (&buf); + op_ret = ret; + } + +out: + STACK_UNWIND_STRICT (stat, frame, op_ret, op_errno, &buf, xdata); + return 0; +} + +int32_t +svs_fstat (call_frame_t *frame, xlator_t *this, fd_t *fd, dict_t *xdata) +{ + svs_private_t *priv = NULL; + struct iatt buf = {0, }; + int32_t op_errno = EINVAL; + int32_t op_ret = -1; + svs_inode_t *inode_ctx = NULL; + struct stat stat = {0, }; + int ret = -1; + glfs_fd_t *glfd = NULL; + svs_fd_t *sfd = NULL; + + GF_VALIDATE_OR_GOTO ("snap-view-daemon", 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); + + priv = this->private; + + /* Instead of doing the check of whether it is a entry point directory + or not by checking the name of the entry and then deciding what + to do, just check the inode context and decide what to be done. + */ + + inode_ctx = svs_inode_ctx_get (this, fd->inode); + if (!inode_ctx) { + gf_log (this->name, GF_LOG_ERROR, "inode context not found for" + " the inode %s", uuid_utoa (fd->inode->gfid)); + op_ret = -1; + op_errno = EINVAL; + goto out; + } + + if (inode_ctx->type == SNAP_VIEW_ENTRY_POINT_INODE) { + svs_iatt_fill (fd->inode->gfid, &buf); + op_ret = 0; + } else if (inode_ctx->type == SNAP_VIEW_VIRTUAL_INODE) { + sfd = svs_fd_ctx_get_or_new (this, fd); + if (!sfd) { + gf_log (this->name, GF_LOG_ERROR, "failed to get the " + "fd context for %s", + uuid_utoa (fd->inode->gfid)); + op_ret = -1; + op_errno = EBADFD; + goto out; + } + + glfd = sfd->fd; + ret = glfs_fstat (glfd, &stat); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "glfs_fstat on " + "gfid: %s failed", uuid_utoa (fd->inode->gfid)); + op_ret = -1; + op_errno = errno; + goto out; + } + + iatt_from_stat (&buf, &stat); + uuid_copy (buf.ia_gfid, fd->inode->gfid); + svs_fill_ino_from_gfid (&buf); + op_ret = ret; + } + +out: + STACK_UNWIND_STRICT (fstat, frame, op_ret, op_errno, &buf, xdata); + return 0; +} + +int32_t +svs_open (call_frame_t *frame, xlator_t *this, loc_t *loc, int32_t flags, + fd_t *fd, dict_t *xdata) +{ + int32_t ret = -1; + svs_inode_t *inode_ctx = NULL; + svs_fd_t *sfd = NULL; + int32_t op_ret = -1; + int32_t op_errno = EINVAL; + glfs_fd_t *glfd = NULL; + glfs_t *fs = NULL; + glfs_object_t *object = NULL; + + + GF_VALIDATE_OR_GOTO ("snap-view-daemon", this, out); + GF_VALIDATE_OR_GOTO (this->name, frame, out); + GF_VALIDATE_OR_GOTO (this->name, fd, out); + GF_VALIDATE_OR_GOTO (this->name, loc, out); + GF_VALIDATE_OR_GOTO (this->name, loc->inode, out); + + inode_ctx = svs_inode_ctx_get (this, loc->inode); + if (!inode_ctx) { + gf_log (this->name, GF_LOG_ERROR, "inode context for %s " + "(gfid: %s) not found", loc->name, + uuid_utoa (loc->inode->gfid)); + goto out; + } + + if (inode_ctx->type == SNAP_VIEW_ENTRY_POINT_INODE) + GF_ASSERT (0); // on entry point it should always be opendir + + fs = inode_ctx->fs; + object = inode_ctx->object; + + glfd = glfs_h_open (fs, object, flags); + if (!glfd) { + gf_log (this->name, GF_LOG_ERROR, "glfs_h_open on %s failed " + "(gfid: %s)", loc->name, uuid_utoa (loc->inode->gfid)); + op_ret = -1; + op_errno = errno; + goto out; + } + + sfd = svs_fd_ctx_get_or_new (this, fd); + if (!sfd) { + gf_log (this->name, GF_LOG_ERROR, "failed to allocate fd " + "context for %s (gfid: %s)", loc->name, + uuid_utoa (loc->inode->gfid)); + op_ret = -1; + op_errno = ENOMEM; + glfs_close (glfd); + goto out; + } + sfd->fd = glfd; + + op_ret = 0; + +out: + STACK_UNWIND_STRICT (open, frame, op_ret, op_errno, fd, NULL); + return 0; +} + +int32_t +svs_readv (call_frame_t *frame, xlator_t *this, + fd_t *fd, size_t size, off_t offset, uint32_t flags, dict_t *xdata) +{ + int32_t op_ret = -1; + int32_t op_errno = 0; + svs_private_t *priv = NULL; + struct iobuf *iobuf = NULL; + struct iobref *iobref = NULL; + struct iovec vec = {0,}; + svs_fd_t *sfd = NULL; + int ret = -1; + struct stat fstatbuf = {0, }; + glfs_fd_t *glfd = NULL; + struct iatt stbuf = {0, }; + + GF_VALIDATE_OR_GOTO ("snap-view-daemon", 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); + + priv = this->private; + VALIDATE_OR_GOTO (priv, out); + + sfd = svs_fd_ctx_get_or_new (this, fd); + if (!sfd) { + gf_log (this->name, GF_LOG_ERROR, "failed to get the fd " + "context for %s", uuid_utoa (fd->inode->gfid)); + op_ret = -1; + op_errno = EBADFD; + goto out; + } + + glfd = sfd->fd; + + iobuf = iobuf_get2 (this->ctx->iobuf_pool, size); + if (!iobuf) { + op_ret = -1; + op_errno = ENOMEM; + goto out; + } + + ret = glfs_pread (glfd, iobuf->ptr, size, offset, 0); + if (ret < 0) { + op_ret = -1; + op_errno = errno; + gf_log (this->name, GF_LOG_ERROR, "glfs_read failed (%s)", + strerror (op_errno)); + goto out; + } + + vec.iov_base = iobuf->ptr; + vec.iov_len = ret; + + iobref = iobref_new (); + + iobref_add (iobref, iobuf); + + ret = glfs_fstat (glfd, &fstatbuf); + if (ret) { + op_ret = -1; + op_errno = errno; + gf_log (this->name, GF_LOG_ERROR, "glfs_fstat failed after " + "readv on %s", uuid_utoa (fd->inode->gfid)); + goto out; + } + + iatt_from_stat (&stbuf, &fstatbuf); + uuid_copy (stbuf.ia_gfid, fd->inode->gfid); + svs_fill_ino_from_gfid (&stbuf); + + /* Hack to notify higher layers of EOF. */ + if (!stbuf.ia_size || (offset + vec.iov_len) >= stbuf.ia_size) + op_errno = ENOENT; + + op_ret = vec.iov_len; + +out: + + STACK_UNWIND_STRICT (readv, frame, op_ret, op_errno, + &vec, 1, &stbuf, iobref, NULL); + + if (iobref) + iobref_unref (iobref); + if (iobuf) + iobuf_unref (iobuf); + + return 0; +} + +int32_t +svs_readlink (call_frame_t *frame, xlator_t *this, + loc_t *loc, size_t size, dict_t *xdata) +{ + svs_inode_t *inode_ctx = NULL; + glfs_t *fs = NULL; + glfs_object_t *object = NULL; + int op_ret = -1; + int op_errno = EINVAL; + char *buf = NULL; + struct iatt stbuf = {0, }; + int ret = -1; + struct stat stat = {0, }; + + GF_VALIDATE_OR_GOTO ("snap-view-daemon", 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); + + inode_ctx = svs_inode_ctx_get (this, loc->inode); + if (!inode_ctx) { + 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; + } + + fs = inode_ctx->fs; + if (!fs) { + gf_log (this->name, GF_LOG_ERROR, "failed to get the fs " + "instance for %s (gfid: %s)", loc->name, + uuid_utoa (loc->inode->gfid)); + op_ret = -1; + op_errno = ESTALE; + goto out; + } + + object = inode_ctx->object; + if (!object) { + gf_log (this->name, GF_LOG_ERROR, "failed to get the object " + "for %s (gfid: %s)", loc->name, + uuid_utoa (loc->inode->gfid)); + op_ret = -1; + op_errno = ESTALE; + goto out; + } + + ret = glfs_h_stat (fs, object, &stat); + if (ret) { + gf_log (this->name, GF_LOG_ERROR, "glfs_h_stat on %s " + "(gfid: %s) failed", loc->name, + uuid_utoa (loc->inode->gfid)); + op_ret = -1; + op_errno = errno; + goto out; + } + + iatt_from_stat (&stbuf, &stat); + uuid_copy (stbuf.ia_gfid, loc->inode->gfid); + svs_fill_ino_from_gfid (&stbuf); + + buf = alloca (size + 1); + op_ret = glfs_h_readlink (fs, object, buf, size); + if (op_ret == -1) { + gf_log (this->name, GF_LOG_ERROR, "readlink on %s failed " + "(gfid: %s)", loc->name, uuid_utoa (loc->inode->gfid)); + op_errno = errno; + goto out; + } + + buf[op_ret] = 0; + +out: + STACK_UNWIND_STRICT (readlink, frame, op_ret, op_errno, buf, &stbuf, + NULL); + + return 0; +} + +int32_t +svs_access (call_frame_t *frame, xlator_t *this, loc_t *loc, int mask, + dict_t *xdata) +{ + int ret = -1; + int32_t op_ret = -1; + int32_t op_errno = EINVAL; + svs_private_t *priv = NULL; + glfs_t *fs = NULL; + glfs_object_t *object = NULL; + svs_inode_t *inode_ctx = NULL; + gf_boolean_t is_fuse_call = 0; + int perm = 0; + int mode = 0; + + GF_VALIDATE_OR_GOTO ("svs", this, 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; + + inode_ctx = svs_inode_ctx_get (this, loc->inode); + if (!inode_ctx) { + gf_log (this->name, GF_LOG_ERROR, "inode context not found for" + " %s", uuid_utoa (loc->inode->gfid)); + op_ret = -1; + op_errno = EINVAL; + goto out; + } + + is_fuse_call = __is_fuse_call (frame); + + if (inode_ctx->type == SNAP_VIEW_ENTRY_POINT_INODE) { + if (is_fuse_call) { + op_ret = 0; + op_errno = 0; + } else { + op_ret = 0; + mode |= POSIX_ACL_READ; + mode |= POSIX_ACL_EXECUTE; + op_errno = mode; + } + goto out; + } + + fs = inode_ctx->fs; + object = inode_ctx->object; + + if (!is_fuse_call) + syncopctx_setfspid (&frame->root->pid); + + ret = glfs_h_access (fs, object, mask); + if (ret < 0) { + gf_log (this->name, GF_LOG_ERROR, "failed to access %s " + "(gfid: %s)", loc->path, uuid_utoa (loc->inode->gfid)); + op_ret = -1; + op_errno = errno; + goto out; + } + + /* The actual posix_acl xlator does acl checks differently for + fuse and nfs. In this case how to send the information of + whether the call came from fuse or nfs to the snapshot volume + via gfapi? + */ + + op_ret = 0; + op_errno = ret; + +out: + + STACK_UNWIND_STRICT (access, frame, op_ret, op_errno, NULL); + return 0; +} + + +int32_t +mem_acct_init (xlator_t *this) +{ + int ret = -1; + + if (!this) + return ret; + + ret = xlator_mem_acct_init (this, gf_svs_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) +{ + svs_private_t *priv = NULL; + int ret = -1; + + /* This can be the top of graph in certain cases */ + if (!this->parents) { + gf_log (this->name, GF_LOG_DEBUG, + "dangling volume. check volfile "); + } + + /* TODO: define a mem-type structure */ + priv = GF_CALLOC (1, sizeof (*priv), gf_svs_mt_priv_t); + if (!priv) + goto out; + + this->private = priv; + + /* get the list of snaps first to return to client xlator */ + ret = svs_get_snapshot_list (this, priv); + + ret = 0; +out: + if (ret && priv) { + GF_FREE (priv); + } + + return ret; +} + +void +fini (xlator_t *this) +{ + svs_private_t *priv = NULL; + priv = this->private; + this->private = NULL; + + if (priv) { + GF_FREE (priv); + } + + return; +} + +struct xlator_fops fops = { + .lookup = svs_lookup, + .stat = svs_stat, + .opendir = svs_opendir, + .readdirp = svs_readdirp, + .readdir = svs_readdir, + .open = svs_open, + .readv = svs_readv, + .flush = svs_flush, + .fstat = svs_fstat, + .getxattr = svs_getxattr, + .access = svs_access, + /* entry fops */ +}; + +struct xlator_cbks cbks = { + .release = svs_release, + .releasedir = svs_releasedir, + .forget = svs_forget, +}; + +struct volume_options options[] = { + /* This translator doesn't take any options, or provide any options */ + { .key = {NULL} }, +}; diff --git a/xlators/features/snapview-server/src/snapview-server.h b/xlators/features/snapview-server/src/snapview-server.h new file mode 100644 index 00000000000..b0e051e0fc3 --- /dev/null +++ b/xlators/features/snapview-server/src/snapview-server.h @@ -0,0 +1,88 @@ +/* + 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 __SNAP_VIEW_H__ +#define __SNAP_VIEW_H__ + +#ifndef _CONFIG_H +#define _CONFIG_H +#include "config.h" +#endif + +#include "dict.h" +#include "defaults.h" +#include "mem-types.h" +#include "call-stub.h" +#include "inode.h" +#include "byte-order.h" +#include "iatt.h" +#include <ctype.h> +#include <sys/uio.h> +#include "glusterfs.h" +#include "xlator.h" +#include "logging.h" +#include "glfs.h" +#include "common-utils.h" +#include "glfs-handles.h" +#include "glfs-internal.h" +#include "glusterfs3-xdr.h" +#include "glusterfs-acl.h" +#include "syncop.h" +#include "list.h" + +/* + * The max number of snap entries we consider currently + */ +#define SNAP_VIEW_MAX_NUM_SNAPS 128 + +#define DEFAULT_SVD_LOG_FILE_DIRECTORY DATADIR "/log/glusterfs" + +typedef enum { + SNAP_VIEW_ENTRY_POINT_INODE = 0, + SNAP_VIEW_VIRTUAL_INODE +} inode_type_t; + +struct svs_inode { + glfs_t *fs; + glfs_object_t *object; + inode_type_t type; + + /* used only for entry point directory where gfid of the directory + from where the entry point was entered is saved. + */ + uuid_t pargfid; + struct iatt buf; +}; +typedef struct svs_inode svs_inode_t; + +struct svs_fd { + glfs_fd_t *fd; +}; +typedef struct svs_fd svs_fd_t; + +struct snap_dirent { + char name[NAME_MAX]; + char uuid[UUID_CANONICAL_FORM_LEN + 1]; + glfs_t *fs; +}; +typedef struct snap_dirent snap_dirent_t; + +struct svs_private { + snap_dirent_t *dirents; + int num_snaps; +}; +typedef struct svs_private svs_private_t; + +glfs_t * +svs_intialise_snapshot_volume (xlator_t *this, const char *name); + +snap_dirent_t * +svs_get_snap_dirent (xlator_t *this, const char *name); + +#endif /* __SNAP_VIEW_H__ */ diff --git a/xlators/mount/fuse/src/fuse-resolve.c b/xlators/mount/fuse/src/fuse-resolve.c index 76b1d9a72cc..fc04d2c8efa 100644 --- a/xlators/mount/fuse/src/fuse-resolve.c +++ b/xlators/mount/fuse/src/fuse-resolve.c @@ -47,6 +47,11 @@ fuse_resolve_loc_touchup (fuse_state_t *state) } else if (loc->inode) { ret = inode_path (loc->inode, NULL, &path); uuid_copy (loc->gfid, loc->inode->gfid); + if (path) { + loc->name = strrchr (path, '/'); + if (loc->name) + loc->name++; + } } if (ret) gf_log (THIS->name, GF_LOG_TRACE, diff --git a/xlators/protocol/server/src/server-resolve.c b/xlators/protocol/server/src/server-resolve.c index b2bff5c531a..9384e765cca 100644 --- a/xlators/protocol/server/src/server-resolve.c +++ b/xlators/protocol/server/src/server-resolve.c @@ -45,8 +45,14 @@ resolve_loc_touchup (call_frame_t *frame) if (!loc->path) { if (loc->parent && resolve->bname) { ret = inode_path (loc->parent, resolve->bname, &path); + loc->name = resolve->bname; } else if (loc->inode) { ret = inode_path (loc->inode, NULL, &path); + if (path) { + loc->name = strrchr (path, '/'); + if (loc->name) + loc->name++; + } } if (ret) gf_log (frame->this->name, GF_LOG_TRACE, @@ -123,15 +129,29 @@ resolve_gfid_cbk (call_frame_t *frame, void *cookie, xlator_t *this, goto out; } - loc_wipe (resolve_loc); - link_inode = inode_link (inode, NULL, NULL, buf); - if (!link_inode) + if (!link_inode) { + loc_wipe (resolve_loc); goto out; + } inode_lookup (link_inode); + /* wipe the loc only after the inode has been linked to the inode + table. Otherwise before inode gets linked to the inode table, + inode would have been unrefed (this might have been destroyed + if refcount becomes 0, and put back to mempool). So once the + inode gets destroyed, inode_link is a redundant operation. But + without knowing that the destroyed inode's pointer is saved in + the resolved_loc as parent (while constructing loc for resolving + the entry) and the inode_new call for resolving the entry will + return the same pointer to the inode as the parent (because in + reality the inode is a free inode present in cold list of the + inode mem-pool). + */ + loc_wipe (resolve_loc); + if (uuid_is_null (resolve->pargfid)) { inode_unref (link_inode); goto out; @@ -143,13 +163,14 @@ resolve_gfid_cbk (call_frame_t *frame, void *cookie, xlator_t *this, resolve_loc->name = resolve->bname; resolve_loc->inode = inode_new (state->itable); + inode_path (resolve_loc->parent, resolve_loc->name, (char **) &resolve_loc->path); STACK_WIND (frame, resolve_gfid_entry_cbk, frame->root->client->bound_xl, frame->root->client->bound_xl->fops->lookup, - &resolve->resolve_loc, NULL); + &resolve->resolve_loc, state->xdata); return 0; out: resolve_continue (frame); @@ -182,7 +203,7 @@ resolve_gfid (call_frame_t *frame) STACK_WIND (frame, resolve_gfid_cbk, frame->root->client->bound_xl, frame->root->client->bound_xl->fops->lookup, - &resolve->resolve_loc, NULL); + &resolve->resolve_loc, state->xdata); return 0; } diff --git a/xlators/protocol/server/src/server-rpc-fops.c b/xlators/protocol/server/src/server-rpc-fops.c index c77748d69f1..f36bc16fc43 100644 --- a/xlators/protocol/server/src/server-rpc-fops.c +++ b/xlators/protocol/server/src/server-rpc-fops.c @@ -1897,6 +1897,8 @@ server_readdirp_cbk (call_frame_t *frame, void *cookie, xlator_t *this, rpcsvc_request_t *req = NULL; int ret = 0; + state = CALL_STATE (frame); + GF_PROTOCOL_DICT_SERIALIZE (this, xdata, &rsp.xdata.xdata_val, rsp.xdata.xdata_len, op_errno, out); @@ -1920,8 +1922,7 @@ server_readdirp_cbk (call_frame_t *frame, void *cookie, xlator_t *this, } } - /* TODO: need more clear thoughts before calling this function. */ - /* gf_link_inodes_from_dirent (this, state->fd->inode, entries); */ + gf_link_inodes_from_dirent (this, state->fd->inode, entries); out: rsp.op_ret = op_ret; |