diff options
Diffstat (limited to 'xlators/features/snapview-server')
8 files changed, 4319 insertions, 0 deletions
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..2935f138a4c --- /dev/null +++ b/xlators/features/snapview-server/src/Makefile.am @@ -0,0 +1,25 @@ +if WITH_SERVER +xlator_LTLIBRARIES = snapview-server.la +endif +xlatordir = $(libdir)/glusterfs/$(PACKAGE_VERSION)/xlator/features + +snapview_server_la_LDFLAGS = -module $(GF_XLATOR_DEFAULT_LDFLAGS) + +snapview_server_la_SOURCES = snapview-server.c snapview-server-mgmt.c \ + snapview-server-helpers.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 snapview-server-messages.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 -I$(top_builddir)/rpc/xdr/src \ + -DDATADIR=\"$(localstatedir)\" + +AM_CFLAGS = -Wall $(GF_CFLAGS) + +CLEANFILES = diff --git a/xlators/features/snapview-server/src/snapview-server-helpers.c b/xlators/features/snapview-server/src/snapview-server-helpers.c new file mode 100644 index 00000000000..62c1ddac49c --- /dev/null +++ b/xlators/features/snapview-server/src/snapview-server-helpers.c @@ -0,0 +1,715 @@ +/* + 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. +*/ +#include "snapview-server.h" +#include "snapview-server-mem-types.h" + +#include <glusterfs/xlator.h> +#include "rpc-clnt.h" +#include "xdr-generic.h" +#include "protocol-common.h" +#include <pthread.h> + +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(void) +{ + 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(); + 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(void) +{ + 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(); + if (!svs_fd) { + gf_msg(this->name, GF_LOG_ERROR, 0, SVS_MSG_NEW_FD_CTX_FAILED, + "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_msg(this->name, GF_LOG_ERROR, 0, + SVS_MSG_GET_INODE_CONTEXT_FAILED, + "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_msg(this->name, GF_LOG_ERROR, errno, SVS_MSG_OPENDIR_FAILED, + "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_msg(this->name, GF_LOG_ERROR, errno, SVS_MSG_OPEN_FAILED, + "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_msg(this->name, GF_LOG_ERROR, 0, SVS_MSG_SET_FD_CONTEXT_FAILED, + "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_msg(this->name, GF_LOG_ERROR, errno, + SVS_MSG_CLOSEDIR_FAILED, + "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_msg(this->name, GF_LOG_ERROR, 0, SVS_MSG_CLOSE_FAILED, + "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; +} + +int +svs_uuid_generate(xlator_t *this, uuid_t gfid, char *snapname, + uuid_t origin_gfid) +{ + char ino_string[NAME_MAX + 32] = ""; + uuid_t tmp = { + 0, + }; + int ret = -1; + + GF_VALIDATE_OR_GOTO("snapview-server", this, out); + GF_VALIDATE_OR_GOTO(this->name, snapname, out); + + (void)snprintf(ino_string, sizeof(ino_string), "%s%s", snapname, + uuid_utoa(origin_gfid)); + + if (gf_gfid_generate_from_xxh64(tmp, ino_string)) { + gf_msg(this->name, GF_LOG_WARNING, 0, SVS_MSG_GFID_GEN_FAILED, + "failed to generate " + "gfid for object with actual gfid of %s " + "(snapname: %s, key: %s)", + uuid_utoa(origin_gfid), snapname, ino_string); + goto out; + } + + gf_uuid_copy(gfid, tmp); + + ret = 0; + + gf_msg_debug(this->name, 0, "gfid generated is %s ", uuid_utoa(gfid)); + +out: + return ret; +} + +void +svs_fill_ino_from_gfid(struct iatt *buf) +{ + 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 (gf_uuid_is_null(buf->ia_gfid)) { + buf->ia_ino = -1; + goto out; + } + + buf->ia_ino = gfid_to_ino(buf->ia_gfid); +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; + + gf_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; +} + +/* priv->snaplist_lock should be held before calling this function */ +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; + + private + = this->private; + + dirents = private->dirents; + if (!dirents) { + goto out; + } + + 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, + int32_t *op_errno) +{ + svs_private_t *priv = NULL; + int32_t ret = -1; + int32_t local_errno = ESTALE; + snap_dirent_t *dirent = NULL; + char volname[PATH_MAX] = { + 0, + }; + glfs_t *fs = NULL; + int loglevel = GF_LOG_INFO; + char logfile[PATH_MAX] = { + 0, + }; + char *volfile_server = 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); + + priv = this->private; + + dirent = __svs_get_snap_dirent(this, name); + if (!dirent) { + gf_msg_debug(this->name, 0, + "snap entry for " + "name %s not found", + name); + local_errno = ENOENT; + goto out; + } + + if (dirent->fs) { + ret = 0; + fs = dirent->fs; + goto out; + } + + snprintf(volname, sizeof(volname), "/snaps/%s/%s/%s", dirent->name, + dirent->snap_volname, dirent->snap_volname); + + fs = glfs_new(volname); + if (!fs) { + local_errno = ENOMEM; + gf_msg(this->name, GF_LOG_ERROR, local_errno, SVS_MSG_GLFS_NEW_FAILED, + "glfs instance for snap volume %s " + "failed", + dirent->name); + goto out; + } + + /* + * Before, localhost was used as the volfile server. But, with that + * method, accessing snapshots started giving ENOENT error if a + * specific bind address is mentioned in the glusterd volume file. + * Check the bug https://bugzilla.redhat.com/show_bug.cgi?id=1725211. + * So, the new method is tried below, where, snapview-server first + * uses the volfile server used by the snapd (obtained from the + * command line arguments saved in the global context of the process). + * If the volfile server in global context is NULL, then localhost + * is tried (like before). + */ + if (this->ctx->cmd_args.volfile_server) { + volfile_server = gf_strdup(this->ctx->cmd_args.volfile_server); + if (!volfile_server) { + gf_msg(this->name, GF_LOG_WARNING, ENOMEM, + SVS_MSG_VOLFILE_SERVER_GET_FAIL, + "failed to copy volfile server %s. ", + this->ctx->cmd_args.volfile_server); + ret = -1; + goto out; + } + } else { + gf_msg(this->name, GF_LOG_WARNING, ENOMEM, + SVS_MSG_VOLFILE_SERVER_GET_FAIL, + "volfile server is NULL in cmd args. " + "Trying with localhost"); + volfile_server = gf_strdup("localhost"); + if (!volfile_server) { + gf_msg(this->name, GF_LOG_WARNING, ENOMEM, + SVS_MSG_VOLFILE_SERVER_GET_FAIL, + "failed to copy volfile server localhost."); + ret = -1; + goto out; + } + } + + ret = glfs_set_volfile_server(fs, "tcp", volfile_server, 24007); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, local_errno, + SVS_MSG_SET_VOLFILE_SERVR_FAILED, + "setting the " + "volfile server %s for snap volume %s " + "failed", + volfile_server, dirent->name); + goto out; + } + + snprintf(logfile, sizeof(logfile), + DEFAULT_SVD_LOG_FILE_DIRECTORY "/snaps/%s/%s-%s.log", + priv->volname, name, dirent->uuid); + + ret = glfs_set_logging(fs, logfile, loglevel); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, local_errno, + SVS_MSG_SET_LOGGING_FAILED, + "failed to set the " + "log file path"); + goto out; + } + + ret = glfs_init(fs); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, local_errno, SVS_MSG_GLFS_INIT_FAILED, + "initing the " + "fs for %s failed", + dirent->name); + goto out; + } + + ret = 0; + +out: + if (ret) { + if (op_errno) + *op_errno = local_errno; + + if (fs) + glfs_fini(fs); + fs = NULL; + } + + if (fs) { + dirent->fs = fs; + } + + GF_FREE(volfile_server); + return fs; +} + +glfs_t * +svs_initialise_snapshot_volume(xlator_t *this, const char *name, + int32_t *op_errno) +{ + glfs_t *fs = NULL; + svs_private_t *priv = 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); + + priv = this->private; + + LOCK(&priv->snaplist_lock); + { + fs = __svs_initialise_snapshot_volume(this, name, op_errno); + } + UNLOCK(&priv->snaplist_lock); + +out: + + 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; + + GF_VALIDATE_OR_GOTO("svs", this, out); + + priv = this->private; + + LOCK(&priv->snaplist_lock); + { + dirents = priv->dirents; + if (!dirents) { + goto unlock; + } + if (priv->num_snaps) + dirent = &dirents[priv->num_snaps - 1]; + } +unlock: + UNLOCK(&priv->snaplist_lock); + +out: + return dirent; +} + +glfs_t * +svs_get_latest_snapshot(xlator_t *this) +{ + glfs_t *fs = NULL; + snap_dirent_t *dirent = NULL; + svs_private_t *priv = NULL; + + GF_VALIDATE_OR_GOTO("svs", this, out); + priv = this->private; + + dirent = svs_get_latest_snap_entry(this); + + if (dirent) { + LOCK(&priv->snaplist_lock); + { + fs = dirent->fs; + } + UNLOCK(&priv->snaplist_lock); + } + +out: + return fs; +} + +glfs_t * +svs_inode_ctx_glfs_mapping(xlator_t *this, svs_inode_t *inode_ctx) +{ + glfs_t *fs = NULL; + + GF_VALIDATE_OR_GOTO("svs", this, out); + GF_VALIDATE_OR_GOTO(this->name, inode_ctx, out); + + fs = inode_ctx->fs; + + SVS_CHECK_VALID_SNAPSHOT_HANDLE(fs, this); + +out: + return fs; +} + +glfs_t * +svs_inode_glfs_mapping(xlator_t *this, inode_t *inode) +{ + svs_inode_t *inode_ctx = NULL; + glfs_t *fs = NULL; + + inode_ctx = svs_inode_ctx_get(this, inode); + if (!inode_ctx) { + gf_msg(this->name, GF_LOG_ERROR, 0, SVS_MSG_GET_INODE_CONTEXT_FAILED, + "inode context not found for" + " the inode %s", + uuid_utoa(inode->gfid)); + goto out; + } + + fs = svs_inode_ctx_glfs_mapping(this, inode_ctx); + +out: + return fs; +} 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..63456b85323 --- /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 <glusterfs/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_snaplist_t, + gf_svs_mt_end +}; + +#endif diff --git a/xlators/features/snapview-server/src/snapview-server-messages.h b/xlators/features/snapview-server/src/snapview-server-messages.h new file mode 100644 index 00000000000..f634ab5d2b0 --- /dev/null +++ b/xlators/features/snapview-server/src/snapview-server-messages.h @@ -0,0 +1,54 @@ +/* + Copyright (c) 2018 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 _SNAPVIEW_SERVER_MESSAGES_H_ +#define _SNAPVIEW_SERVER_MESSAGES_H_ + +#include <glusterfs/glfs-message-id.h> + +/* To add new message IDs, append new identifiers at the end of the list. + * + * Never remove a message ID. If it's not used anymore, you can rename it or + * leave it as it is, but not delete it. This is to prevent reutilization of + * IDs by other messages. + * + * The component name must match one of the entries defined in + * glfs-message-id.h. + */ + +GLFS_MSGID(SNAPVIEW_SERVER, SVS_MSG_NO_MEMORY, SVS_MSG_MEM_ACNT_FAILED, + SVS_MSG_NULL_GFID, SVS_MSG_GET_LATEST_SNAP_FAILED, + SVS_MSG_INVALID_GLFS_CTX, SVS_MSG_LOCK_DESTROY_FAILED, + SVS_MSG_SNAPSHOT_LIST_CHANGED, SVS_MSG_MGMT_INIT_FAILED, + SVS_MSG_GET_SNAPSHOT_LIST_FAILED, SVS_MSG_GET_GLFS_H_OBJECT_FAILED, + SVS_MSG_PARENT_CTX_OR_NAME_NULL, SVS_MSG_SET_INODE_CONTEXT_FAILED, + SVS_MSG_GET_INODE_CONTEXT_FAILED, SVS_MSG_NEW_INODE_CTX_FAILED, + SVS_MSG_DELETE_INODE_CONTEXT_FAILED, SVS_MSG_SET_FD_CONTEXT_FAILED, + SVS_MSG_NEW_FD_CTX_FAILED, SVS_MSG_DELETE_FD_CTX_FAILED, + SVS_MSG_GETXATTR_FAILED, SVS_MSG_LISTXATTR_FAILED, + SVS_MSG_RELEASEDIR_FAILED, SVS_MSG_RELEASE_FAILED, + SVS_MSG_TELLDIR_FAILED, SVS_MSG_STAT_FAILED, SVS_MSG_STATFS_FAILED, + SVS_MSG_OPEN_FAILED, SVS_MSG_READ_FAILED, SVS_MSG_READLINK_FAILED, + SVS_MSG_ACCESS_FAILED, SVS_MSG_GET_FD_CONTEXT_FAILED, + SVS_MSG_DICT_SET_FAILED, SVS_MSG_OPENDIR_FAILED, + SVS_MSG_FS_INSTANCE_INVALID, SVS_MSG_SETFSUID_FAIL, + SVS_MSG_SETFSGID_FAIL, SVS_MSG_SETFSGRPS_FAIL, + SVS_MSG_BUILD_TRNSPRT_OPT_FAILED, SVS_MSG_RPC_INIT_FAILED, + SVS_MSG_REG_NOTIFY_FAILED, SVS_MSG_REG_CBK_PRGM_FAILED, + SVS_MSG_RPC_CLNT_START_FAILED, SVS_MSG_XDR_PAYLOAD_FAILED, + SVS_MSG_NULL_CTX, SVS_MSG_RPC_CALL_FAILED, SVS_MSG_XDR_DECODE_FAILED, + SVS_MSG_RSP_DICT_EMPTY, SVS_MSG_DICT_GET_FAILED, + SVS_MSG_SNAP_LIST_REFRESH_FAILED, SVS_MSG_RPC_REQ_FAILED, + SVS_MSG_CLOSEDIR_FAILED, SVS_MSG_CLOSE_FAILED, + SVS_MSG_GFID_GEN_FAILED, SVS_MSG_GLFS_NEW_FAILED, + SVS_MSG_SET_VOLFILE_SERVR_FAILED, SVS_MSG_SET_LOGGING_FAILED, + SVS_MSG_VOLFILE_SERVER_GET_FAIL, SVS_MSG_GLFS_INIT_FAILED); + +#endif /* !_SNAPVIEW_CLIENT_MESSAGES_H_ */ diff --git a/xlators/features/snapview-server/src/snapview-server-mgmt.c b/xlators/features/snapview-server/src/snapview-server-mgmt.c new file mode 100644 index 00000000000..ecf31c3b880 --- /dev/null +++ b/xlators/features/snapview-server/src/snapview-server-mgmt.c @@ -0,0 +1,524 @@ +/* + 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. +*/ +#include "snapview-server.h" +#include "snapview-server-mem-types.h" +#include <pthread.h> + +int +mgmt_cbk_snap(struct rpc_clnt *rpc, void *mydata, void *data) +{ + xlator_t *this = NULL; + + this = mydata; + GF_ASSERT(this); + + gf_msg("mgmt", GF_LOG_INFO, 0, SVS_MSG_SNAPSHOT_LIST_CHANGED, + "list of snapshots changed"); + + svs_get_snapshot_list(this); + return 0; +} + +static rpcclnt_cb_actor_t svs_cbk_actors[GF_CBK_MAXVALUE] = { + [GF_CBK_GET_SNAPS] = {"GETSNAPS", mgmt_cbk_snap, GF_CBK_GET_SNAPS}, +}; + +static struct rpcclnt_cb_program svs_cbk_prog = { + .progname = "GlusterFS Callback", + .prognum = GLUSTER_CBK_PROGRAM, + .progver = GLUSTER_CBK_VERSION, + .actors = svs_cbk_actors, + .numactors = GF_CBK_MAXVALUE, +}; + +static char *clnt_handshake_procs[GF_HNDSK_MAXVALUE] = { + [GF_HNDSK_NULL] = "NULL", + [GF_HNDSK_EVENT_NOTIFY] = "EVENTNOTIFY", +}; + +static rpc_clnt_prog_t svs_clnt_handshake_prog = { + .progname = "GlusterFS Handshake", + .prognum = GLUSTER_HNDSK_PROGRAM, + .progver = GLUSTER_HNDSK_VERSION, + .procnames = clnt_handshake_procs, +}; + +static int +svs_rpc_notify(struct rpc_clnt *rpc, void *mydata, rpc_clnt_event_t event, + void *data) +{ + xlator_t *this = NULL; + int ret = 0; + + this = mydata; + + switch (event) { + case RPC_CLNT_CONNECT: + ret = svs_get_snapshot_list(this); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, EINVAL, + SVS_MSG_GET_SNAPSHOT_LIST_FAILED, + "Error in refreshing the snaplist " + "infrastructure"); + ret = -1; + } + break; + default: + break; + } + return ret; +} + +int +svs_mgmt_init(xlator_t *this) +{ + int ret = -1; + svs_private_t *priv = NULL; + dict_t *options = NULL; + int port = GF_DEFAULT_BASE_PORT; + char *host = NULL; + cmd_args_t *cmd_args = NULL; + glusterfs_ctx_t *ctx = NULL; + xlator_cmdline_option_t *opt = NULL; + + GF_VALIDATE_OR_GOTO("snapview-server", this, out); + GF_VALIDATE_OR_GOTO(this->name, this->private, out); + GF_VALIDATE_OR_GOTO(this->name, this->ctx, out); + + priv = this->private; + + ctx = this->ctx; + cmd_args = &ctx->cmd_args; + + host = "localhost"; + if (cmd_args->volfile_server) + host = cmd_args->volfile_server; + + options = dict_new(); + if (!options) + goto out; + + opt = find_xlator_option_in_cmd_args_t("address-family", cmd_args); + ret = rpc_transport_inet_options_build(options, host, port, + (opt != NULL ? opt->value : NULL)); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, SVS_MSG_BUILD_TRNSPRT_OPT_FAILED, + "failed to build the " + "transport options"); + goto out; + } + + priv->rpc = rpc_clnt_new(options, this, this->name, 8); + if (!priv->rpc) { + gf_msg(this->name, GF_LOG_ERROR, 0, SVS_MSG_RPC_INIT_FAILED, + "failed to initialize RPC"); + goto out; + } + + ret = rpc_clnt_register_notify(priv->rpc, svs_rpc_notify, this); + if (ret) { + gf_msg(this->name, GF_LOG_WARNING, 0, SVS_MSG_REG_NOTIFY_FAILED, + "failed to register notify function"); + goto out; + } + + ret = rpcclnt_cbk_program_register(priv->rpc, &svs_cbk_prog, this); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, SVS_MSG_REG_CBK_PRGM_FAILED, + "failed to register callback program"); + goto out; + } + + ret = rpc_clnt_start(priv->rpc); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, SVS_MSG_RPC_CLNT_START_FAILED, + "failed to start the rpc " + "client"); + goto out; + } + + ret = 0; + + gf_msg_debug(this->name, 0, "svs mgmt init successful"); + +out: + if (options) + dict_unref(options); + if (ret) + if (priv) { + rpc_clnt_connection_cleanup(&priv->rpc->conn); + rpc_clnt_unref(priv->rpc); + priv->rpc = NULL; + } + + return ret; +} + +int +svs_mgmt_submit_request(void *req, call_frame_t *frame, glusterfs_ctx_t *ctx, + rpc_clnt_prog_t *prog, int procnum, fop_cbk_fn_t cbkfn, + xdrproc_t xdrproc) +{ + int ret = -1; + int count = 0; + struct iovec iov = { + 0, + }; + struct iobuf *iobuf = NULL; + struct iobref *iobref = NULL; + ssize_t xdr_size = 0; + + GF_VALIDATE_OR_GOTO("snapview-server", frame, out); + GF_VALIDATE_OR_GOTO("snapview-server", req, out); + GF_VALIDATE_OR_GOTO("snapview-server", ctx, out); + GF_VALIDATE_OR_GOTO("snapview-server", prog, out); + + GF_ASSERT(frame->this); + + iobref = iobref_new(); + if (!iobref) { + gf_msg(frame->this->name, GF_LOG_WARNING, ENOMEM, SVS_MSG_NO_MEMORY, + "failed to allocate " + "new iobref"); + goto out; + } + + if (req) { + xdr_size = xdr_sizeof(xdrproc, req); + + iobuf = iobuf_get2(ctx->iobuf_pool, xdr_size); + if (!iobuf) { + goto out; + } + + iobref_add(iobref, iobuf); + + iov.iov_base = iobuf->ptr; + iov.iov_len = iobuf_pagesize(iobuf); + + /* Create the xdr payload */ + ret = xdr_serialize_generic(iov, req, xdrproc); + if (ret == -1) { + gf_msg(frame->this->name, GF_LOG_WARNING, 0, + SVS_MSG_XDR_PAYLOAD_FAILED, "Failed to create XDR payload"); + goto out; + } + iov.iov_len = ret; + count = 1; + } + + ret = rpc_clnt_submit(ctx->mgmt, prog, procnum, cbkfn, &iov, count, NULL, 0, + iobref, frame, NULL, 0, NULL, 0, NULL); + +out: + if (iobref) + iobref_unref(iobref); + + if (iobuf) + iobuf_unref(iobuf); + return ret; +} + +int +mgmt_get_snapinfo_cbk(struct rpc_req *req, struct iovec *iov, int count, + void *myframe) +{ + gf_getsnap_name_uuid_rsp rsp = { + 0, + }; + call_frame_t *frame = NULL; + glusterfs_ctx_t *ctx = NULL; + int ret = -1; + dict_t *dict = NULL; + char key[32] = {0}; + int len; + int snapcount = 0; + svs_private_t *priv = NULL; + xlator_t *this = NULL; + int i = 0; + int j = 0; + char *value = NULL; + snap_dirent_t *dirents = NULL; + snap_dirent_t *old_dirents = NULL; + int oldcount = 0; + + GF_VALIDATE_OR_GOTO("snapview-server", req, error_out); + GF_VALIDATE_OR_GOTO("snapview-server", myframe, error_out); + GF_VALIDATE_OR_GOTO("snapview-server", iov, error_out); + + frame = myframe; + this = frame->this; + ctx = frame->this->ctx; + priv = this->private; + + if (!ctx) { + errno = EINVAL; + gf_msg(frame->this->name, GF_LOG_ERROR, errno, SVS_MSG_NULL_CTX, + "NULL context"); + goto out; + } + + if (-1 == req->rpc_status) { + errno = EINVAL; + gf_msg(frame->this->name, GF_LOG_ERROR, errno, SVS_MSG_RPC_CALL_FAILED, + "RPC call is not successful"); + goto out; + } + + ret = xdr_to_generic(*iov, &rsp, (xdrproc_t)xdr_gf_getsnap_name_uuid_rsp); + if (ret < 0) { + gf_msg(frame->this->name, GF_LOG_ERROR, 0, SVS_MSG_XDR_DECODE_FAILED, + "Failed to decode xdr response, rsp.op_ret = %d", rsp.op_ret); + goto out; + } + + if (rsp.op_ret == -1) { + errno = rsp.op_errno; + ret = -1; + goto out; + } + + if (!rsp.dict.dict_len) { + ret = -1; + errno = EINVAL; + gf_msg(frame->this->name, GF_LOG_ERROR, errno, SVS_MSG_RSP_DICT_EMPTY, + "Response dict is not populated"); + goto out; + } + + dict = dict_new(); + if (!dict) { + ret = -1; + errno = ENOMEM; + goto out; + } + + ret = dict_unserialize(rsp.dict.dict_val, rsp.dict.dict_len, &dict); + if (ret) { + errno = EINVAL; + gf_msg(frame->this->name, GF_LOG_ERROR, errno, + LG_MSG_DICT_UNSERIAL_FAILED, "Failed to unserialize dictionary"); + goto out; + } + + ret = dict_get_int32(dict, "snap-count", (int32_t *)&snapcount); + if (ret) { + errno = EINVAL; + ret = -1; + gf_msg(this->name, GF_LOG_ERROR, errno, SVS_MSG_DICT_GET_FAILED, + "Error retrieving snapcount"); + goto out; + } + + if (snapcount > 0) { + /* first time we are fetching snap list */ + dirents = GF_CALLOC(snapcount, sizeof(snap_dirent_t), + gf_svs_mt_dirents_t); + if (!dirents) { + errno = ENOMEM; + ret = -1; + gf_msg(frame->this->name, GF_LOG_ERROR, errno, SVS_MSG_NO_MEMORY, + "Unable to allocate memory"); + goto out; + } + } + + for (i = 0; i < snapcount; i++) { + len = snprintf(key, sizeof(key), "snap-volname.%d", i + 1); + ret = dict_get_strn(dict, key, len, &value); + if (ret) { + errno = EINVAL; + ret = -1; + gf_msg(this->name, GF_LOG_ERROR, errno, SVS_MSG_DICT_GET_FAILED, + "Error retrieving snap volname %d", i + 1); + goto out; + } + + strncpy(dirents[i].snap_volname, value, + sizeof(dirents[i].snap_volname)); + + len = snprintf(key, sizeof(key), "snap-id.%d", i + 1); + ret = dict_get_strn(dict, key, len, &value); + if (ret) { + errno = EINVAL; + ret = -1; + gf_msg(this->name, GF_LOG_ERROR, errno, SVS_MSG_DICT_GET_FAILED, + "Error retrieving snap uuid %d", i + 1); + goto out; + } + strncpy(dirents[i].uuid, value, sizeof(dirents[i].uuid)); + + len = snprintf(key, sizeof(key), "snapname.%d", i + 1); + ret = dict_get_strn(dict, key, len, &value); + if (ret) { + errno = EINVAL; + ret = -1; + gf_msg(this->name, GF_LOG_ERROR, errno, SVS_MSG_DICT_GET_FAILED, + "Error retrieving snap name %d", i + 1); + goto out; + } + strncpy(dirents[i].name, value, sizeof(dirents[i].name)); + } + + /* + * Got the new snap list populated in dirents + * The new snap list is either a subset or a superset of + * the existing snaplist old_dirents which has priv->num_snaps + * number of entries. + * + * If subset, then clean up the fs for entries which are + * no longer relevant. + * + * For other overlapping entries set the fs for new dirents + * entries which have a fs assigned already in old_dirents + * + * We do this as we don't want to do new glfs_init()s repeatedly + * as the dirents entries for snapshot volumes get repatedly + * cleaned up and allocated. And if we don't then that will lead + * to memleaks + */ + + LOCK(&priv->snaplist_lock); + { + oldcount = priv->num_snaps; + old_dirents = priv->dirents; + for (i = 0; i < priv->num_snaps; i++) { + for (j = 0; j < snapcount; j++) { + if ((!strcmp(old_dirents[i].name, dirents[j].name)) && + (!strcmp(old_dirents[i].uuid, dirents[j].uuid))) { + dirents[j].fs = old_dirents[i].fs; + old_dirents[i].fs = NULL; + break; + } + } + } + + priv->dirents = dirents; + priv->num_snaps = snapcount; + } + UNLOCK(&priv->snaplist_lock); + + if (old_dirents) { + for (i = 0; i < oldcount; i++) { + if (old_dirents[i].fs) + gf_msg_debug(this->name, 0, + "calling glfs_fini on " + "name: %s, snap_volname: %s, uuid: %s", + old_dirents[i].name, old_dirents[i].snap_volname, + old_dirents[i].uuid); + glfs_fini(old_dirents[i].fs); + } + } + + GF_FREE(old_dirents); + + ret = 0; + +out: + if (dict) { + dict_unref(dict); + } + free(rsp.dict.dict_val); + free(rsp.op_errstr); + + if (ret && dirents) { + gf_msg(this->name, GF_LOG_WARNING, 0, SVS_MSG_SNAP_LIST_REFRESH_FAILED, + "Could not update dirents with refreshed snap list"); + GF_FREE(dirents); + } + + if (myframe) + SVS_STACK_DESTROY(myframe); + +error_out: + return ret; +} + +int +svs_get_snapshot_list(xlator_t *this) +{ + gf_getsnap_name_uuid_req req = {{ + 0, + }}; + int ret = -1; + dict_t *dict = NULL; + glusterfs_ctx_t *ctx = NULL; + call_frame_t *frame = NULL; + svs_private_t *priv = NULL; + gf_boolean_t frame_cleanup = _gf_true; + + GF_VALIDATE_OR_GOTO("snapview-server", this, out); + + ctx = this->ctx; + if (!ctx) { + gf_msg(this->name, GF_LOG_ERROR, 0, SVS_MSG_NULL_CTX, "ctx is NULL"); + goto out; + } + + frame = create_frame(this, ctx->pool); + if (!frame) { + gf_msg(this->name, GF_LOG_ERROR, 0, LG_MSG_FRAME_ERROR, + "Error allocating frame"); + goto out; + } + + priv = this->private; + + dict = dict_new(); + if (!dict) { + gf_msg(this->name, GF_LOG_ERROR, ENOMEM, SVS_MSG_NO_MEMORY, + "Error allocating dictionary"); + goto out; + } + + ret = dict_set_str(dict, "volname", priv->volname); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, SVS_MSG_DICT_SET_FAILED, + "Error setting volname in dict"); + goto out; + } + + ret = dict_allocate_and_serialize(dict, &req.dict.dict_val, + &req.dict.dict_len); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, LG_MSG_DICT_UNSERIAL_FAILED, + "Failed to serialize dictionary"); + ret = -1; + goto out; + } + + ret = svs_mgmt_submit_request( + &req, frame, ctx, &svs_clnt_handshake_prog, GF_HNDSK_GET_SNAPSHOT_INFO, + mgmt_get_snapinfo_cbk, (xdrproc_t)xdr_gf_getsnap_name_uuid_req); + + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, 0, SVS_MSG_RPC_REQ_FAILED, + "Error sending snapshot names RPC request"); + } + + frame_cleanup = _gf_false; + +out: + if (dict) { + dict_unref(dict); + } + GF_FREE(req.dict.dict_val); + + if (frame_cleanup && frame) { + /* + * Destroy the frame if we encountered an error + * Else we need to clean it up in + * mgmt_get_snapinfo_cbk + */ + SVS_STACK_DESTROY(frame); + } + + return ret; +} 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..76cccae5914 --- /dev/null +++ b/xlators/features/snapview-server/src/snapview-server.c @@ -0,0 +1,2720 @@ +/* + 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. +*/ +#include "snapview-server.h" +#include "snapview-server-mem-types.h" +#include <glusterfs/compat-errno.h> + +#include <glusterfs/xlator.h> +#include "rpc-clnt.h" +#include "xdr-generic.h" +#include "protocol-common.h" +#include <glusterfs/syscall.h> +#include <pthread.h> + +#include "glfs-internal.h" + +int +gf_setcredentials(uid_t *uid, gid_t *gid, uint16_t ngrps, uint32_t *groups) +{ + int ret = 0; + + if (uid) { + ret = glfs_setfsuid(*uid); + if (ret != 0) { + gf_msg("snapview-server", GF_LOG_ERROR, 0, SVS_MSG_SETFSUID_FAIL, + "failed to set uid " + "%u in thread context", + *uid); + return ret; + } + } + if (gid) { + ret = glfs_setfsgid(*gid); + if (ret != 0) { + gf_msg("snapview-server", GF_LOG_ERROR, 0, SVS_MSG_SETFSGID_FAIL, + "failed to set gid " + "%u in thread context", + *gid); + return ret; + } + } + + if (ngrps != 0 && groups) { + ret = glfs_setfsgroups(ngrps, groups); + if (ret != 0) { + gf_msg("snapview-server", GF_LOG_ERROR, 0, SVS_MSG_SETFSGRPS_FAIL, + "failed to set " + "groups in thread context"); + return ret; + } + } + return 0; +} + +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 (gf_uuid_is_null(loc->inode->gfid)) { + gf_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) { + op_ret = -1; + *op_errno = ENOMEM; + gf_msg(this->name, GF_LOG_ERROR, *op_errno, + SVS_MSG_NEW_INODE_CTX_FAILED, + "failed to " + "allocate inode context for entry point " + "directory"); + goto out; + } + + gf_uuid_copy(inode_ctx->pargfid, loc->pargfid); + memcpy(&inode_ctx->buf, buf, sizeof(*buf)); + inode_ctx->type = SNAP_VIEW_ENTRY_POINT_INODE; + } else { + inode_ctx = svs_inode_ctx_get(this, loc->inode); + 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; + +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 (gf_uuid_is_null(loc->gfid) && gf_uuid_is_null(loc->inode->gfid)) { + gf_msg(this->name, GF_LOG_ERROR, 0, SVS_MSG_NULL_GFID, "gfid is NULL"); + goto out; + } + + if (!gf_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) { + op_ret = -1; + *op_errno = EINVAL; + gf_msg(this->name, GF_LOG_ERROR, *op_errno, + SVS_MSG_GET_LATEST_SNAP_FAILED, + "failed to get the latest " + "snapshot"); + goto out; + } + + object = glfs_h_create_from_handle(fs, handle_obj, GFAPI_HANDLE_LENGTH, + &statbuf); + if (!object) { + op_ret = -1; + *op_errno = ESTALE; + gf_msg(this->name, GF_LOG_ERROR, *op_errno, + SVS_MSG_GET_GLFS_H_OBJECT_FAILED, + "failed to do lookup and get " + "the handle on the snapshot %s (path: %s, gfid: %s)", + loc->name, loc->path, uuid_utoa(loc->gfid)); + goto out; + } + + inode_ctx = svs_inode_ctx_get_or_new(this, loc->inode); + if (!inode_ctx) { + op_ret = -1; + *op_errno = ENOMEM; + gf_msg(this->name, GF_LOG_ERROR, *op_errno, + SVS_MSG_NEW_INODE_CTX_FAILED, + "failed to allocate inode " + "context"); + goto out; + } + + iatt_from_stat(buf, &statbuf); + if (!gf_uuid_is_null(loc->gfid)) + gf_uuid_copy(buf->ia_gfid, loc->gfid); + else + gf_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, op_errno); + if (!fs) { + gf_msg_debug(this->name, 0, + "failed to create " + "the fs instance for snap %s", + loc->name); + *op_errno = ENOENT; + op_ret = -1; + 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) { + op_ret = -1; + *op_errno = errno; + /* Should this be in warning or error mode? */ + gf_msg_debug(this->name, 0, + "failed to do lookup and " + "get the handle on the snapshot %s", + loc->name); + goto out; + } + + inode_ctx = svs_inode_ctx_get_or_new(this, loc->inode); + if (!inode_ctx) { + op_ret = -1; + *op_errno = ENOMEM; + gf_msg(this->name, GF_LOG_ERROR, *op_errno, + SVS_MSG_NEW_INODE_CTX_FAILED, + "failed to allocate " + "inode context"); + goto out; + } + + if (gf_uuid_is_null(loc->gfid) && gf_uuid_is_null(loc->inode->gfid)) + gf_uuid_generate(gfid); + else { + if (!gf_uuid_is_null(loc->inode->gfid)) + gf_uuid_copy(gfid, loc->inode->gfid); + else + gf_uuid_copy(gfid, loc->gfid); + } + iatt_from_stat(buf, &statbuf); + gf_uuid_copy(buf->ia_gfid, gfid); + svs_fill_ino_from_gfid(buf); + inode_ctx->type = SNAP_VIEW_SNAPSHOT_INODE; + inode_ctx->fs = fs; + inode_ctx->object = object; + memcpy(&inode_ctx->buf, buf, sizeof(*buf)); + svs_iatt_fill(parent->gfid, postparent); + + SVS_STRDUP(inode_ctx->snapname, loc->name); + if (!inode_ctx->snapname) { + op_ret = -1; + *op_errno = ENOMEM; + goto out; + } + op_ret = 0; + +out: + if (op_ret) { + if (object) + glfs_h_close(object); + + if (inode_ctx) + inode_ctx->object = NULL; + } + + 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 = { + 0, + }; + + 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, 0); + if (!object) { + /* should this be in WARNING or ERROR mode? */ + gf_msg_debug(this->name, 0, + "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; + } + + if (gf_uuid_is_null(object->gfid)) { + /* should this be in WARNING or ERROR mode? */ + gf_msg_debug(this->name, 0, + "gfid from glfs handle is " + "NULL 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) { + op_ret = -1; + *op_errno = ENOMEM; + gf_msg(this->name, GF_LOG_ERROR, *op_errno, + SVS_MSG_NEW_INODE_CTX_FAILED, + "failed to allocate " + "inode context"); + goto out; + } + + if (gf_uuid_is_null(loc->gfid) && gf_uuid_is_null(loc->inode->gfid)) { + if (svs_uuid_generate(this, gfid, parent_ctx->snapname, object->gfid)) { + /* + * should op_errno be something else such as + * EINVAL or ESTALE? + */ + op_ret = -1; + *op_errno = EIO; + goto out; + } + } else { + if (!gf_uuid_is_null(loc->inode->gfid)) + gf_uuid_copy(gfid, loc->inode->gfid); + else + gf_uuid_copy(gfid, loc->gfid); + } + + iatt_from_stat(buf, &statbuf); + gf_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); + + if (IA_ISDIR(buf->ia_type)) { + SVS_STRDUP(inode_ctx->snapname, parent_ctx->snapname); + if (!inode_ctx->snapname) { + op_ret = -1; + *op_errno = ENOMEM; + goto out; + } + } + + op_ret = 0; + +out: + if (op_ret) { + if (object) + glfs_h_close(object); + + if (inode_ctx) + inode_ctx->object = NULL; + } + + 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; + int ret = -1; + char tmp_uuid[64] = { + 0, + }; + glfs_t *fs = NULL; + + 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 { + /* Though fs and object are present in the inode context, its + * better to check if fs is valid or not before doing anything. + * Its for the protection from the following operations. + * 1) Create a file on the glusterfs mount point + * 2) Create a snapshot (say "snap1") + * 3) Access the contents of the snapshot + * 4) Delete the file from the mount point + * 5) Delete the snapshot "snap1" + * 6) Create a new snapshot "snap1" + * + * Now accessing the new snapshot "snap1" gives problems. + * Because the inode and dentry created for snap1 would not be + * deleted upon the deletion of the snapshot (as deletion of + * snapshot is a gluster cli operation, not a fop). So next time + * upon creation of a new snap with same name, the previous + * inode and dentry itself will be used. But the inode context + * contains old information about the glfs_t instance and the + * handle in the gfapi world. Thus the glfs_t instance should + * be checked before accessing. If its wrong, then right + * instance should be obtained by doing the lookup. + */ + if (inode_ctx->fs && inode_ctx->object) { + fs = inode_ctx->fs; + SVS_CHECK_VALID_SNAPSHOT_HANDLE(fs, this); + if (fs) { + 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; + } else { + inode_ctx->fs = NULL; + inode_ctx->object = NULL; + ret = svs_get_handle(this, loc, inode_ctx, op_errno); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, *op_errno, + SVS_MSG_GET_GLFS_H_OBJECT_FAILED, + "failed to get the handle for " + "%s (gfid %s)", + loc->path, uuid_utoa_r(loc->inode->gfid, tmp_uuid)); + op_ret = -1; + 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; + gf_msg(this->name, GF_LOG_ERROR, *op_errno, + SVS_MSG_PARENT_CTX_OR_NAME_NULL, "%s is NULL", + loc->name ? "parent context" : "loc->name"); + 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); + + 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; + inode_t *parent = NULL; + gf_boolean_t entry_point_key = _gf_false; + gf_boolean_t entry_point = _gf_false; + call_stack_t *root = NULL; + + 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); + + root = frame->root; + op_ret = gf_setcredentials(&root->uid, &root->gid, root->ngrps, + root->groups); + if (op_ret != 0) { + goto out; + } + + /* For lookups sent on inodes (i.e not parent inode + basename, but + direct inode itself which usually is a nameless lookup or revalidate + on the inode), loc->name will not be there. Get it from path if + it is there. + This is the difference between nameless lookup and revalidate lookup + on an inode: + nameless lookup: loc->path contains gfid and strrchr on it fails + revalidate lookup: loc->path contains the entry name of the inode + and strrchr gives the name of the entry from path + */ + if (loc->path) { + if (!loc->name || (loc->name && !strcmp(loc->name, ""))) { + loc->name = strrchr(loc->path, '/'); + if (loc->name) + loc->name++; + } + } + + 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); + + if (xdata && !inode_ctx) { + ret = dict_get_str_boolean(xdata, "entry-point", _gf_false); + if (ret == -1) { + gf_msg_debug(this->name, 0, + "failed to get the " + "entry point info"); + entry_point_key = _gf_false; + } else { + entry_point_key = ret; + } + + if (loc->name && strlen(loc->name)) { + /* lookup can come with the entry-point set in the dict + * for the parent directory of the entry-point as well. + * So consider entry_point only for named lookup + */ + entry_point = entry_point_key; + } + } + + if (inode_ctx && inode_ctx->type == SNAP_VIEW_ENTRY_POINT_INODE) { + /* entry-point may not be set in the dictonary. + * This can happen if snap-view client is restarted where + * inode-ctx not available and a nameless lookup has come + */ + entry_point = _gf_true; + } + + /* 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) { + if (gf_uuid_is_null(loc->gfid) && gf_uuid_is_null(loc->inode->gfid)) { + op_ret = -1; + op_errno = ESTALE; + gf_msg_debug(this->name, 0, + "gfid is NULL. Either the lookup " + "came on missing entry or the " + "entry is stale"); + goto out; + } + + if (!entry_point_key) { + /* This can happen when there is no inode_ctx available. + * snapview-server might have restarted or + * graph change might have happened + */ + op_ret = -1; + op_errno = ESTALE; + goto out; + } + + /* lookup is on the parent directory of entry-point. + * this would have already looked up by snap-view client + * so return success + */ + if (!gf_uuid_is_null(loc->gfid)) + gf_uuid_copy(buf.ia_gfid, loc->gfid); + else + gf_uuid_copy(buf.ia_gfid, loc->inode->gfid); + + svs_iatt_fill(buf.ia_gfid, &buf); + svs_iatt_fill(buf.ia_gfid, &postparent); + + op_ret = 0; + 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) +{ + 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; + call_stack_t *root = 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); + + root = frame->root; + op_ret = gf_setcredentials(&root->uid, &root->gid, root->ngrps, + root->groups); + if (op_ret != 0) { + goto out; + } + + inode_ctx = svs_inode_ctx_get(this, loc->inode); + if (!inode_ctx) { + op_ret = -1; + op_errno = ESTALE; + gf_msg(this->name, GF_LOG_ERROR, op_errno, + SVS_MSG_GET_INODE_CONTEXT_FAILED, + "inode context not found " + "for the inode %s", + uuid_utoa(loc->inode->gfid)); + 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 { + SVS_GET_INODE_CTX_INFO(inode_ctx, fs, object, this, loc, op_ret, + op_errno, out); + + glfd = glfs_h_opendir(fs, object); + if (!glfd) { + op_ret = -1; + op_errno = errno; + gf_msg(this->name, GF_LOG_ERROR, op_errno, SVS_MSG_OPENDIR_FAILED, + "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) { + op_ret = -1; + op_errno = ENOMEM; + gf_msg(this->name, GF_LOG_ERROR, op_errno, + SVS_MSG_NEW_FD_CTX_FAILED, + "failed to allocate fd context " + "for %s (gfid: %s)", + loc->name, uuid_utoa(fd->inode->gfid)); + 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; +} + +/* + * This function adds the xattr keys present in the list (@list) to the dict. + * But the list contains only the names of the xattrs (and no value, as + * the gfapi functions for the listxattr operations would return only the + * names of the xattrs in the buffer provided by the caller, though they had + * got the values of those xattrs from posix) as described in the man page of + * listxattr. But before unwinding snapview-server has to put those names + * back into the dict. But to get the values for those xattrs it has to do the + * getxattr operation on each xattr which might turn out to be a costly + * operation. So for each of the xattrs present in the list, a 0 byte value + * ("") is set into the dict before unwinding. Since ("") is also a valid xattr + * value(in a file system) we use an extra key in the same dictionary as an + * indicator to other xlators which want to cache the xattrs (as of now, + * md-cache which caches acl and selinux related xattrs) to not to cache the + * values of the xattrs present in the dict. + */ +int32_t +svs_add_xattrs_to_dict(xlator_t *this, dict_t *dict, char *list, ssize_t size) +{ + char keybuffer[4096] = { + 0, + }; + size_t remaining_size = 0; + int32_t list_offset = 0; + int32_t ret = -1; + + GF_VALIDATE_OR_GOTO("snapview-daemon", this, out); + GF_VALIDATE_OR_GOTO(this->name, dict, out); + GF_VALIDATE_OR_GOTO(this->name, list, out); + + remaining_size = size; + list_offset = 0; + while (remaining_size > 0) { + strncpy(keybuffer, list + list_offset, sizeof(keybuffer) - 1); +#ifdef GF_DARWIN_HOST_OS + /* The protocol expect namespace for now */ + char *newkey = NULL; + gf_add_prefix(XATTR_USER_PREFIX, keybuffer, &newkey); + strcpy(keybuffer, newkey); + GF_FREE(newkey); +#endif + ret = dict_set_str(dict, keybuffer, ""); + if (ret < 0) { + gf_msg(this->name, GF_LOG_ERROR, 0, SVS_MSG_DICT_SET_FAILED, + "dict set operation " + "for the key %s failed.", + keybuffer); + goto out; + } + + remaining_size -= strlen(keybuffer) + 1; + list_offset += strlen(keybuffer) + 1; + } /* while (remaining_size > 0) */ + + /* Add an additional key to indicate that we don't need to cache these + * xattrs(with value "") */ + ret = dict_set_str(dict, "glusterfs.skip-cache", ""); + if (ret < 0) { + gf_msg(this->name, GF_LOG_ERROR, 0, SVS_MSG_DICT_SET_FAILED, + "dict set operation for the key glusterfs.skip-cache failed."); + goto out; + } + + ret = 0; + +out: + return ret; +} + +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; + call_stack_t *root = 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", loc, out); + GF_VALIDATE_OR_GOTO("snap-view-daemon", loc->inode, out); + + root = frame->root; + op_ret = gf_setcredentials(&root->uid, &root->gid, root->ngrps, + root->groups); + if (op_ret != 0) { + goto out; + } + + inode_ctx = svs_inode_ctx_get(this, loc->inode); + if (!inode_ctx) { + op_ret = -1; + op_errno = ESTALE; + gf_msg(this->name, GF_LOG_ERROR, op_errno, + SVS_MSG_GET_INODE_CONTEXT_FAILED, + "inode context not found " + "for the inode %s", + uuid_utoa(loc->inode->gfid)); + goto out; + } + + /* ENODATA is sent if the getxattr is on entry point directory + or the inode is SNAP_VIEW_ENTRY_POINT_INODE. Entry point is + a virtual directory on which setxattr operations are not + allowed. If getxattr has to be faked as success, then a value + for the name of the xattr has to be sent which we don't have. + */ + if (inode_ctx->type == SNAP_VIEW_ENTRY_POINT_INODE) { + op_ret = -1; + op_errno = ENODATA; + goto out; + } else { + SVS_GET_INODE_CTX_INFO(inode_ctx, fs, object, this, loc, op_ret, + op_errno, out); + + dict = dict_new(); + if (!dict) { + op_ret = -1; + op_errno = ENOMEM; + gf_msg(this->name, GF_LOG_ERROR, op_errno, SVS_MSG_NO_MEMORY, + "failed to allocate dict"); + goto out; + } + + size = glfs_h_getxattrs(fs, object, name, NULL, 0); + if (size == -1) { + op_ret = -1; + op_errno = errno; + if (errno == ENODATA) { + gf_msg_debug(this->name, 0, + "getxattr on " + "%s failed (ket: %s) with %s", + loc->path, name, strerror(errno)); + } else { + gf_msg(this->name, GF_LOG_ERROR, op_errno, + SVS_MSG_GETXATTR_FAILED, + "getxattr on %s failed (key: %s) with %s", loc->path, + name, strerror(errno)); + } + goto out; + } + value = GF_CALLOC(size + 1, sizeof(char), gf_common_mt_char); + if (!value) { + op_ret = -1; + op_errno = ENOMEM; + gf_msg(this->name, GF_LOG_ERROR, op_errno, SVS_MSG_NO_MEMORY, + "failed to allocate memory for getxattr " + "on %s (key: %s)", + loc->name, name); + goto out; + } + + size = glfs_h_getxattrs(fs, object, name, value, size); + if (size == -1) { + op_ret = -1; + op_errno = errno; + gf_msg(this->name, GF_LOG_ERROR, op_errno, SVS_MSG_GETXATTR_FAILED, + "failed to get the xattr %s for " + "entry %s", + name, loc->name); + goto out; + } + value[size] = '\0'; + + if (name) { + op_ret = dict_set_dynptr(dict, (char *)name, value, size); + if (op_ret < 0) { + op_errno = -op_ret; + gf_msg(this->name, GF_LOG_ERROR, op_errno, + SVS_MSG_DICT_SET_FAILED, + "dict set operation for %s for " + "the key %s failed.", + loc->path, name); + GF_FREE(value); + value = NULL; + goto out; + } + } else { + op_ret = svs_add_xattrs_to_dict(this, dict, value, size); + if (op_ret == -1) { + op_errno = ENOMEM; + gf_msg(this->name, GF_LOG_ERROR, op_errno, SVS_MSG_NO_MEMORY, + "failed to add xattrs from the list to " + "dict for %s (gfid: %s)", + loc->path, uuid_utoa(loc->inode->gfid)); + goto out; + } + GF_FREE(value); + value = NULL; + } + } + +out: + if (op_ret && value) + GF_FREE(value); + + STACK_UNWIND_STRICT(getxattr, frame, op_ret, op_errno, dict, NULL); + + if (dict) + dict_unref(dict); + + 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) { + op_ret = -1; + op_errno = ESTALE; + gf_msg(this->name, GF_LOG_ERROR, op_errno, + SVS_MSG_GET_INODE_CONTEXT_FAILED, + "inode context not found " + "for the inode %s", + uuid_utoa(fd->inode->gfid)); + goto out; + } + + if (!(svs_inode_ctx_glfs_mapping(this, inode_ctx))) { + op_ret = -1; + op_errno = EBADF; + gf_msg(this->name, GF_LOG_ERROR, op_errno, SVS_MSG_FS_INSTANCE_INVALID, + "glfs instance %p to which the inode %s " + "belongs to does not exist. The snapshot " + "corresponding to the instance might have" + "been deleted or deactivated", + inode_ctx->fs, uuid_utoa(fd->inode->gfid)); + goto out; + } + + sfd = svs_fd_ctx_get_or_new(this, fd); + if (!sfd) { + op_ret = -1; + op_errno = EBADFD; + gf_msg(this->name, GF_LOG_ERROR, op_errno, + SVS_MSG_GET_FD_CONTEXT_FAILED, + "failed to get the fd " + "context for %s", + uuid_utoa(fd->inode->gfid)); + goto out; + } + + glfd = sfd->fd; + /* EINVAL is sent if the getxattr is on entry point directory + or the inode is SNAP_VIEW_ENTRY_POINT_INODE. Entry point is + a virtual directory on which setxattr operations are not + allowed. If getxattr has to be faked as success, then a value + for the name of the xattr has to be sent which we don't have. + */ + if (inode_ctx->type == SNAP_VIEW_ENTRY_POINT_INODE) { + op_ret = -1; + op_errno = EINVAL; + goto out; + } else { + dict = dict_new(); + if (!dict) { + op_ret = -1; + op_errno = ENOMEM; + gf_msg(this->name, GF_LOG_ERROR, op_errno, SVS_MSG_NO_MEMORY, + "failed to allocate dict " + "(gfid: %s, key: %s)", + uuid_utoa(fd->inode->gfid), name); + goto out; + } + + if (name) { + size = glfs_fgetxattr(glfd, name, NULL, 0); + if (size == -1) { + op_ret = -1; + op_errno = errno; + gf_msg(this->name, GF_LOG_ERROR, op_errno, + SVS_MSG_GETXATTR_FAILED, + "getxattr on %s failed " + "(key: %s)", + uuid_utoa(fd->inode->gfid), name); + goto out; + } + value = GF_CALLOC(size + 1, sizeof(char), gf_common_mt_char); + if (!value) { + op_ret = -1; + op_errno = ENOMEM; + gf_msg(this->name, GF_LOG_ERROR, op_errno, SVS_MSG_NO_MEMORY, + "failed to " + "allocate memory for getxattr on %s " + "(key: %s)", + uuid_utoa(fd->inode->gfid), name); + goto out; + } + + size = glfs_fgetxattr(glfd, name, value, size); + if (size == -1) { + op_ret = -1; + op_errno = errno; + gf_msg(this->name, GF_LOG_ERROR, op_errno, + SVS_MSG_GETXATTR_FAILED, + "failed to get the xattr %s " + "for inode %s", + name, uuid_utoa(fd->inode->gfid)); + goto out; + } + value[size] = '\0'; + + op_ret = dict_set_dynptr(dict, (char *)name, value, size); + if (op_ret < 0) { + op_errno = -op_ret; + gf_msg(this->name, GF_LOG_ERROR, op_errno, + SVS_MSG_DICT_SET_FAILED, + "dict set operation for gfid %s " + "for the key %s failed.", + uuid_utoa(fd->inode->gfid), name); + goto out; + } + } else { + size = glfs_flistxattr(glfd, NULL, 0); + if (size == -1) { + op_errno = errno; + gf_msg(this->name, GF_LOG_ERROR, op_errno, + SVS_MSG_LISTXATTR_FAILED, "listxattr on %s failed", + uuid_utoa(fd->inode->gfid)); + goto out; + } + + value = GF_CALLOC(size + 1, sizeof(char), gf_common_mt_char); + if (!value) { + op_ret = -1; + op_errno = ENOMEM; + gf_msg(this->name, GF_LOG_ERROR, op_errno, SVS_MSG_NO_MEMORY, + "failed to " + "allocate buffer for xattr " + "list (%s)", + uuid_utoa(fd->inode->gfid)); + goto out; + } + + size = glfs_flistxattr(glfd, value, size); + if (size == -1) { + op_ret = -1; + op_errno = errno; + gf_msg(this->name, GF_LOG_ERROR, op_errno, + SVS_MSG_LISTXATTR_FAILED, "listxattr on %s failed", + uuid_utoa(fd->inode->gfid)); + goto out; + } + + op_ret = svs_add_xattrs_to_dict(this, dict, value, size); + if (op_ret == -1) { + op_errno = ENOMEM; + gf_msg(this->name, GF_LOG_ERROR, op_errno, SVS_MSG_NO_MEMORY, + "failed to add xattrs from the list " + "to dict (gfid: %s)", + uuid_utoa(fd->inode->gfid)); + goto out; + } + GF_FREE(value); + } + + op_ret = 0; + op_errno = 0; + } + +out: + if (op_ret) + GF_FREE(value); + + STACK_UNWIND_STRICT(fgetxattr, frame, op_ret, op_errno, dict, NULL); + + if (dict) + dict_unref(dict); + + 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; + svs_inode_t *svs_inode = NULL; + glfs_t *fs = NULL; + inode_t *inode = NULL; + + 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_msg_debug(this->name, 0, "pfd from fd=%p is NULL", fd); + goto out; + } + + inode = fd->inode; + + svs_inode = svs_inode_ctx_get(this, inode); + if (svs_inode) { + fs = svs_inode->fs; /* should inode->lock be held for this? */ + SVS_CHECK_VALID_SNAPSHOT_HANDLE(fs, this); + if (fs) { + sfd = (svs_fd_t *)(long)tmp_pfd; + if (sfd->fd) { + ret = glfs_closedir(sfd->fd); + if (ret) + gf_msg(this->name, GF_LOG_WARNING, errno, + SVS_MSG_RELEASEDIR_FAILED, + "failed to close the glfd for " + "directory %s", + uuid_utoa(fd->inode->gfid)); + } + } + } + + GF_FREE(sfd); + +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; + svs_inode_t *inode_ctx = NULL; + call_stack_t *root = NULL; + + GF_VALIDATE_OR_GOTO("snapview-server", this, out); + GF_VALIDATE_OR_GOTO(this->name, frame, out); + GF_VALIDATE_OR_GOTO(this->name, fd, out); + + root = frame->root; + op_ret = gf_setcredentials(&root->uid, &root->gid, root->ngrps, + root->groups); + if (op_ret != 0) { + goto out; + } + + inode_ctx = svs_inode_ctx_get(this, fd->inode); + if (!inode_ctx) { + op_ret = -1; + op_errno = EINVAL; + gf_msg(this->name, GF_LOG_ERROR, op_errno, + SVS_MSG_GET_INODE_CONTEXT_FAILED, + "inode context not found for" + " the inode %s", + uuid_utoa(fd->inode->gfid)); + goto out; + } + + ret = fd_ctx_get(fd, this, &value); + if (ret < 0 && inode_ctx->type != SNAP_VIEW_ENTRY_POINT_INODE) { + op_errno = EINVAL; + gf_msg(this->name, GF_LOG_WARNING, op_errno, + SVS_MSG_GET_FD_CONTEXT_FAILED, "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; + inode_t *inode = NULL; + svs_inode_t *svs_inode = NULL; + glfs_t *fs = NULL; + + 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_msg_debug(this->name, 0, "pfd from fd=%p is NULL", fd); + goto out; + } + + inode = fd->inode; + + svs_inode = svs_inode_ctx_get(this, inode); + if (svs_inode) { + fs = svs_inode->fs; /* should inode->lock be held for this? */ + SVS_CHECK_VALID_SNAPSHOT_HANDLE(fs, this); + if (fs) { + sfd = (svs_fd_t *)(long)tmp_pfd; + if (sfd->fd) { + ret = glfs_close(sfd->fd); + if (ret) + gf_msg(this->name, GF_LOG_ERROR, errno, + SVS_MSG_RELEASE_FAILED, + "failed to close " + "the glfd for %s", + uuid_utoa(fd->inode->gfid)); + } + } + } + + GF_FREE(sfd); +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_msg(this->name, GF_LOG_ERROR, 0, SVS_MSG_DELETE_INODE_CONTEXT_FAILED, + "failed to delete the inode " + "context of %s", + uuid_utoa(inode->gfid)); + goto out; + } + + inode_ctx = (svs_inode_t *)(uintptr_t)value; + if (!inode_ctx) + goto out; + + if (inode_ctx->snapname) + GF_FREE(inode_ctx->snapname); + + /* + * glfs_h_close leads to unref and forgetting of the + * underlying inode in the gfapi world. i.e. the inode + * which inode_ctx->object points to. + * As of now the only possibility is, this forget came as a + * result of snapdaemon's inode table reaching the lru + * limit and receiving forget as a result of purging of + * extra inodes that exceeded the limit. But, care must + * be taken to ensure that, the gfapi instance to which + * the glfs_h_object belongs to is not deleted. Otherwise + * this might result in access of a freed pointer. + * This will still be helpful in reducing the memory + * footprint of snapdaemon when the fs instance itself is + * valid (i.e. present and not destroyed due to either snap + * deactivate or snap delete), but the lru limit is reached. + * The forget due to lru limit will make the underlying inode + * being unrefed and forgotten. + */ + if (svs_inode_ctx_glfs_mapping(this, inode_ctx)) { + glfs_h_close(inode_ctx->object); + inode_ctx->object = NULL; + } + GF_FREE(inode_ctx); + +out: + return 0; +} + +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; + GF_ASSERT(priv); + + /* create the dir entries */ + LOCK(&priv->snaplist_lock); + { + 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 unlock; + + entry = gf_dirent_for_name(dirents[i].name); + if (!entry) { + gf_msg(this->name, GF_LOG_ERROR, ENOMEM, SVS_MSG_NO_MEMORY, + "failed to allocate dentry for %s", dirents[i].name); + goto unlock; + } + + entry->d_off = i + 1; + /* + * readdir on the entry-point directory to the snapshot + * world, will return elements in the list of the + * snapshots as the directory entries. Since the entries + * returned are virtual entries which does not exist + * physically on the disk, pseudo inode numbers are + * generated. + */ + entry->d_ino = i + 2 * 42; + entry->d_type = DT_DIR; + list_add_tail(&entry->list, &entries->list); + ++i; + count++; + filled_size += this_size; + } + } +unlock: + UNLOCK(&priv->snaplist_lock); + +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); + + while (filled_size < size) { + in_case = glfs_telldir(glfd); + if (in_case == -1) { + gf_msg(this->name, GF_LOG_ERROR, errno, SVS_MSG_TELLDIR_FAILED, + "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) { + /* + * Since gf_dirent_for_name can return + * NULL only when it fails to allocate + * memory for the directory entry, + * SVS_MSG_NO_MEMORY is used as the + * message-id. + */ + gf_msg(this->name, GF_LOG_ERROR, errno, SVS_MSG_NO_MEMORY, + "could not create gf_dirent " + "for entry %s: (%s)", + entry->d_name, strerror(errno)); + break; + } + entry->d_off = glfs_telldir(glfd); + entry->d_ino = de.d_ino; + entry->d_type = de.d_type; + if (readdirplus) { + iatt_from_stat(buf, &statbuf); + 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; + } + +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); + + if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) + goto 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) { + gf_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; + gf_uuid_copy(entry->d_stat.ia_gfid, buf.ia_gfid); + } + } else { + if (parent_ctx->type == SNAP_VIEW_ENTRY_POINT_INODE) { + inode = inode_new(parent->table); + entry->inode = inode; + + /* 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_msg(this->name, GF_LOG_ERROR, ENOMEM, SVS_MSG_NO_MEMORY, + "failed to allocate inode " + "context for %s", + entry->d_name); + inode_unref(entry->inode); + entry->inode = NULL; + goto out; + } + + /* Generate virtual gfid for SNAPSHOT dir and + * update the statbuf + */ + gf_uuid_generate(random_gfid); + gf_uuid_copy(buf.ia_gfid, random_gfid); + svs_fill_ino_from_gfid(&buf); + buf.ia_type = IA_IFDIR; + entry->d_ino = buf.ia_ino; + entry->d_stat = buf; + inode_ctx->buf = buf; + inode_ctx->type = SNAP_VIEW_SNAPSHOT_INODE; + } else { + /* For files under snapshot world do not set + * entry->inode and reset statbuf (except ia_ino), + * so that FUSE/Kernel will send an explicit lookup. + * entry->d_stat contains the statbuf information + * of original file, so for NFS not to cache this + * information and to send explicit lookup, it is + * required to reset the statbuf. + * Virtual gfid for these files will be generated in the + * first lookup. + */ + buf.ia_ino = entry->d_ino; + entry->d_stat = buf; + } + } + +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 *parent_ctx = NULL; + svs_fd_t *svs_fd = NULL; + call_stack_t *root = 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); + + root = frame->root; + op_ret = gf_setcredentials(&root->uid, &root->gid, root->ngrps, + root->groups); + if (op_ret != 0) { + goto unwind; + } + + parent_ctx = svs_inode_ctx_get(this, fd->inode); + if (!parent_ctx) { + op_ret = -1; + op_errno = EINVAL; + gf_msg(this->name, GF_LOG_ERROR, op_errno, + SVS_MSG_GET_INODE_CONTEXT_FAILED, + "failed to get the inode " + "context for %s", + uuid_utoa(fd->inode->gfid)); + 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) { + op_ret = -1; + op_errno = EBADFD; + gf_msg(this->name, GF_LOG_ERROR, op_errno, + SVS_MSG_GET_FD_CONTEXT_FAILED, + "failed to get the fd context " + "for the inode %s", + uuid_utoa(fd->inode->gfid)); + 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) +{ + gf_dirent_t entries = { + { + { + 0, + }, + }, + }; + 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; + + INIT_LIST_HEAD(&entries.list); + + 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); + + inode_ctx = svs_inode_ctx_get(this, fd->inode); + if (!inode_ctx) { + op_ret = -1; + op_errno = EINVAL; + gf_msg(this->name, GF_LOG_ERROR, op_errno, + SVS_MSG_GET_INODE_CONTEXT_FAILED, + "inode context not found in " + "the inode %s", + uuid_utoa(fd->inode->gfid)); + 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) { + op_ret = -1; + op_errno = EBADFD; + gf_msg(this->name, GF_LOG_ERROR, op_errno, + SVS_MSG_GET_FD_CONTEXT_FAILED, + "failed to get the fd " + "context for %s", + uuid_utoa(fd->inode->gfid)); + 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; +} + +/* + * This function is mainly helpful for NFS. Till now NFS server was not linking + * the inodes in readdirp, which caused problems when below operations were + * performed. + * + * 1) ls -l in one of the snaopshots (snapview-server would generate gfids for + * each entry on the fly and link the inodes associated with those entries) + * 2) NFS server upon getting readdirp reply would not link the inodes of the + * entries. But it used to generate filehandles for each entry and associate + * the gfid of that entry with the filehandle and send it as part of the + * reply to nfs client. + * 3) NFS client would send the filehandle of one of those entries when some + * activity is done on it. + * 4) NFS server would not be able to find the inode for the gfid present in the + * filehandle (as the inode was not linked) and would go for hard resolution + * by sending a lookup on the gfid by creating a new inode. + * 5) snapview-client will not able to identify whether the inode is a real + * inode existing in the main volume or a virtual inode existing in the + * snapshots as there would not be any inode context. + * 6) Since the gfid upon which lookup is sent is a virtual gfid which is not + * present in the disk, lookup would fail and the application would get an + * error. + * + * The above problem is fixed by the below commit which makes snapview server + * more compatible with nfs server (1dea949cb60c3814c9206df6ba8dddec8d471a94). + * But now because NFS server does inode linking in readdirp has introduced + * the below issue. + * In readdirp though snapview-server allocates inode contexts it does not + * actually perform lookup on each entry it obtained in readdirp (as doing + * a lookup via gfapi over the network for each entry would be costly). + * + * Till now it was not a problem with NFS server, as NFS was sending a lookup on + * the gfid it got from NFS client, for which it was not able to find the right + * inode. So snapview-server was able to get the fs instance (glfs_t) of the + * snapshot volume to which the entry belongs to, and the handle for the entry + * from the corresponding snapshot volume and fill those information in the + * inode context. + * + * But now, since NFS server is able to find the inode from the inode table for + * the gfid it got from the NFS client, it won't send lookup. Rather it directly + * sends the fop it received from the client. Now this causes problems for + * snapview-server. Because for each fop snapview-server assumes that lookup has + * been performed on that entry and the entry's inode context contains the + * pointers for the fs instance and the handle to the entry in that fs. When NFS + * server sends the fop and snapview-server finds that the fs instance and the + * handle within the inode context are NULL it unwinds with EINVAL. + * + * So to handle this, if fs instance or handle within the inode context are + * NULL, then do a lookup based on parent inode context's fs instance. And + * unwind the results obtained as part of lookup + */ + +int32_t +svs_get_handle(xlator_t *this, loc_t *loc, svs_inode_t *inode_ctx, + int32_t *op_errno) +{ + svs_inode_t *parent_ctx = NULL; + int ret = -1; + inode_t *parent = NULL; + struct iatt postparent = { + 0, + }; + struct iatt buf = { + 0, + }; + char uuid1[64]; + + GF_VALIDATE_OR_GOTO("snap-view-daemon", this, out); + GF_VALIDATE_OR_GOTO(this->name, loc, out); + GF_VALIDATE_OR_GOTO(this->name, loc->inode, out); + + if (loc->path) { + if (!loc->name || (loc->name && !strcmp(loc->name, ""))) { + loc->name = strrchr(loc->path, '/'); + if (loc->name) + loc->name++; + } + } + + 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); + + if (!parent_ctx) { + *op_errno = EINVAL; + gf_msg(this->name, GF_LOG_WARNING, *op_errno, + SVS_MSG_GET_INODE_CONTEXT_FAILED, + "failed to get the parent " + "context for %s (%s)", + loc->path, uuid_utoa_r(loc->inode->gfid, uuid1)); + goto out; + } + + if (parent_ctx) { + if (parent_ctx->type == SNAP_VIEW_ENTRY_POINT_INODE) + ret = svs_lookup_snapshot(this, loc, &buf, &postparent, parent, + parent_ctx, op_errno); + else + ret = svs_lookup_entry(this, loc, &buf, &postparent, parent, + parent_ctx, op_errno); + } + +out: + if (parent) + inode_unref(parent); + + return ret; +} + +int32_t +svs_stat(call_frame_t *frame, xlator_t *this, loc_t *loc, dict_t *xdata) +{ + 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; + call_stack_t *root = NULL; + + 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); + + root = frame->root; + op_ret = gf_setcredentials(&root->uid, &root->gid, root->ngrps, + root->groups); + if (op_ret != 0) { + goto out; + } + + /* 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) { + op_ret = -1; + op_errno = EINVAL; + gf_msg(this->name, GF_LOG_ERROR, op_errno, + SVS_MSG_GET_INODE_CONTEXT_FAILED, + "inode context not found for %s", uuid_utoa(loc->inode->gfid)); + goto out; + } + + if (inode_ctx->type == SNAP_VIEW_ENTRY_POINT_INODE) { + svs_iatt_fill(loc->inode->gfid, &buf); + op_ret = 0; + } else { + SVS_GET_INODE_CTX_INFO(inode_ctx, fs, object, this, loc, op_ret, + op_errno, out); + + ret = glfs_h_stat(fs, object, &stat); + if (ret) { + op_ret = -1; + op_errno = errno; + gf_msg(this->name, GF_LOG_ERROR, op_errno, SVS_MSG_STAT_FAILED, + "glfs_h_stat on %s (gfid: %s) " + "failed", + loc->name, uuid_utoa(loc->inode->gfid)); + goto out; + } else + gf_msg_debug(this->name, 0, "stat on %s (%s) successful", loc->path, + uuid_utoa(loc->inode->gfid)); + + iatt_from_stat(&buf, &stat); + gf_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) +{ + 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; + call_stack_t *root = 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); + + /* 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. + */ + + root = frame->root; + op_ret = gf_setcredentials(&root->uid, &root->gid, root->ngrps, + root->groups); + if (op_ret != 0) { + goto out; + } + + inode_ctx = svs_inode_ctx_get(this, fd->inode); + if (!inode_ctx) { + op_ret = -1; + op_errno = EINVAL; + gf_msg(this->name, GF_LOG_ERROR, op_errno, + SVS_MSG_GET_INODE_CONTEXT_FAILED, + "inode context not found for" + " the inode %s", + uuid_utoa(fd->inode->gfid)); + goto out; + } + + if (inode_ctx->type == SNAP_VIEW_ENTRY_POINT_INODE) { + svs_iatt_fill(fd->inode->gfid, &buf); + op_ret = 0; + } else { + if (!(svs_inode_ctx_glfs_mapping(this, inode_ctx))) { + op_ret = -1; + op_errno = EBADF; + gf_msg(this->name, GF_LOG_ERROR, op_errno, + SVS_MSG_FS_INSTANCE_INVALID, + "glfs instance %p to which the inode %s " + "belongs to does not exist. That snapshot " + "corresponding to the fs instance " + "might have been deleted or deactivated.", + inode_ctx->fs, uuid_utoa(fd->inode->gfid)); + goto out; + } + + sfd = svs_fd_ctx_get_or_new(this, fd); + if (!sfd) { + op_ret = -1; + op_errno = EBADFD; + gf_msg(this->name, GF_LOG_ERROR, op_errno, + SVS_MSG_GET_FD_CONTEXT_FAILED, + "failed to get the fd context " + "for %s", + uuid_utoa(fd->inode->gfid)); + goto out; + } + + glfd = sfd->fd; + ret = glfs_fstat(glfd, &stat); + if (ret) { + op_ret = -1; + op_errno = errno; + gf_msg(this->name, GF_LOG_ERROR, op_errno, SVS_MSG_STAT_FAILED, + "glfs_fstat on gfid: %s failed", uuid_utoa(fd->inode->gfid)); + goto out; + } + + iatt_from_stat(&buf, &stat); + gf_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_statfs(call_frame_t *frame, xlator_t *this, loc_t *loc, dict_t *xdata) +{ + struct statvfs 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; + int ret = -1; + call_stack_t *root = NULL; + + 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); + + root = frame->root; + op_ret = gf_setcredentials(&root->uid, &root->gid, root->ngrps, + root->groups); + if (op_ret != 0) { + goto out; + } + + /* 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) { + op_ret = -1; + op_errno = EINVAL; + gf_msg(this->name, GF_LOG_ERROR, op_errno, + SVS_MSG_GET_INODE_CONTEXT_FAILED, + "inode context not found for %s", uuid_utoa(loc->inode->gfid)); + goto out; + } + + SVS_GET_INODE_CTX_INFO(inode_ctx, fs, object, this, loc, op_ret, op_errno, + out); + + ret = glfs_h_statfs(fs, object, &buf); + if (ret) { + op_ret = -1; + op_errno = errno; + gf_msg(this->name, GF_LOG_ERROR, op_errno, SVS_MSG_STATFS_FAILED, + "glfs_h_statvfs on %s (gfid: %s) " + "failed", + loc->name, uuid_utoa(loc->inode->gfid)); + goto out; + } + op_ret = ret; + +out: + STACK_UNWIND_STRICT(statfs, 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) +{ + 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; + call_stack_t *root = 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); + + root = frame->root; + + inode_ctx = svs_inode_ctx_get(this, loc->inode); + if (!inode_ctx) { + gf_msg(this->name, GF_LOG_ERROR, op_errno, + SVS_MSG_GET_INODE_CONTEXT_FAILED, + "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 + + SVS_GET_INODE_CTX_INFO(inode_ctx, fs, object, this, loc, op_ret, op_errno, + out); + + op_ret = gf_setcredentials(&root->uid, &root->gid, root->ngrps, + root->groups); + if (op_ret != 0) { + goto out; + } + + glfd = glfs_h_open(fs, object, flags); + if (!glfd) { + op_ret = -1; + op_errno = errno; + gf_msg(this->name, GF_LOG_ERROR, op_errno, SVS_MSG_OPEN_FAILED, + "glfs_h_open on %s failed (gfid: %s)", loc->name, + uuid_utoa(loc->inode->gfid)); + goto out; + } + + sfd = svs_fd_ctx_get_or_new(this, fd); + if (!sfd) { + op_ret = -1; + op_errno = ENOMEM; + gf_msg(this->name, GF_LOG_ERROR, op_errno, SVS_MSG_NO_MEMORY, + "failed to allocate fd context " + "for %s (gfid: %s)", + loc->name, uuid_utoa(loc->inode->gfid)); + 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 glfs_stat fstatbuf = { + 0, + }; + glfs_fd_t *glfd = NULL; + struct iatt stbuf = { + 0, + }; + call_stack_t *root = 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; + VALIDATE_OR_GOTO(priv, out); + + root = frame->root; + op_ret = gf_setcredentials(&root->uid, &root->gid, root->ngrps, + root->groups); + if (op_ret != 0) { + goto out; + } + + if (!svs_inode_glfs_mapping(this, fd->inode)) { + op_ret = -1; + op_errno = EBADF; /* should this be some other error? */ + gf_msg(this->name, GF_LOG_ERROR, op_errno, SVS_MSG_FS_INSTANCE_INVALID, + "glfs instance to which the inode " + "%s receiving read request belongs, " + "does not exist anymore", + uuid_utoa(fd->inode->gfid)); + goto out; + } + + sfd = svs_fd_ctx_get_or_new(this, fd); + if (!sfd) { + op_ret = -1; + op_errno = EBADFD; + gf_msg(this->name, GF_LOG_ERROR, op_errno, + SVS_MSG_GET_INODE_CONTEXT_FAILED, + "failed to get the fd " + "context for %s", + uuid_utoa(fd->inode->gfid)); + goto out; + } + + glfd = sfd->fd; + + iobuf = iobuf_get2(this->ctx->iobuf_pool, size); + if (!iobuf) { + op_ret = -1; + op_errno = ENOMEM; + gf_msg(this->name, GF_LOG_ERROR, op_errno, SVS_MSG_NO_MEMORY, + "failed to " + "allocate iobuf while reading the " + "file with gfid %s", + uuid_utoa(fd->inode->gfid)); + goto out; + } + + ret = glfs_pread(glfd, iobuf->ptr, size, offset, 0, &fstatbuf); + if (ret < 0) { + op_ret = -1; + op_errno = errno; + gf_msg(this->name, GF_LOG_ERROR, op_errno, SVS_MSG_READ_FAILED, + "glfs_read failed on %s (%s)", uuid_utoa(fd->inode->gfid), + strerror(op_errno)); + goto out; + } + + vec.iov_base = iobuf->ptr; + vec.iov_len = ret; + + iobref = iobref_new(); + + iobref_add(iobref, iobuf); + glfs_iatt_from_statx(&stbuf, &fstatbuf); + gf_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, + }; + call_stack_t *root = NULL; + + 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); + + root = frame->root; + op_ret = gf_setcredentials(&root->uid, &root->gid, root->ngrps, + root->groups); + if (op_ret != 0) { + goto out; + } + + inode_ctx = svs_inode_ctx_get(this, loc->inode); + if (!inode_ctx) { + op_ret = -1; + op_errno = EINVAL; + gf_msg(this->name, GF_LOG_ERROR, op_errno, + SVS_MSG_GET_INODE_CONTEXT_FAILED, + "failed to get inode context " + "for %s (gfid: %s)", + loc->name, uuid_utoa(loc->inode->gfid)); + goto out; + } + + SVS_GET_INODE_CTX_INFO(inode_ctx, fs, object, this, loc, op_ret, op_errno, + out); + + ret = glfs_h_stat(fs, object, &stat); + if (ret) { + op_ret = -1; + op_errno = errno; + gf_msg(this->name, GF_LOG_ERROR, op_errno, SVS_MSG_STAT_FAILED, + "glfs_h_stat on %s (gfid: %s) " + "failed", + loc->name, uuid_utoa(loc->inode->gfid)); + goto out; + } + + iatt_from_stat(&stbuf, &stat); + gf_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) { + op_errno = errno; + gf_msg(this->name, GF_LOG_ERROR, op_errno, SVS_MSG_READLINK_FAILED, + "readlink on %s failed (gfid: %s)", loc->name, + uuid_utoa(loc->inode->gfid)); + 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; + glfs_t *fs = NULL; + glfs_object_t *object = NULL; + svs_inode_t *inode_ctx = NULL; + gf_boolean_t is_fuse_call = 0; + int mode = 0; + call_stack_t *root = NULL; + + 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); + + root = frame->root; + op_ret = gf_setcredentials(&root->uid, &root->gid, root->ngrps, + root->groups); + if (op_ret != 0) { + goto out; + } + + inode_ctx = svs_inode_ctx_get(this, loc->inode); + if (!inode_ctx) { + op_ret = -1; + op_errno = EINVAL; + gf_msg(this->name, GF_LOG_ERROR, op_errno, + SVS_MSG_GET_INODE_CONTEXT_FAILED, + "inode context not found for %s", uuid_utoa(loc->inode->gfid)); + goto out; + } + + is_fuse_call = __is_fuse_call(frame); + + /* + * For entry-point directory, set read and execute bits. But not write + * permissions. + */ + 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; + } + + SVS_GET_INODE_CTX_INFO(inode_ctx, fs, object, this, loc, op_ret, op_errno, + out); + + /* The actual posix_acl xlator does acl checks differently for + fuse and nfs. So set frame->root->pid as fspid of the syncop + if the call came from nfs + */ + if (!is_fuse_call) { + syncopctx_setfspid(&frame->root->pid); + syncopctx_setfsuid(&frame->root->uid); + syncopctx_setfsgid(&frame->root->gid); + syncopctx_setfsgroups(frame->root->ngrps, frame->root->groups); + } + + ret = glfs_h_access(fs, object, mask); + if (ret < 0) { + op_ret = -1; + op_errno = errno; + gf_msg(this->name, GF_LOG_ERROR, op_errno, SVS_MSG_ACCESS_FAILED, + "failed to access %s (gfid: %s)", loc->path, + uuid_utoa(loc->inode->gfid)); + goto out; + } + + op_ret = 0; + op_errno = ret; + +out: + + STACK_UNWIND_STRICT(access, frame, op_ret, op_errno, NULL); + return 0; +} + +int32_t +notify(xlator_t *this, int32_t event, void *data, ...) +{ + switch (event) { + case GF_EVENT_PARENT_UP: { + /* Tell the parent that snapview-server xlator is up */ + default_notify(this, GF_EVENT_CHILD_UP, data); + } break; + default: + break; + } + 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_msg(this->name, GF_LOG_WARNING, 0, SVS_MSG_MEM_ACNT_FAILED, + "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_msg_debug(this->name, 0, "dangling volume. check volfile "); + } + + priv = GF_CALLOC(1, sizeof(*priv), gf_svs_mt_priv_t); + if (!priv) { + gf_msg(this->name, GF_LOG_ERROR, ENOMEM, SVS_MSG_NO_MEMORY, + "failed to " + "allocate memory for this->private "); + goto out; + } + + this->private = priv; + + GF_OPTION_INIT("volname", priv->volname, str, out); + LOCK_INIT(&priv->snaplist_lock); + + LOCK(&priv->snaplist_lock); + { + priv->num_snaps = 0; + } + UNLOCK(&priv->snaplist_lock); + + /* What to do here upon failure? should init be failed or succeed? */ + /* If succeeded, then dynamic management of snapshots will not */ + /* happen.*/ + ret = svs_mgmt_init(this); + if (ret) { + gf_msg(this->name, GF_LOG_WARNING, EINVAL, SVS_MSG_MGMT_INIT_FAILED, + "failed to initiate the " + "mgmt rpc callback for svs. Dymamic management of the" + "snapshots will not happen"); + goto out; + } + + /* get the list of snaps first to return to client xlator */ + ret = svs_get_snapshot_list(this); + if (ret) { + gf_msg(this->name, GF_LOG_ERROR, EINVAL, + SVS_MSG_GET_SNAPSHOT_LIST_FAILED, + "Error initializing snaplist infrastructure"); + ret = -1; + goto out; + } + + ret = 0; + +out: + if (ret && priv) { + LOCK_DESTROY(&priv->snaplist_lock); + GF_FREE(priv->dirents); + GF_FREE(priv); + } + + return ret; +} + +void +fini(xlator_t *this) +{ + svs_private_t *priv = NULL; + glusterfs_ctx_t *ctx = NULL; + int ret = 0; + + GF_ASSERT(this); + priv = this->private; + this->private = NULL; + ctx = this->ctx; + if (!ctx) + gf_msg(this->name, GF_LOG_ERROR, 0, SVS_MSG_INVALID_GLFS_CTX, + "Invalid ctx found"); + + if (priv) { + ret = LOCK_DESTROY(&priv->snaplist_lock); + if (ret != 0) { + gf_msg(this->name, GF_LOG_WARNING, errno, + SVS_MSG_LOCK_DESTROY_FAILED, + "Could not destroy mutex snaplist_lock"); + } + + if (priv->dirents) { + GF_FREE(priv->dirents); + } + + if (priv->rpc) { + /* cleanup the saved-frames before last unref */ + rpc_clnt_connection_cleanup(&priv->rpc->conn); + rpc_clnt_unref(priv->rpc); + } + + GF_FREE(priv); + } + + return; +} + +struct xlator_fops fops = { + .lookup = svs_lookup, + .stat = svs_stat, + .statfs = svs_statfs, + .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, + .readlink = svs_readlink, + /* entry fops */ +}; + +struct xlator_cbks cbks = { + .release = svs_release, + .releasedir = svs_releasedir, + .forget = svs_forget, +}; + +struct volume_options options[] = { + { + .key = {"volname"}, + .type = GF_OPTION_TYPE_STR, + }, + {.key = {NULL}}, +}; + +xlator_api_t xlator_api = { + .init = init, + .fini = fini, + .notify = notify, + .mem_acct_init = mem_acct_init, + .op_version = {1}, + .fops = &fops, + .cbks = &cbks, + .options = options, + .identifier = "snapview-server", + .category = GF_MAINTAINED, +}; 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..6472422e715 --- /dev/null +++ b/xlators/features/snapview-server/src/snapview-server.h @@ -0,0 +1,255 @@ +/* + 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__ + +#include <glusterfs/dict.h> +#include <glusterfs/defaults.h> +#include <glusterfs/mem-types.h> +#include <glusterfs/call-stub.h> +#include <glusterfs/byte-order.h> +#include <glusterfs/iatt.h> +#include <ctype.h> +#include <sys/uio.h> +#include <glusterfs/glusterfs.h> +#include <glusterfs/logging.h> +#include "glfs.h" +#include "glfs-handles.h" +#include "glfs-internal.h" +#include "glusterfs3-xdr.h" +#include <glusterfs/glusterfs-acl.h> +#include <glusterfs/syncop.h> +#include <glusterfs/list.h> +#include <glusterfs/timer.h> +#include "rpc-clnt.h" +#include "protocol-common.h" +#include "xdr-generic.h" +#include "snapview-server-messages.h" + +#define DEFAULT_SVD_LOG_FILE_DIRECTORY DATADIR "/log/glusterfs" + +#define SNAP_VIEW_MAX_GLFS_T 256 +#define SNAP_VIEW_MAX_GLFS_FDS 1024 +#define SNAP_VIEW_MAX_GLFS_OBJ_HANDLES 1024 + +#define SVS_STACK_DESTROY(_frame) \ + do { \ + ((call_frame_t *)_frame)->local = NULL; \ + STACK_DESTROY(((call_frame_t *)_frame)->root); \ + } while (0) + +#define SVS_CHECK_VALID_SNAPSHOT_HANDLE(fs, this) \ + do { \ + svs_private_t *_private = NULL; \ + _private = this->private; \ + int i = 0; \ + gf_boolean_t found = _gf_false; \ + glfs_t *tmp_fs = NULL; \ + LOCK(&_private->snaplist_lock); \ + { \ + for (i = 0; i < _private->num_snaps; i++) { \ + tmp_fs = _private->dirents[i].fs; \ + gf_log(this->name, GF_LOG_DEBUG, \ + "snap name: %s, snap volume: %s," \ + "dirent->fs: %p", \ + _private->dirents[i].name, \ + _private->dirents[i].snap_volname, tmp_fs); \ + if (tmp_fs && fs && (tmp_fs == fs)) { \ + found = _gf_true; \ + gf_msg_debug(this->name, 0, \ + "found the fs " \ + "instance"); \ + break; \ + } \ + } \ + } \ + UNLOCK(&_private->snaplist_lock); \ + \ + if (!found) { \ + gf_log(this->name, GF_LOG_WARNING, \ + "failed to" \ + " find the fs instance %p", \ + fs); \ + fs = NULL; \ + } \ + } while (0) + +#define SVS_GET_INODE_CTX_INFO(inode_ctx, fs, object, this, loc, ret, \ + op_errno, label) \ + do { \ + fs = inode_ctx->fs; \ + object = inode_ctx->object; \ + SVS_CHECK_VALID_SNAPSHOT_HANDLE(fs, this); \ + if (!fs) \ + object = NULL; \ + \ + if (!fs || !object) { \ + int32_t tmp = -1; \ + char tmp_uuid[64]; \ + \ + tmp = svs_get_handle(this, loc, inode_ctx, &op_errno); \ + if (tmp) { \ + gf_log(this->name, GF_LOG_ERROR, \ + "failed to get the handle for %s " \ + "(gfid: %s)", \ + loc->path, uuid_utoa_r(loc->inode->gfid, tmp_uuid)); \ + ret = -1; \ + goto label; \ + } \ + \ + fs = inode_ctx->fs; \ + object = inode_ctx->object; \ + } \ + } while (0); + +#define SVS_STRDUP(dst, src) \ + do { \ + if (dst && strcmp(src, dst)) { \ + GF_FREE(dst); \ + dst = NULL; \ + } \ + \ + if (!dst) \ + dst = gf_strdup(src); \ + } while (0) + +int +svs_mgmt_submit_request(void *req, call_frame_t *frame, glusterfs_ctx_t *ctx, + rpc_clnt_prog_t *prog, int procnum, fop_cbk_fn_t cbkfn, + xdrproc_t xdrproc); + +int +svs_get_snapshot_list(xlator_t *this); + +int +mgmt_get_snapinfo_cbk(struct rpc_req *req, struct iovec *iov, int count, + void *myframe); + +typedef enum { + SNAP_VIEW_ENTRY_POINT_INODE = 0, + SNAP_VIEW_SNAPSHOT_INODE, + 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; + + /* This is used to generate gfid for all sub files/dirs under this + * snapshot + */ + char *snapname; + 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]; + char snap_volname[NAME_MAX]; + glfs_t *fs; +}; +typedef struct snap_dirent snap_dirent_t; + +struct svs_private { + snap_dirent_t *dirents; + int num_snaps; + char *volname; + struct list_head snaplist; + gf_lock_t snaplist_lock; + struct rpc_clnt *rpc; +}; +typedef struct svs_private svs_private_t; + +int +__svs_inode_ctx_set(xlator_t *this, inode_t *inode, svs_inode_t *svs_inode); + +svs_inode_t * +__svs_inode_ctx_get(xlator_t *this, inode_t *inode); + +svs_inode_t * +svs_inode_ctx_get(xlator_t *this, inode_t *inode); + +int32_t +svs_inode_ctx_set(xlator_t *this, inode_t *inode, svs_inode_t *svs_inode); + +svs_inode_t * +svs_inode_ctx_get_or_new(xlator_t *this, inode_t *inode); + +int +__svs_fd_ctx_set(xlator_t *this, fd_t *fd, svs_fd_t *svs_fd); + +svs_fd_t * +__svs_fd_ctx_get(xlator_t *this, fd_t *fd); + +svs_fd_t * +svs_fd_ctx_get(xlator_t *this, fd_t *fd); + +int32_t +svs_fd_ctx_set(xlator_t *this, fd_t *fd, svs_fd_t *svs_fd); + +svs_fd_t * +__svs_fd_ctx_get_or_new(xlator_t *this, fd_t *fd); + +svs_fd_t * +svs_fd_ctx_get_or_new(xlator_t *this, fd_t *fd); + +int +svs_uuid_generate(xlator_t *this, uuid_t gfid, char *snapname, + uuid_t origin_gfid); + +void +svs_fill_ino_from_gfid(struct iatt *buf); + +void +svs_iatt_fill(uuid_t gfid, struct iatt *buf); + +snap_dirent_t * +svs_get_latest_snap_entry(xlator_t *this); + +glfs_t * +svs_get_latest_snapshot(xlator_t *this); + +glfs_t * +svs_initialise_snapshot_volume(xlator_t *this, const char *name, + int32_t *op_errno); + +glfs_t * +__svs_initialise_snapshot_volume(xlator_t *this, const char *name, + int32_t *op_errno); + +snap_dirent_t * +__svs_get_snap_dirent(xlator_t *this, const char *name); + +int +svs_mgmt_init(xlator_t *this); + +int32_t +svs_get_handle(xlator_t *this, loc_t *loc, svs_inode_t *inode_ctx, + int32_t *op_errno); + +glfs_t * +svs_inode_glfs_mapping(xlator_t *this, inode_t *inode); + +glfs_t * +svs_inode_ctx_glfs_mapping(xlator_t *this, svs_inode_t *inode_ctx); + +#endif /* __SNAP_VIEW_H__ */ |
