summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/src/glfs-handleops.c51
-rw-r--r--api/src/glfs-handles.h3
-rw-r--r--configure.ac4
-rw-r--r--libglusterfs/src/syncop.c17
-rw-r--r--libglusterfs/src/xlator.h1
-rwxr-xr-xtests/bugs/bug-859581.t12
-rw-r--r--xlators/features/Makefile.am2
-rw-r--r--xlators/features/quota/src/quota.c3
-rw-r--r--xlators/features/snapview-client/Makefile.am1
-rw-r--r--xlators/features/snapview-client/src/Makefile.am15
-rw-r--r--xlators/features/snapview-client/src/snapview-client-mem-types.h23
-rw-r--r--xlators/features/snapview-client/src/snapview-client.c1687
-rw-r--r--xlators/features/snapview-client/src/snapview-client.h110
-rw-r--r--xlators/features/snapview-server/Makefile.am1
-rw-r--r--xlators/features/snapview-server/src/Makefile.am22
-rw-r--r--xlators/features/snapview-server/src/snapview-server-mem-types.h25
-rw-r--r--xlators/features/snapview-server/src/snapview-server.c2395
-rw-r--r--xlators/features/snapview-server/src/snapview-server.h88
-rw-r--r--xlators/mount/fuse/src/fuse-resolve.c5
-rw-r--r--xlators/protocol/server/src/server-resolve.c31
-rw-r--r--xlators/protocol/server/src/server-rpc-fops.c5
21 files changed, 4489 insertions, 12 deletions
diff --git a/api/src/glfs-handleops.c b/api/src/glfs-handleops.c
index f7ad454bf62..247b8aebf8b 100644
--- a/api/src/glfs-handleops.c
+++ b/api/src/glfs-handleops.c
@@ -945,6 +945,57 @@ out:
return glfd;
}
+int
+glfs_h_access (struct glfs *fs, struct glfs_object *object, int mask)
+{
+ int ret = -1;
+ xlator_t *subvol = NULL;
+ inode_t *inode = NULL;
+ loc_t loc = {0, };
+
+ /* validate in args */
+ if ((fs == NULL) || (object == NULL)) {
+ errno = EINVAL;
+ goto out;
+ }
+
+ __glfs_entry_fs (fs);
+
+ /* get the active volume */
+ subvol = glfs_active_subvol (fs);
+ if (!subvol) {
+ ret = -1;
+ errno = EIO;
+ goto out;
+ }
+
+ /* get/refresh the in arg objects inode in correlation to the xlator */
+ inode = glfs_resolve_inode (fs, subvol, object);
+ if (!inode) {
+ errno = ESTALE;
+ goto out;
+ }
+
+
+ GLFS_LOC_FILL_INODE (inode, loc, out);
+
+ /* fop/op */
+
+ ret = syncop_access (subvol, &loc, mask);
+ DECODE_SYNCOP_ERR (ret);
+
+out:
+ loc_wipe (&loc);
+
+ if (inode)
+ inode_unref (inode);
+
+
+ glfs_subvol_done (fs, subvol);
+
+ return ret;
+}
+
ssize_t
glfs_h_extract_handle (struct glfs_object *object, unsigned char *handle,
int len)
diff --git a/api/src/glfs-handles.h b/api/src/glfs-handles.h
index a6c6c0a3443..ad963455e85 100644
--- a/api/src/glfs-handles.h
+++ b/api/src/glfs-handles.h
@@ -162,6 +162,9 @@ struct glfs_fd *glfs_h_opendir (struct glfs *fs,
struct glfs_fd *glfs_h_open (struct glfs *fs, struct glfs_object *object,
int flags) __THROW;
+int
+glfs_h_access (struct glfs *fs, struct glfs_object *object, int mask) __THROW;
+
__END_DECLS
#endif /* !_GLFS_HANDLES_H */
diff --git a/configure.ac b/configure.ac
index 39bea64caa7..27b76004e93 100644
--- a/configure.ac
+++ b/configure.ac
@@ -139,6 +139,10 @@ AC_CONFIG_FILES([Makefile
xlators/features/protect/src/Makefile
xlators/features/gfid-access/Makefile
xlators/features/gfid-access/src/Makefile
+ xlators/features/snapview-server/Makefile
+ xlators/features/snapview-server/src/Makefile
+ xlators/features/snapview-client/Makefile
+ xlators/features/snapview-client/src/Makefile
xlators/playground/Makefile
xlators/playground/template/Makefile
xlators/playground/template/src/Makefile
diff --git a/libglusterfs/src/syncop.c b/libglusterfs/src/syncop.c
index 9705a7d5452..3dd622c8e99 100644
--- a/libglusterfs/src/syncop.c
+++ b/libglusterfs/src/syncop.c
@@ -2174,6 +2174,21 @@ syncop_access_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
return 0;
}
+/* posix_acl xlator will respond in different ways for access calls from
+ fuse and access calls from nfs. For fuse, checking op_ret is sufficient
+ to check whether the access call is successful or not. But for nfs the
+ mode of the access that is permitted is put into op_errno before unwind.
+ With syncop, the caller of syncop_access will not be able to get the
+ mode of the access despite call being successul (since syncop_access
+ returns only the op_ret collected in args).
+ Now, if access call is failed, then args.op_ret is returned to recognise
+ the failure. But if op_ret is zero, then the mode of access which is
+ set in args.op_errno is returned. Thus the caller of syncop_access
+ has to check whether the return value is less than zero or not. If the
+ return value it got is less than zero, then access call is failed.
+ If it is not, then the access call is successful and the value the caller
+ got is the mode of the access.
+*/
int
syncop_access (xlator_t *subvol, loc_t *loc, int32_t mask)
{
@@ -2184,7 +2199,7 @@ syncop_access (xlator_t *subvol, loc_t *loc, int32_t mask)
if (args.op_ret < 0)
return -args.op_errno;
- return args.op_ret;
+ return args.op_errno;
}
diff --git a/libglusterfs/src/xlator.h b/libglusterfs/src/xlator.h
index 1daa06ec2ed..47ae6cdd6b6 100644
--- a/libglusterfs/src/xlator.h
+++ b/libglusterfs/src/xlator.h
@@ -29,6 +29,7 @@
#include "latency.h"
#define FIRST_CHILD(xl) (xl->children->xlator)
+#define SECOND_CHILD(xl) (xl->children->next->xlator)
#define GF_SET_ATTR_MODE 0x1
#define GF_SET_ATTR_UID 0x2
diff --git a/tests/bugs/bug-859581.t b/tests/bugs/bug-859581.t
index f31e8b311ef..0aab48bb02b 100755
--- a/tests/bugs/bug-859581.t
+++ b/tests/bugs/bug-859581.t
@@ -22,7 +22,7 @@ TEST rm -f $(gf_get_gfid_backend_file_path $B0/${V0}1 "dir1")
TEST rmdir $B0/${V0}1/dir1/dir2
TEST $CLI volume heal $V0 full
-sleep 5
+EXPECT_WITHIN $HEAL_TIMEOUT "0" afr_get_pending_heal_count $V0
TEST [ -d $B0/${V0}1/dir1/dir2 ]
TEST [ ! -d $(gf_get_gfid_backend_file_path $B0/${V0}1 "dir1") ]
@@ -35,7 +35,15 @@ TEST $CLI volume start $V0
EXPECT 'Started' volinfo_field $V0 'Status';
TEST glusterfs --entry-timeout=0 --attribute-timeout=0 -s $H0 --volfile-id $V0 $M0;
-ls -l $M0/
+# Till now, protocol/server was not doing inode linking as part of readdirp.
+# But pas part of user servicable snapshots patcth, changes to do inode linking
+# in protocol/server in readdirp, were introduced. So now to make sure
+# the gfid handle of dir1 is healed, explicit lookup has to be sent on it.
+# Otherwise, whenever ls -l is done just on the mount point $M0, lookup on the
+# entries received as part of readdirp, is not sent, because the inodes for
+# those entries were linked as part of readdirp itself. i.e instead of doing
+# "ls -l $M0", it has to be the below command.
+ls -l $M0/dir1;
TEST [ -h $(gf_get_gfid_backend_file_path $B0/${V0}1 "dir1") ]
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;