summaryrefslogtreecommitdiffstats
path: root/xlators/features/snapview-server
diff options
context:
space:
mode:
authorRaghavendra Bhat <raghavendra@redhat.com>2014-05-07 20:13:43 +0530
committerVijay Bellur <vbellur@redhat.com>2014-05-29 09:25:46 -0700
commitcc0378d39f4082f51d5ef6e02b3007fe9e78cb31 (patch)
treef5c82bece9cf1a2fd79685ef2d89bcd2a5b8428f /xlators/features/snapview-server
parent58b9edee87bba3ffe812cf15f171926be017575b (diff)
user servicable snapshots
Change-Id: Idbf27dbe088e646a8ab81cedc5818413795895ea Signed-off-by: Raghavendra Bhat <raghavendra@redhat.com> Signed-off-by: Anand Subramanian <anands@redhat.com> Signed-off-by: Raghavendra Bhat <raghavendra@redhat.com> Reviewed-on: http://review.gluster.org/7700 Tested-by: Gluster Build System <jenkins@build.gluster.com> Reviewed-by: Vijay Bellur <vbellur@redhat.com>
Diffstat (limited to 'xlators/features/snapview-server')
-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
5 files changed, 2531 insertions, 0 deletions
diff --git a/xlators/features/snapview-server/Makefile.am b/xlators/features/snapview-server/Makefile.am
new file mode 100644
index 0000000..af437a6
--- /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 0000000..0966ae4
--- /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 0000000..6820b5e
--- /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 0000000..6553723
--- /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 0000000..b0e051e
--- /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__ */