/* Copyright (c) 2014 Red Hat, Inc. This file is part of GlusterFS. This file is licensed to you under your choice of the GNU Lesser General Public License, version 3 or any later version (LGPLv3 or later), or the GNU General Public License, version 2 (GPLv2), in all cases as published by the Free Software Foundation. */ #ifndef _CONFIG_H #define _CONFIG_H #include "config.h" #endif #include "snapview-server.h" #include "snapview-server-mem-types.h" /* * Helper functions */ int __svs_inode_ctx_set (xlator_t *this, inode_t *inode, svs_inode_t *svs_inode) { uint64_t value = 0; int ret = -1; GF_VALIDATE_OR_GOTO ("snapview-server", this, out); GF_VALIDATE_OR_GOTO (this->name, inode, out); GF_VALIDATE_OR_GOTO (this->name, svs_inode, out); value = (uint64_t)(long) svs_inode; ret = __inode_ctx_set (inode, this, &value); out: return ret; } svs_inode_t * __svs_inode_ctx_get (xlator_t *this, inode_t *inode) { svs_inode_t *svs_inode = NULL; uint64_t value = 0; int ret = -1; GF_VALIDATE_OR_GOTO ("snapview-server", this, out); GF_VALIDATE_OR_GOTO (this->name, inode, out); ret = __inode_ctx_get (inode, this, &value); if (ret) goto out; svs_inode = (svs_inode_t *) ((long) value); out: return svs_inode; } svs_inode_t * svs_inode_ctx_get (xlator_t *this, inode_t *inode) { svs_inode_t *svs_inode = NULL; GF_VALIDATE_OR_GOTO ("snapview-server", this, out); GF_VALIDATE_OR_GOTO (this->name, inode, out); LOCK (&inode->lock); { svs_inode = __svs_inode_ctx_get (this, inode); } UNLOCK (&inode->lock); out: return svs_inode; } int32_t svs_inode_ctx_set (xlator_t *this, inode_t *inode, svs_inode_t *svs_inode) { int32_t ret = -1; GF_VALIDATE_OR_GOTO ("snapview-server", this, out); GF_VALIDATE_OR_GOTO (this->name, inode, out); GF_VALIDATE_OR_GOTO (this->name, svs_inode, out); LOCK (&inode->lock); { ret = __svs_inode_ctx_set (this, inode, svs_inode); } UNLOCK (&inode->lock); out: return ret; } svs_inode_t * svs_inode_new () { svs_inode_t *svs_inode = NULL; svs_inode = GF_CALLOC (1, sizeof (*svs_inode), gf_svs_mt_svs_inode_t); return svs_inode; } svs_inode_t * svs_inode_ctx_get_or_new (xlator_t *this, inode_t *inode) { svs_inode_t *svs_inode = NULL; int ret = -1; GF_VALIDATE_OR_GOTO ("snapview-server", this, out); GF_VALIDATE_OR_GOTO (this->name, inode, out); LOCK (&inode->lock); { svs_inode = __svs_inode_ctx_get (this, inode); if (!svs_inode) { svs_inode = svs_inode_new (this, inode); if (svs_inode) { ret = __svs_inode_ctx_set (this, inode, svs_inode); if (ret) { GF_FREE (svs_inode); svs_inode = NULL; } } } } UNLOCK (&inode->lock); out: return svs_inode; } svs_fd_t * svs_fd_new () { svs_fd_t *svs_fd = NULL; svs_fd = GF_CALLOC (1, sizeof (*svs_fd), gf_svs_mt_svs_fd_t); return svs_fd; } int __svs_fd_ctx_set (xlator_t *this, fd_t *fd, svs_fd_t *svs_fd) { uint64_t value = 0; int ret = -1; GF_VALIDATE_OR_GOTO ("snapview-server", this, out); GF_VALIDATE_OR_GOTO (this->name, fd, out); GF_VALIDATE_OR_GOTO (this->name, svs_fd, out); value = (uint64_t)(long) svs_fd; ret = __fd_ctx_set (fd, this, value); out: return ret; } svs_fd_t * __svs_fd_ctx_get (xlator_t *this, fd_t *fd) { svs_fd_t *svs_fd = NULL; uint64_t value = 0; int ret = -1; GF_VALIDATE_OR_GOTO ("snapview-server", this, out); GF_VALIDATE_OR_GOTO (this->name, fd, out); ret = __fd_ctx_get (fd, this, &value); if (ret) return NULL; svs_fd = (svs_fd_t *) ((long) value); out: return svs_fd; } svs_fd_t * svs_fd_ctx_get (xlator_t *this, fd_t *fd) { svs_fd_t *svs_fd = NULL; GF_VALIDATE_OR_GOTO ("snapview-server", this, out); GF_VALIDATE_OR_GOTO (this->name, fd, out); LOCK (&fd->lock); { svs_fd = __svs_fd_ctx_get (this, fd); } UNLOCK (&fd->lock); out: return svs_fd; } int32_t svs_fd_ctx_set (xlator_t *this, fd_t *fd, svs_fd_t *svs_fd) { int32_t ret = -1; GF_VALIDATE_OR_GOTO ("snapview-server", this, out); GF_VALIDATE_OR_GOTO (this->name, fd, out); GF_VALIDATE_OR_GOTO (this->name, svs_fd, out); LOCK (&fd->lock); { ret = __svs_fd_ctx_set (this, fd, svs_fd); } UNLOCK (&fd->lock); out: return ret; } svs_fd_t * __svs_fd_ctx_get_or_new (xlator_t *this, fd_t *fd) { svs_fd_t *svs_fd = NULL; int ret = -1; glfs_t *fs = NULL; glfs_object_t *object = NULL; svs_inode_t *inode_ctx = NULL; glfs_fd_t *glfd = NULL; inode_t *inode = NULL; GF_VALIDATE_OR_GOTO ("snapview-server", this, out); GF_VALIDATE_OR_GOTO (this->name, fd, out); inode = fd->inode; svs_fd = __svs_fd_ctx_get (this, fd); if (svs_fd) { ret = 0; goto out; } svs_fd = svs_fd_new (this, fd); if (!svs_fd) { gf_log (this->name, GF_LOG_ERROR, "failed to allocate new fd " "context for gfid %s", uuid_utoa (inode->gfid)); goto out; } if (fd_is_anonymous (fd)) { inode_ctx = svs_inode_ctx_get (this, inode); if (!inode_ctx) { gf_log (this->name, GF_LOG_ERROR, "failed to get inode " "context for %s", uuid_utoa (inode->gfid)); goto out; } fs = inode_ctx->fs; object = inode_ctx->object; if (inode->ia_type == IA_IFDIR) { glfd = glfs_h_opendir (fs, object); if (!glfd) { gf_log (this->name, GF_LOG_ERROR, "failed to " "open the directory %s", uuid_utoa (inode->gfid)); goto out; } } if (inode->ia_type == IA_IFREG) { glfd = glfs_h_open (fs, object, O_RDONLY|O_LARGEFILE); if (!glfd) { gf_log (this->name, GF_LOG_ERROR, "failed to " "open the file %s", uuid_utoa (inode->gfid)); goto out; } } svs_fd->fd = glfd; } ret = __svs_fd_ctx_set (this, fd, svs_fd); if (ret) { gf_log (this->name, GF_LOG_ERROR, "failed to set fd context " "for gfid %s", uuid_utoa (inode->gfid)); if (svs_fd->fd) { if (inode->ia_type == IA_IFDIR) { ret = glfs_closedir (svs_fd->fd); if (ret) gf_log (this->name, GF_LOG_ERROR, "failed to close the fd for %s", uuid_utoa (inode->gfid)); } if (inode->ia_type == IA_IFREG) { ret = glfs_close (svs_fd->fd); if (ret) gf_log (this->name, GF_LOG_ERROR, "failed to close the fd for %s", uuid_utoa (inode->gfid)); } } ret = -1; } out: if (ret) { GF_FREE (svs_fd); svs_fd = NULL; } return svs_fd; } svs_fd_t * svs_fd_ctx_get_or_new (xlator_t *this, fd_t *fd) { svs_fd_t *svs_fd = NULL; GF_VALIDATE_OR_GOTO ("snapview-server", this, out); GF_VALIDATE_OR_GOTO (this->name, fd, out); LOCK (&fd->lock); { svs_fd = __svs_fd_ctx_get_or_new (this, fd); } UNLOCK (&fd->lock); out: return svs_fd; } void svs_fill_ino_from_gfid (struct iatt *buf) { uint64_t temp_ino = 0; int j = 0; int i = 0; xlator_t *this = NULL; this = THIS; GF_VALIDATE_OR_GOTO ("snapview-server", this, out); GF_VALIDATE_OR_GOTO (this->name, buf, out); /* consider least significant 8 bytes of value out of gfid */ if (uuid_is_null (buf->ia_gfid)) { buf->ia_ino = -1; goto out; } for (i = 15; i > (15 - 8); i--) { temp_ino += (uint64_t)(buf->ia_gfid[i]) << j; j += 8; } buf->ia_ino = temp_ino; out: return; } void svs_iatt_fill (uuid_t gfid, struct iatt *buf) { struct timeval tv = {0, }; xlator_t *this = NULL; this = THIS; GF_VALIDATE_OR_GOTO ("snapview-server", this, out); GF_VALIDATE_OR_GOTO (this->name, buf, out); buf->ia_type = IA_IFDIR; buf->ia_uid = 0; buf->ia_gid = 0; buf->ia_size = 0; buf->ia_nlink = 2; buf->ia_blocks = 8; buf->ia_size = 4096; uuid_copy (buf->ia_gfid, gfid); svs_fill_ino_from_gfid (buf); buf->ia_prot = ia_prot_from_st_mode (0755); gettimeofday (&tv, 0); buf->ia_mtime = buf->ia_atime = buf->ia_ctime = tv.tv_sec; buf->ia_mtime_nsec = buf->ia_atime_nsec = buf->ia_ctime_nsec = (tv.tv_usec * 1000); out: return; } snap_dirent_t * svs_get_snap_dirent (xlator_t *this, const char *name) { svs_private_t *private = NULL; int i = 0; snap_dirent_t *dirents = NULL; snap_dirent_t *tmp_dirent = NULL; snap_dirent_t *dirent = NULL; GF_VALIDATE_OR_GOTO ("snapview-server", this, out); GF_VALIDATE_OR_GOTO (this->name, this->private, out); GF_VALIDATE_OR_GOTO (this->name, name, out); private = this->private; dirents = private->dirents; tmp_dirent = dirents; for (i = 0; i < private->num_snaps; i++) { if (!strcmp (tmp_dirent->name, name)) { dirent = tmp_dirent; break; } tmp_dirent++; } out: return dirent; } glfs_t * svs_initialise_snapshot_volume (xlator_t *this, const char *name) { svs_private_t *priv = NULL; int32_t ret = -1; snap_dirent_t *dirent = NULL; char volname[PATH_MAX] = {0, }; glfs_t *fs = NULL; int loglevel = GF_LOG_INFO; char logfile[PATH_MAX] = {0, }; GF_VALIDATE_OR_GOTO ("snapview-server", this, out); GF_VALIDATE_OR_GOTO (this->name, this->private, out); GF_VALIDATE_OR_GOTO (this->name, name, out); priv = this->private; dirent = svs_get_snap_dirent (this, name); if (!dirent) { gf_log (this->name, GF_LOG_ERROR, "snap entry for name %s " "not found", name); goto out; } if (dirent->fs) { ret = 0; fs = dirent->fs; goto out; } snprintf (volname, sizeof (volname), "/snaps/%s/%s", dirent->name, dirent->uuid); fs = glfs_new (volname); if (!fs) { gf_log (this->name, GF_LOG_ERROR, "glfs instance for snap volume %s " "failed", dirent->name); goto out; } ret = glfs_set_volfile_server (fs, "tcp", "localhost", 24007); if (ret) { gf_log (this->name, GF_LOG_ERROR, "setting the " "volfile srever for snap volume %s " "failed", dirent->name); goto out; } ret = glfs_init (fs); if (ret) { gf_log (this->name, GF_LOG_ERROR, "initing the " "fs for %s failed", dirent->name); goto out; } snprintf (logfile, sizeof (logfile), DEFAULT_SVD_LOG_FILE_DIRECTORY "/%s-%s.log", name, dirent->uuid); ret = glfs_set_logging(fs, logfile, loglevel); if (ret) { gf_log (this->name, GF_LOG_ERROR, "failed to set the " "log file path"); goto out; } ret = 0; out: if (ret && fs) { glfs_fini (fs); fs = NULL; } if (fs) dirent->fs = fs; return fs; } snap_dirent_t * svs_get_latest_snap_entry (xlator_t *this) { svs_private_t *priv = NULL; snap_dirent_t *dirents = NULL; snap_dirent_t *dirent = NULL; snap_dirent_t *tmp_dirent = NULL; GF_VALIDATE_OR_GOTO ("svs", this, out); priv = this->private; dirents = priv->dirents; if (priv->num_snaps) { tmp_dirent = &dirents[priv->num_snaps - 1]; dirent = tmp_dirent; } out: return dirent; } glfs_t * svs_get_latest_snapshot (xlator_t *this) { glfs_t *fs = NULL; snap_dirent_t *dirent = NULL; GF_VALIDATE_OR_GOTO ("svs", this, out); dirent = svs_get_latest_snap_entry (this); if (dirent) fs = dirent->fs; out: return fs; } int32_t svs_lookup_entry_point (xlator_t *this, loc_t *loc, inode_t *parent, struct iatt *buf, struct iatt *postparent, int32_t *op_errno) { uuid_t gfid; svs_inode_t *inode_ctx = NULL; int op_ret = -1; GF_VALIDATE_OR_GOTO ("snapview-server", this, out); GF_VALIDATE_OR_GOTO (this->name, loc, out); GF_VALIDATE_OR_GOTO (this->name, loc->inode, out); GF_VALIDATE_OR_GOTO (this->name, buf, out); GF_VALIDATE_OR_GOTO (this->name, postparent, out); if (uuid_is_null (loc->inode->gfid)) { uuid_generate (gfid); svs_iatt_fill (gfid, buf); /* Here the inode context of the entry point directory is filled with just the type of the inode and the gfid of the parent from where the entry point was entered. The glfs object and the fs instance will be NULL. */ if (parent) svs_iatt_fill (parent->gfid, postparent); else { svs_iatt_fill (buf->ia_gfid, postparent); } inode_ctx = svs_inode_ctx_get_or_new (this, loc->inode); if (!inode_ctx) { gf_log (this->name, GF_LOG_ERROR, "failed to " "allocate inode context for entry point " "directory"); op_ret = -1; *op_errno = ENOMEM; goto out; } uuid_copy (inode_ctx->pargfid, loc->pargfid); memcpy (&inode_ctx->buf, buf, sizeof (*buf)); inode_ctx->type = SNAP_VIEW_ENTRY_POINT_INODE; } else { if (inode_ctx) { memcpy (buf, &inode_ctx->buf, sizeof (*buf)); svs_iatt_fill (inode_ctx->pargfid, postparent); } else { svs_iatt_fill (loc->inode->gfid, buf); if (parent) svs_iatt_fill (parent->gfid, postparent); else { svs_iatt_fill (loc->inode->gfid, postparent); } } } op_ret = 0; goto out; out: return op_ret; } /* When lookup comes from client and the protocol/server tries to resolve the pargfid via just sending the gfid as part of lookup, if the inode for the parent gfid is not found. But since that gfid has not yet been looked up yet, inode will not be having inode context and parent is not there (as it is the parent of the entry that is being resolved). So without parent and inode context, svs cannot know which snapshot to look into. In such cases, the amguity is handled by looking into the latest snapshot. If the directory is there in the latest snapshot, lookup is successful, otherwise it is a failure. So for any directory created after taking the latest snapshot, entry into snapshot world is denied. i.e you have to be part of snapshot world to enter it. If the gfid is not found there, then unwind with ESTALE This gets executed mainly in the situation where the snapshot entry point is entered from a non-root directory and that non-root directory's inode (or gfid) is not yet looked up. And in each case when a gfid has to be looked up (without any inode contex and parent context present), last snapshot is referred and a random gfid is not generated. */ int32_t svs_lookup_gfid (xlator_t *this, loc_t *loc, struct iatt *buf, struct iatt *postparent, int32_t *op_errno) { int32_t op_ret = -1; unsigned char handle_obj[GFAPI_HANDLE_LENGTH] = {0, }; glfs_t *fs = NULL; glfs_object_t *object = NULL; struct stat statbuf = {0, }; svs_inode_t *inode_ctx = NULL; GF_VALIDATE_OR_GOTO ("snapview-server", this, out); GF_VALIDATE_OR_GOTO (this->name, loc, out); GF_VALIDATE_OR_GOTO (this->name, loc->inode, out); GF_VALIDATE_OR_GOTO (this->name, buf, out); GF_VALIDATE_OR_GOTO (this->name, postparent, out); if (uuid_is_null (loc->gfid) && uuid_is_null (loc->inode->gfid)) { gf_log (this->name, GF_LOG_ERROR, "gfid is NULL"); goto out; } if (!uuid_is_null (loc->inode->gfid)) memcpy (handle_obj, loc->inode->gfid, GFAPI_HANDLE_LENGTH); else memcpy (handle_obj, loc->gfid, GFAPI_HANDLE_LENGTH); fs = svs_get_latest_snapshot (this); if (!fs) { gf_log (this->name, GF_LOG_ERROR, "failed to get the latest " "snapshot"); op_ret = -1; *op_errno = EINVAL; goto out; } object = glfs_h_create_from_handle (fs, handle_obj, GFAPI_HANDLE_LENGTH, &statbuf); if (!object) { gf_log (this->name, GF_LOG_ERROR, "failed to do lookup and get " "the handle on the snapshot %s (path: %s, gfid: %s)", loc->name, loc->path, uuid_utoa (loc->gfid)); op_ret = -1; *op_errno = ESTALE; goto out; } inode_ctx = svs_inode_ctx_get_or_new (this, loc->inode); if (!inode_ctx) { gf_log (this->name, GF_LOG_ERROR, "failed to allocate inode " "context"); op_ret = -1; *op_errno = ENOMEM; goto out; } iatt_from_stat (buf, &statbuf); if (loc->gfid) uuid_copy (buf->ia_gfid, loc->gfid); else uuid_copy (buf->ia_gfid, loc->inode->gfid); inode_ctx->type = SNAP_VIEW_VIRTUAL_INODE; inode_ctx->fs = fs; inode_ctx->object = object; memcpy (&inode_ctx->buf, buf, sizeof (*buf)); svs_iatt_fill (buf->ia_gfid, postparent); op_ret = 0; out: return op_ret; } /* If the parent is an entry point inode, then create the handle for the snapshot on which lookup came. i.e in reality lookup came on the directory from which the entry point directory was entered, but lookup is into the past. So create the handle for it by doing the name-less lookup on the gfid (which can be obtained from parent's context */ int32_t svs_lookup_snapshot (xlator_t *this, loc_t *loc, struct iatt *buf, struct iatt *postparent, inode_t *parent, svs_inode_t *parent_ctx, int32_t *op_errno) { int32_t op_ret = -1; unsigned char handle_obj[GFAPI_HANDLE_LENGTH] = {0, }; glfs_t *fs = NULL; glfs_object_t *object = NULL; struct stat statbuf = {0, }; svs_inode_t *inode_ctx = NULL; uuid_t gfid; GF_VALIDATE_OR_GOTO ("snapview-server", this, out); GF_VALIDATE_OR_GOTO (this->name, loc, out); GF_VALIDATE_OR_GOTO (this->name, loc->inode, out); GF_VALIDATE_OR_GOTO (this->name, buf, out); GF_VALIDATE_OR_GOTO (this->name, postparent, out); GF_VALIDATE_OR_GOTO (this->name, parent_ctx, out); GF_VALIDATE_OR_GOTO (this->name, parent, out); fs = svs_initialise_snapshot_volume (this, loc->name); if (!fs) { gf_log (this->name, GF_LOG_ERROR, "failed to " "create the fs instance for snap %s", loc->name); op_ret = -1; *op_errno = ESTALE; goto out; } memcpy (handle_obj, parent_ctx->pargfid, GFAPI_HANDLE_LENGTH); object = glfs_h_create_from_handle (fs, handle_obj, GFAPI_HANDLE_LENGTH, &statbuf); if (!object) { gf_log (this->name, GF_LOG_ERROR, "failed to do lookup and " "get the handle on the snapshot %s", loc->name); op_ret = -1; *op_errno = errno; goto out; } inode_ctx = svs_inode_ctx_get_or_new (this, loc->inode); if (!inode_ctx) { gf_log (this->name, GF_LOG_ERROR, "failed to " "allocate inode context"); op_ret = -1; *op_errno = ENOMEM; goto out; } if (uuid_is_null (loc->gfid) && uuid_is_null (loc->inode->gfid)) uuid_generate (gfid); else { if (!uuid_is_null (loc->inode->gfid)) uuid_copy (gfid, loc->inode->gfid); else uuid_copy (gfid, loc->gfid); } iatt_from_stat (buf, &statbuf); uuid_copy (buf->ia_gfid, gfid); svs_fill_ino_from_gfid (buf); inode_ctx->type = SNAP_VIEW_VIRTUAL_INODE; inode_ctx->fs = fs; inode_ctx->object = object; memcpy (&inode_ctx->buf, buf, sizeof (*buf)); svs_iatt_fill (parent->gfid, postparent); op_ret = 0; out: return op_ret; } /* Both parent and entry are from snapshot world */ int32_t svs_lookup_entry (xlator_t *this, loc_t *loc, struct iatt *buf, struct iatt *postparent, inode_t *parent, svs_inode_t *parent_ctx, int32_t *op_errno) { int32_t op_ret = -1; glfs_t *fs = NULL; glfs_object_t *object = NULL; struct stat statbuf = {0, }; svs_inode_t *inode_ctx = NULL; glfs_object_t *parent_object = NULL; uuid_t gfid; GF_VALIDATE_OR_GOTO ("snapview-server", this, out); GF_VALIDATE_OR_GOTO (this->name, loc, out); GF_VALIDATE_OR_GOTO (this->name, loc->inode, out); GF_VALIDATE_OR_GOTO (this->name, buf, out); GF_VALIDATE_OR_GOTO (this->name, postparent, out); GF_VALIDATE_OR_GOTO (this->name, parent_ctx, out); GF_VALIDATE_OR_GOTO (this->name, parent, out); parent_object = parent_ctx->object; fs = parent_ctx->fs; object = glfs_h_lookupat (fs, parent_object, loc->name, &statbuf); if (!object) { gf_log (this->name, GF_LOG_ERROR, "failed to do lookup and " "get the handle for entry %s (path: %s)", loc->name, loc->path); op_ret = -1; *op_errno = errno; goto out; } inode_ctx = svs_inode_ctx_get_or_new (this, loc->inode); if (!inode_ctx) { gf_log (this->name, GF_LOG_ERROR, "failed to " "allocate inode context"); op_ret = -1; *op_errno = ENOMEM; goto out; } if (uuid_is_null (loc->gfid) && uuid_is_null (loc->inode->gfid)) uuid_generate (gfid); else { if (!uuid_is_null (loc->inode->gfid)) uuid_copy (gfid, loc->inode->gfid); else uuid_copy (gfid, loc->gfid); } iatt_from_stat (buf, &statbuf); uuid_copy (buf->ia_gfid, gfid); svs_fill_ino_from_gfid (buf); inode_ctx->type = SNAP_VIEW_VIRTUAL_INODE; inode_ctx->fs = fs; inode_ctx->object = object; memcpy (&inode_ctx->buf, buf, sizeof (*buf)); svs_iatt_fill (parent->gfid, postparent); op_ret = 0; out: return op_ret; } /* inode context is there means lookup has come on an object which was built either as part of lookup or as part of readdirp. But in readdirp we would not have got the handle to access the object in the gfapi world. So if inode context contains glfs_t instance for the right gfapi world and glfs_object_t handle for accessing it in the gfapi world, then unwind with success as the snapshots as of now are read-only. If the above condition is not met, then send lookup call again to the gfapi world. It can happen only if both parent context and the name of the entry are present. If parent is an entry point to snapshot world: * parent is needed for getting the gfid on which lookup has to be done (the gfid present in the inode is a virtual gfid) in the snapshot world. * name is required to get the right glfs_t instance on which lookup has to be done If parent is a directory from snapshot world: * parent context is needed to get the glfs_t instance and to get the handle to parent directory in the snapshot world. * name is needed to do the lookup on the right entry in the snapshot world */ int32_t svs_revalidate (xlator_t *this, loc_t *loc, inode_t *parent, svs_inode_t *inode_ctx, svs_inode_t *parent_ctx, struct iatt *buf, struct iatt *postparent, int32_t *op_errno) { int32_t op_ret = -1; glfs_t *fs = NULL; glfs_object_t *object = NULL; unsigned char handle_obj[GFAPI_HANDLE_LENGTH] = {0, }; GF_VALIDATE_OR_GOTO ("snapview-server", this, out); GF_VALIDATE_OR_GOTO (this->name, buf, out); GF_VALIDATE_OR_GOTO (this->name, postparent, out); GF_VALIDATE_OR_GOTO (this->name, inode_ctx, out); if (inode_ctx->type == SNAP_VIEW_ENTRY_POINT_INODE) { svs_iatt_fill (loc->inode->gfid, buf); if (parent) svs_iatt_fill (parent->gfid, postparent); else svs_iatt_fill (loc->inode->gfid, postparent); op_ret = 0; goto out; } else { if (inode_ctx->fs && inode_ctx->object) { memcpy (buf, &inode_ctx->buf, sizeof (*buf)); if (parent) svs_iatt_fill (parent->gfid, postparent); else svs_iatt_fill (buf->ia_gfid, postparent); op_ret = 0; goto out; } /* To send the lookup to gfapi world, both the name of the entry as well as the parent context is needed. */ if (!loc->name || !parent_ctx) { *op_errno = ESTALE; goto out; } if (parent_ctx->type == SNAP_VIEW_ENTRY_POINT_INODE) op_ret = svs_lookup_snapshot (this, loc, buf, postparent, parent, parent_ctx, op_errno); else op_ret = svs_lookup_entry (this, loc, buf, postparent, parent, parent_ctx, op_errno); if (op_ret) *op_errno = ESTALE; goto out; } out: return op_ret; } int32_t svs_lookup (call_frame_t *frame, xlator_t *this, loc_t *loc, dict_t *xdata) { struct iatt buf = {0, }; int32_t op_ret = -1; int32_t op_errno = EINVAL; struct iatt postparent = {0,}; svs_inode_t *inode_ctx = NULL; svs_inode_t *parent_ctx = NULL; int32_t ret = -1; svs_private_t *private = NULL; inode_t *parent = NULL; glfs_t *fs = NULL; snap_dirent_t *dirent = NULL; gf_boolean_t entry_point = _gf_false; GF_VALIDATE_OR_GOTO ("svs", this, out); GF_VALIDATE_OR_GOTO (this->name, this->private, out); GF_VALIDATE_OR_GOTO (this->name, frame, out); GF_VALIDATE_OR_GOTO (this->name, loc, out); GF_VALIDATE_OR_GOTO (this->name, loc->inode, out); private = this->private; if (loc->name) { ret = dict_get_str_boolean (xdata, "entry-point", _gf_false); if (ret == -1) { gf_log (this->name, GF_LOG_ERROR, "failed to get the " "entry point info"); entry_point = _gf_false; } else { entry_point = ret; } } if (loc->parent) parent = inode_ref (loc->parent); else { parent = inode_find (loc->inode->table, loc->pargfid); if (!parent) parent = inode_parent (loc->inode, NULL, NULL); } if (parent) parent_ctx = svs_inode_ctx_get (this, parent); inode_ctx = svs_inode_ctx_get (this, loc->inode); /* Initialize latest snapshot, which is used for nameless lookups */ dirent = svs_get_latest_snap_entry (this); if (!dirent->fs) fs = svs_initialise_snapshot_volume (this, dirent->name); /* lookup is on the entry point to the snapshot world */ if (entry_point) { op_ret = svs_lookup_entry_point (this, loc, parent, &buf, &postparent, &op_errno); goto out; } /* revalidate */ if (inode_ctx) { op_ret = svs_revalidate (this, loc, parent, inode_ctx, parent_ctx, &buf, &postparent, &op_errno); goto out; } /* This can happen when entry point directory is entered from non-root directory. (ex: if /mnt/glusterfs is the mount point, then entry point (say .snaps) is entered from /mnt/glusterfs/dir/.snaps). Also it can happen when client sends a nameless lookup on just a gfid and the server does not have the inode in the inode table. */ if (!inode_ctx && !parent_ctx) { op_ret = svs_lookup_gfid (this, loc, &buf, &postparent, &op_errno); goto out; } if (parent_ctx) { if (parent_ctx->type == SNAP_VIEW_ENTRY_POINT_INODE) op_ret = svs_lookup_snapshot (this, loc, &buf, &postparent, parent, parent_ctx, &op_errno); else op_ret = svs_lookup_entry (this, loc, &buf, &postparent, parent, parent_ctx, &op_errno); goto out; } out: STACK_UNWIND_STRICT (lookup, frame, op_ret, op_errno, loc?loc->inode:NULL, &buf, xdata, &postparent); if (parent) inode_unref (parent); return 0; } int32_t svs_opendir (call_frame_t *frame, xlator_t *this, loc_t *loc, fd_t *fd, dict_t *xdata) { int32_t ret = -1; svs_inode_t *inode_ctx = NULL; int32_t op_ret = -1; int32_t op_errno = EINVAL; svs_fd_t *svs_fd = NULL; glfs_fd_t *glfd = NULL; glfs_t *fs = NULL; glfs_object_t *object = NULL; GF_VALIDATE_OR_GOTO ("snap-view-daemon", this, out); GF_VALIDATE_OR_GOTO (this->name, frame, out); GF_VALIDATE_OR_GOTO (this->name, fd, out); GF_VALIDATE_OR_GOTO (this->name, loc, out); GF_VALIDATE_OR_GOTO (this->name, loc->inode, out); inode_ctx = svs_inode_ctx_get (this, loc->inode); if (!inode_ctx) { gf_log (this->name, GF_LOG_ERROR, "inode context not found " "for the inode %s", uuid_utoa (loc->inode->gfid)); op_ret = -1; op_errno = ESTALE; goto out; } /* Fake success is sent if the opendir is on the entry point directory or the inode is SNAP_VIEW_ENTRY_POINT_INODE */ if (inode_ctx->type == SNAP_VIEW_ENTRY_POINT_INODE) { op_ret = 0; op_errno = 0; goto out; } else if (inode_ctx->type == SNAP_VIEW_VIRTUAL_INODE) { fs = inode_ctx->fs; object = inode_ctx->object; glfd = glfs_h_opendir (fs, object); if (!glfd) { op_ret = -1; op_errno = errno; gf_log (this->name, GF_LOG_ERROR, "opendir on %s " "failed (gfid: %s)", loc->name, uuid_utoa (loc->inode->gfid)); goto out; } svs_fd = svs_fd_ctx_get_or_new (this, fd); if (!svs_fd) { gf_log (this->name, GF_LOG_ERROR, "failed to allocate " "fd context %s (gfid: %s)", loc->name, uuid_utoa (fd->inode->gfid)); op_ret = -1; op_errno = ENOMEM; glfs_closedir (glfd); goto out; } svs_fd->fd = glfd; op_ret = 0; op_errno = 0; } out: STACK_UNWIND_STRICT (opendir, frame, op_ret, op_errno, fd, NULL); return 0; } int32_t svs_getxattr (call_frame_t *frame, xlator_t *this, loc_t *loc, const char *name, dict_t *xdata) { svs_inode_t *inode_ctx = NULL; int32_t op_ret = -1; int32_t op_errno = EINVAL; glfs_t *fs = NULL; glfs_object_t *object = NULL; char *value = 0; ssize_t size = 0; dict_t *dict = NULL; int ret = -1; GF_VALIDATE_OR_GOTO ("snap-view-daemon", this, out); GF_VALIDATE_OR_GOTO ("snap-view-daemon", frame, out); GF_VALIDATE_OR_GOTO ("snap-view-daemon", loc, out); GF_VALIDATE_OR_GOTO ("snap-view-daemon", loc->inode, out); inode_ctx = svs_inode_ctx_get (this, loc->inode); if (!inode_ctx) { gf_log (this->name, GF_LOG_ERROR, "inode context not found " "for the inode %s", uuid_utoa (loc->inode->gfid)); op_ret = -1; op_errno = ESTALE; goto out; } /* Fake success is sent if the getxattr is on entry point directory or the inode is SNAP_VIEW_ENTRY_POINT_INODE */ if (inode_ctx->type == SNAP_VIEW_ENTRY_POINT_INODE) { op_ret = -1; op_errno = EINVAL; goto out; } else if (inode_ctx->type == SNAP_VIEW_VIRTUAL_INODE) { fs = inode_ctx->fs; object = inode_ctx->object; size = glfs_h_getxattrs (fs, object, name, NULL, 0); if (!size == -1) { gf_log (this->name, GF_LOG_ERROR, "getxattr on %s " "failed (key: %s)", loc->name, name); op_ret = -1; op_errno = errno; goto out; } value = GF_CALLOC (size + 1, sizeof (char), gf_common_mt_char); if (!value) { gf_log (this->name, GF_LOG_ERROR, "failed to allocate " "memory for getxattr on %s (key: %s)", loc->name, name); op_ret = -1; op_errno = ENOMEM; goto out; } size = glfs_h_getxattrs (fs, object, name, value, size); if (size == -1) { gf_log (this->name, GF_LOG_ERROR, "failed to get the " "xattr %s for entry %s", name, loc->name); op_ret = -1; op_errno = errno; goto out; } value[size] = '\0'; dict = dict_new (); if (!dict) { gf_log (this->name, GF_LOG_ERROR, "failed to allocate " "dict"); op_ret = -1; op_errno = ENOMEM; goto out; } op_ret = dict_set_dynptr (dict, (char *)name, value, size); if (op_ret < 0) { op_errno = -op_ret; gf_log (this->name, GF_LOG_ERROR, "dict set operation " "for %s for the key %s failed.", loc->path, name); GF_FREE (value); goto out; } op_ret = 0; op_errno = 0; } out: if (op_ret) GF_FREE (value); STACK_UNWIND_STRICT (getxattr, frame, op_ret, op_errno, dict, NULL); return 0; } int32_t svs_fgetxattr (call_frame_t *frame, xlator_t *this, fd_t *fd, const char *name, dict_t *xdata) { svs_inode_t *inode_ctx = NULL; int32_t op_ret = -1; int32_t op_errno = EINVAL; char *value = 0; ssize_t size = 0; dict_t *dict = NULL; svs_fd_t *sfd = NULL; glfs_fd_t *glfd = NULL; GF_VALIDATE_OR_GOTO ("snap-view-daemon", this, out); GF_VALIDATE_OR_GOTO ("snap-view-daemon", frame, out); GF_VALIDATE_OR_GOTO ("snap-view-daemon", fd, out); GF_VALIDATE_OR_GOTO ("snap-view-daemon", fd->inode, out); inode_ctx = svs_inode_ctx_get (this, fd->inode); if (!inode_ctx) { gf_log (this->name, GF_LOG_ERROR, "inode context not found " "for the inode %s", uuid_utoa (fd->inode->gfid)); op_ret = -1; op_errno = ESTALE; goto out; } sfd = svs_fd_ctx_get_or_new (this, fd); if (!sfd) { gf_log (this->name, GF_LOG_ERROR, "failed to get the fd " "context for %s", uuid_utoa (fd->inode->gfid)); op_ret = -1; op_errno = EBADFD; goto out; } glfd = sfd->fd; /* Fake success is sent if the getxattr is on entry point directory or the inode is SNAP_VIEW_ENTRY_POINT_INODE */ if (inode_ctx->type == SNAP_VIEW_ENTRY_POINT_INODE) { op_ret = -1; op_errno = EINVAL; goto out; } if (inode_ctx->type == SNAP_VIEW_VIRTUAL_INODE) { size = glfs_fgetxattr (glfd, name, NULL, 0); if (!size == -1) { gf_log (this->name, GF_LOG_ERROR, "getxattr on %s " "failed (key: %s)", uuid_utoa (fd->inode->gfid), name); op_ret = -1; op_errno = errno; goto out; } value = GF_CALLOC (size + 1, sizeof (char), gf_common_mt_char); if (!value) { gf_log (this->name, GF_LOG_ERROR, "failed to allocate " "memory for getxattr on %s (key: %s)", uuid_utoa (fd->inode->gfid), name); op_ret = -1; op_errno = ENOMEM; goto out; } size = glfs_fgetxattr (glfd, name, value, size); if (size == -1) { gf_log (this->name, GF_LOG_ERROR, "failed to get the " "xattr %s for inode %s", name, uuid_utoa (fd->inode->gfid)); op_ret = -1; op_errno = errno; goto out; } value[size] = '\0'; dict = dict_new (); if (!dict) { gf_log (this->name, GF_LOG_ERROR, "failed to allocate " "dict"); op_ret = -1; op_errno = ENOMEM; goto out; } op_ret = dict_set_dynptr (dict, (char *)name, value, size); if (op_ret < 0) { op_errno = -op_ret; gf_log (this->name, GF_LOG_ERROR, "dict set operation " "for gfid %s for the key %s failed.", uuid_utoa (fd->inode->gfid), name); GF_FREE (value); goto out; } op_ret = 0; op_errno = 0; } out: if (op_ret) GF_FREE (value); STACK_UNWIND_STRICT (fgetxattr, frame, op_ret, op_errno, dict, NULL); return 0; } int32_t svs_releasedir (xlator_t *this, fd_t *fd) { svs_fd_t *sfd = NULL; uint64_t tmp_pfd = 0; int ret = 0; GF_VALIDATE_OR_GOTO ("snapview-server", this, out); GF_VALIDATE_OR_GOTO (this->name, fd, out); ret = fd_ctx_del (fd, this, &tmp_pfd); if (ret < 0) { gf_log (this->name, GF_LOG_DEBUG, "pfd from fd=%p is NULL", fd); goto out; } sfd = (svs_fd_t *)(long)tmp_pfd; if (sfd->fd) { ret = glfs_closedir (sfd->fd); if (ret) gf_log (this->name, GF_LOG_WARNING, "failed to close " "the glfd for directory %s", uuid_utoa (fd->inode->gfid)); } out: return 0; } int32_t svs_flush (call_frame_t *frame, xlator_t *this, fd_t *fd, dict_t *xdata) { int32_t op_ret = -1; int32_t op_errno = 0; int ret = -1; uint64_t value = 0; GF_VALIDATE_OR_GOTO ("snapview-server", this, out); GF_VALIDATE_OR_GOTO (this->name, frame, out); GF_VALIDATE_OR_GOTO (this->name, fd, out); ret = fd_ctx_get (fd, this, &value); if (ret < 0) { op_errno = EINVAL; gf_log (this->name, GF_LOG_WARNING, "pfd is NULL on fd=%p", fd); goto out; } op_ret = 0; out: STACK_UNWIND_STRICT (flush, frame, op_ret, op_errno, NULL); return 0; } int32_t svs_release (xlator_t *this, fd_t *fd) { svs_fd_t *sfd = NULL; uint64_t tmp_pfd = 0; int ret = 0; GF_VALIDATE_OR_GOTO ("snapview-server", this, out); GF_VALIDATE_OR_GOTO (this->name, fd, out); ret = fd_ctx_del (fd, this, &tmp_pfd); if (ret < 0) { gf_log (this->name, GF_LOG_DEBUG, "pfd from fd=%p is NULL", fd); goto out; } sfd = (svs_fd_t *)(long)tmp_pfd; if (sfd->fd) { ret = glfs_close (sfd->fd); if (ret) { gf_log (this->name, GF_LOG_ERROR, "failed to close " "the glfd for %s", uuid_utoa (fd->inode->gfid)); } } out: return 0; } int32_t svs_forget (xlator_t *this, inode_t *inode) { int ret = -1; uint64_t value = 0; svs_inode_t *inode_ctx = NULL; GF_VALIDATE_OR_GOTO ("snapview-server", this, out); GF_VALIDATE_OR_GOTO (this->name, inode, out); ret = inode_ctx_del (inode, this, &value); if (ret) { gf_log (this->name, GF_LOG_ERROR, "failed to delte the inode " "context of %s", uuid_utoa (inode->gfid)); goto out; } inode_ctx = (svs_inode_t *)value; if (inode_ctx->object) glfs_h_close (inode_ctx->object); GF_FREE (inode_ctx); out: return 0; } /* As of now, the list of snapshots is obtained by reading a predefined file (which the user has to generate using these commands) * /usr/local/sbin/gluster snapshot info | grep -i snap | grep -i Volume | grep -i Name | cut -d':' -f 2 > /tmp/tmp-snap-uuids /usr/local/sbin/gluster snapshot info | grep Snapshot | cut -d':' -f 2 > /tmp/tmp-snap_names This is a temporary workaround which will be changed to a notification based mechanism where snapd gets the list of snapshots from glusterd */ int svs_get_snapshot_list (xlator_t *this, svs_private_t *priv) { int ret = -1; char str_uuid[256] = {'\0'}; char str_name[256] = {'\0'}; int snap_count = 0; snap_dirent_t *dirents = NULL; FILE *fpn = NULL; FILE *fpu = NULL; int i = 0; GF_VALIDATE_OR_GOTO ("snapview-server", this, out); GF_VALIDATE_OR_GOTO (this->name, priv, out); dirents = GF_CALLOC (sizeof (*dirents), SNAP_VIEW_MAX_NUM_SNAPS, gf_svs_mt_dirents_t); if (!dirents) { gf_log (this->name, GF_LOG_ERROR, "failed to allocate memory"); goto out; /* error, bail */ } priv->dirents = dirents; fpu = fopen ("/tmp/tmp-snap-uuids", "r+"); fpn = fopen ("/tmp/tmp-snap_names", "r+"); if (!fpu || !fpn) { gf_log (this->name, GF_LOG_ERROR, "failed to open the file"); goto out; } while ((fscanf (fpu, "%s", str_uuid) != -1) && (fscanf (fpn, "%s", str_name) != -1)) { strncpy (dirents[i].uuid, str_uuid, strlen (str_uuid) + 1); strncpy (dirents[i].name, str_name, strlen (str_name) + 1); ++snap_count; ++i; } priv->num_snaps = snap_count; fclose (fpn); fclose (fpu); ret = 0; out: return ret; } int svs_fill_readdir (xlator_t *this, gf_dirent_t *entries, size_t size, off_t off) { gf_dirent_t *entry = NULL; svs_private_t *priv = NULL; int i = 0; snap_dirent_t *dirents = NULL; int this_size = 0; int filled_size = 0; int count = 0; GF_VALIDATE_OR_GOTO ("snap-view-daemon", this, out); GF_VALIDATE_OR_GOTO ("snap-view-daemon", entries, out); priv = this->private; /* create the dir entries */ dirents = priv->dirents; for (i = off; i < priv->num_snaps; ) { this_size = sizeof (gf_dirent_t) + strlen (dirents[i].name) + 1; if (this_size + filled_size > size ) goto out; entry = gf_dirent_for_name (dirents[i].name); if (!entry) { gf_log (this->name, GF_LOG_ERROR, "failed to allocate " "dentry for %s", dirents[i].name); goto out; } entry->d_off = i + 1; entry->d_ino = i + 2*42; entry->d_type = DT_DIR; list_add_tail (&entry->list, &entries->list); ++i; count++; filled_size += this_size; } out: return count; } int32_t svs_glfs_readdir (xlator_t *this, glfs_fd_t *glfd, gf_dirent_t *entries, int32_t *op_errno, struct iatt *buf, gf_boolean_t readdirplus, size_t size) { int filled_size = 0; int this_size = 0; int32_t ret = -1; int32_t count = 0; gf_dirent_t *entry = NULL; struct dirent *dirents = NULL; struct dirent de = {0, }; struct stat statbuf = {0, }; off_t in_case = -1; GF_VALIDATE_OR_GOTO ("svs", this, out); GF_VALIDATE_OR_GOTO (this->name, glfd, out); GF_VALIDATE_OR_GOTO (this->name, entries, out); GF_VALIDATE_OR_GOTO (this->name, buf, out); while (filled_size < size) { in_case = glfs_telldir (glfd); if (in_case == -1) { gf_log (this->name, GF_LOG_ERROR, "telldir failed"); break; } if (readdirplus) ret = glfs_readdirplus_r (glfd, &statbuf, &de, &dirents); else ret = glfs_readdir_r (glfd, &de, &dirents); if (ret == 0 && dirents != NULL) { if (readdirplus) this_size = max (sizeof (gf_dirent_t), sizeof (gfs3_dirplist)) + strlen (de.d_name) + 1; else this_size = sizeof (gf_dirent_t) + strlen (de.d_name) + 1; if (this_size + filled_size > size) { glfs_seekdir (glfd, in_case); break; } entry = gf_dirent_for_name (de.d_name); if (!entry) { gf_log (this->name, GF_LOG_ERROR, "could not create gf_dirent " "for entry %s: (%s)", entry->d_name, strerror (errno)); break; } entry->d_off = de.d_off; entry->d_ino = de.d_ino; entry->d_type = de.d_type; iatt_from_stat (buf, &statbuf); if (readdirplus) entry->d_stat = *buf; list_add_tail (&entry->list, &entries->list); filled_size += this_size; count++; } else if (ret == 0 && dirents == NULL) { *op_errno = ENOENT; break; } else if (ret != 0) { *op_errno = errno; break; } dirents = NULL; ret = -1; } out: return count; } /* readdirp can be of 2 types. 1) It can come on entry point directory where the list of snapshots is sent as dirents. In this case, the iatt structure is filled on the fly if the inode is not found for the entry or the inode context is NULL. Other wise if inode is found and inode context is there the iatt structure saved in the context is used. 2) It can be on a directory in one of the snapshots. In this case, the readdirp call would have sent us a iatt structure. So the same structure is used with the exception that the gfid and the inode numbers will be newly generated and filled in. */ void svs_readdirp_fill (xlator_t *this, inode_t *parent, svs_inode_t *parent_ctx, gf_dirent_t *entry) { inode_t *inode = NULL; uuid_t random_gfid = {0,}; struct iatt buf = {0, }; svs_inode_t *inode_ctx = NULL; GF_VALIDATE_OR_GOTO ("snapview-server", this, out); GF_VALIDATE_OR_GOTO (this->name, parent, out); GF_VALIDATE_OR_GOTO (this->name, parent_ctx, out); GF_VALIDATE_OR_GOTO (this->name, entry, out); inode = inode_grep (parent->table, parent, entry->d_name); if (inode) { entry->inode = inode; inode_ctx = svs_inode_ctx_get (this, inode); if (!inode_ctx) { uuid_copy (buf.ia_gfid, inode->gfid); svs_iatt_fill (inode->gfid, &buf); buf.ia_type = inode->ia_type; } else { buf = inode_ctx->buf; } entry->d_ino = buf.ia_ino; if (parent_ctx->type == SNAP_VIEW_ENTRY_POINT_INODE) entry->d_stat = buf; else { entry->d_stat.ia_ino = buf.ia_ino; uuid_copy (entry->d_stat.ia_gfid, buf.ia_gfid); } } else { inode = inode_new (parent->table); entry->inode = inode; uuid_generate (random_gfid); uuid_copy (buf.ia_gfid, random_gfid); svs_fill_ino_from_gfid (&buf); entry->d_ino = buf.ia_ino; /* If inode context allocation fails, then do not send the inode for that particular entry as part of readdirp response. Fuse and protocol/server will link the inodes in readdirp only if the entry contains inode in it. */ inode_ctx = svs_inode_ctx_get_or_new (this, inode); if (!inode_ctx) { gf_log (this->name, GF_LOG_ERROR, "failed to allocate " "inode context for %s", entry->d_name); inode_unref (entry->inode); entry->inode = NULL; goto out; } inode_ctx->type = SNAP_VIEW_VIRTUAL_INODE; if (parent_ctx->type == SNAP_VIEW_ENTRY_POINT_INODE) { buf.ia_type = IA_IFDIR; inode_ctx->buf = buf; entry->d_stat = buf; } else { uuid_copy (entry->d_stat.ia_gfid, buf.ia_gfid); entry->d_stat.ia_ino = buf.ia_ino; inode_ctx->buf = entry->d_stat; } } out: return; } /* In readdirp, though new inode is created along with the generation of new gfid, the inode context created will not contain the glfs_t instance for the filesystem it belongs to and the handle for it in the gfapi world. (handle is obtained only by doing the lookup call on the entry and doing lookup on each entry received as part of readdir call is a costly operation. So the fs and handle is NULL in the inode context and is filled in when lookup comes on that object. */ int32_t svs_readdirp (call_frame_t *frame, xlator_t *this, fd_t *fd, size_t size, off_t off, dict_t *dict) { gf_dirent_t entries; gf_dirent_t *entry = NULL; struct iatt buf = {0, }; int count = 0; int op_ret = -1; int op_errno = EINVAL; svs_inode_t *inode_ctx = NULL; svs_inode_t *parent_ctx = NULL; svs_fd_t *svs_fd = NULL; GF_VALIDATE_OR_GOTO ("snap-view-daemon", this, unwind); GF_VALIDATE_OR_GOTO (this->name, frame, unwind); GF_VALIDATE_OR_GOTO (this->name, fd, unwind); GF_VALIDATE_OR_GOTO (this->name, fd->inode, unwind); INIT_LIST_HEAD (&entries.list); parent_ctx = svs_inode_ctx_get (this, fd->inode); if (!parent_ctx) { gf_log (this->name, GF_LOG_ERROR, "failed to get the inode " "context for %s", uuid_utoa (fd->inode->gfid)); op_ret = -1; op_errno = EINVAL; goto unwind; } if (parent_ctx->type == SNAP_VIEW_ENTRY_POINT_INODE) { LOCK (&fd->lock); { count = svs_fill_readdir (this, &entries, size, off); } UNLOCK (&fd->lock); op_ret = count; list_for_each_entry (entry, &entries.list, list) { svs_readdirp_fill (this, fd->inode, parent_ctx, entry); } goto unwind; } else { svs_fd = svs_fd_ctx_get_or_new (this, fd); if (!svs_fd) { gf_log (this->name, GF_LOG_ERROR, "failed to get the " "fd context %s", uuid_utoa (fd->inode->gfid)); op_ret = -1; op_errno = EBADFD; goto unwind; } glfs_seekdir (svs_fd->fd, off); LOCK (&fd->lock); { count = svs_glfs_readdir (this, svs_fd->fd, &entries, &op_errno, &buf, _gf_true, size); } UNLOCK (&fd->lock); op_ret = count; list_for_each_entry (entry, &entries.list, list) { svs_readdirp_fill (this, fd->inode, parent_ctx, entry); } goto unwind; } unwind: STACK_UNWIND_STRICT (readdirp, frame, op_ret, op_errno, &entries, dict); gf_dirent_free (&entries); return 0; } int32_t svs_readdir (call_frame_t *frame, xlator_t *this, fd_t *fd, size_t size, off_t off, dict_t *xdata) { svs_private_t *priv = NULL; gf_dirent_t entries; int count = 0; svs_inode_t *inode_ctx = NULL; int op_errno = EINVAL; int op_ret = -1; svs_fd_t *svs_fd = NULL; glfs_fd_t *glfd = NULL; GF_VALIDATE_OR_GOTO ("snap-view-server", this, unwind); GF_VALIDATE_OR_GOTO (this->name, frame, unwind); GF_VALIDATE_OR_GOTO (this->name, fd, unwind); GF_VALIDATE_OR_GOTO (this->name, fd->inode, unwind); priv = this->private; inode_ctx = svs_inode_ctx_get (this, fd->inode); if (!inode_ctx) { gf_log (this->name, GF_LOG_ERROR, "inode context not found in " "the inode %s", uuid_utoa (fd->inode->gfid)); op_ret = -1; op_errno = EINVAL; goto unwind; } if (inode_ctx->type == SNAP_VIEW_ENTRY_POINT_INODE) { LOCK (&fd->lock); { count = svs_fill_readdir (this, &entries, size, off); } UNLOCK (&fd->lock); } else { svs_fd = svs_fd_ctx_get_or_new (this, fd); if (!svs_fd) { gf_log (this->name, GF_LOG_ERROR, "failed to get the " "fd context %s", uuid_utoa (fd->inode->gfid)); op_ret = -1; op_errno = EBADFD; goto unwind; } glfd = svs_fd->fd; LOCK (&fd->lock); { count = svs_glfs_readdir (this, glfd, &entries, &op_errno, NULL, _gf_false, size); } UNLOCK (&fd->lock); } op_ret = count; unwind: STACK_UNWIND_STRICT (readdir, frame, op_ret, op_errno, &entries, xdata); gf_dirent_free (&entries); return 0; } int32_t svs_stat (call_frame_t *frame, xlator_t *this, loc_t *loc, dict_t *xdata) { svs_private_t *priv = NULL; struct iatt buf = {0, }; int32_t op_errno = EINVAL; int32_t op_ret = -1; svs_inode_t *inode_ctx = NULL; glfs_t *fs = NULL; glfs_object_t *object = NULL; struct stat stat = {0, }; int ret = -1; GF_VALIDATE_OR_GOTO ("snap-view-daemon", this, out); GF_VALIDATE_OR_GOTO (this->name, frame, out); GF_VALIDATE_OR_GOTO (this->name, loc, out); GF_VALIDATE_OR_GOTO (this->name, loc->inode, out); priv = this->private; /* Instead of doing the check of whether it is a entry point directory or not by checking the name of the entry and then deciding what to do, just check the inode context and decide what to be done. */ inode_ctx = svs_inode_ctx_get (this, loc->inode); if (!inode_ctx) { gf_log (this->name, GF_LOG_ERROR, "inode context not found for" " %s", uuid_utoa (loc->inode->gfid)); op_ret = -1; op_errno = EINVAL; goto out; } if (inode_ctx->type == SNAP_VIEW_ENTRY_POINT_INODE) { svs_iatt_fill (loc->inode->gfid, &buf); op_ret = 0; } else if (inode_ctx->type == SNAP_VIEW_VIRTUAL_INODE) { fs = inode_ctx->fs; object = inode_ctx->object; ret = glfs_h_stat (fs, object, &stat); if (ret) { gf_log (this->name, GF_LOG_ERROR, "glfs_h_stat on %s " "(gfid: %s) failed", loc->name, uuid_utoa (loc->inode->gfid)); op_ret = -1; op_errno = errno; goto out; } iatt_from_stat (&buf, &stat); uuid_copy (buf.ia_gfid, loc->inode->gfid); svs_fill_ino_from_gfid (&buf); op_ret = ret; } out: STACK_UNWIND_STRICT (stat, frame, op_ret, op_errno, &buf, xdata); return 0; } int32_t svs_fstat (call_frame_t *frame, xlator_t *this, fd_t *fd, dict_t *xdata) { svs_private_t *priv = NULL; struct iatt buf = {0, }; int32_t op_errno = EINVAL; int32_t op_ret = -1; svs_inode_t *inode_ctx = NULL; struct stat stat = {0, }; int ret = -1; glfs_fd_t *glfd = NULL; svs_fd_t *sfd = NULL; GF_VALIDATE_OR_GOTO ("snap-view-daemon", this, out); GF_VALIDATE_OR_GOTO (this->name, frame, out); GF_VALIDATE_OR_GOTO (this->name, fd, out); GF_VALIDATE_OR_GOTO (this->name, fd->inode, out); priv = this->private; /* Instead of doing the check of whether it is a entry point directory or not by checking the name of the entry and then deciding what to do, just check the inode context and decide what to be done. */ inode_ctx = svs_inode_ctx_get (this, fd->inode); if (!inode_ctx) { gf_log (this->name, GF_LOG_ERROR, "inode context not found for" " the inode %s", uuid_utoa (fd->inode->gfid)); op_ret = -1; op_errno = EINVAL; goto out; } if (inode_ctx->type == SNAP_VIEW_ENTRY_POINT_INODE) { svs_iatt_fill (fd->inode->gfid, &buf); op_ret = 0; } else if (inode_ctx->type == SNAP_VIEW_VIRTUAL_INODE) { sfd = svs_fd_ctx_get_or_new (this, fd); if (!sfd) { gf_log (this->name, GF_LOG_ERROR, "failed to get the " "fd context for %s", uuid_utoa (fd->inode->gfid)); op_ret = -1; op_errno = EBADFD; goto out; } glfd = sfd->fd; ret = glfs_fstat (glfd, &stat); if (ret) { gf_log (this->name, GF_LOG_ERROR, "glfs_fstat on " "gfid: %s failed", uuid_utoa (fd->inode->gfid)); op_ret = -1; op_errno = errno; goto out; } iatt_from_stat (&buf, &stat); uuid_copy (buf.ia_gfid, fd->inode->gfid); svs_fill_ino_from_gfid (&buf); op_ret = ret; } out: STACK_UNWIND_STRICT (fstat, frame, op_ret, op_errno, &buf, xdata); return 0; } int32_t svs_open (call_frame_t *frame, xlator_t *this, loc_t *loc, int32_t flags, fd_t *fd, dict_t *xdata) { int32_t ret = -1; svs_inode_t *inode_ctx = NULL; svs_fd_t *sfd = NULL; int32_t op_ret = -1; int32_t op_errno = EINVAL; glfs_fd_t *glfd = NULL; glfs_t *fs = NULL; glfs_object_t *object = NULL; GF_VALIDATE_OR_GOTO ("snap-view-daemon", this, out); GF_VALIDATE_OR_GOTO (this->name, frame, out); GF_VALIDATE_OR_GOTO (this->name, fd, out); GF_VALIDATE_OR_GOTO (this->name, loc, out); GF_VALIDATE_OR_GOTO (this->name, loc->inode, out); inode_ctx = svs_inode_ctx_get (this, loc->inode); if (!inode_ctx) { gf_log (this->name, GF_LOG_ERROR, "inode context for %s " "(gfid: %s) not found", loc->name, uuid_utoa (loc->inode->gfid)); goto out; } if (inode_ctx->type == SNAP_VIEW_ENTRY_POINT_INODE) GF_ASSERT (0); // on entry point it should always be opendir fs = inode_ctx->fs; object = inode_ctx->object; glfd = glfs_h_open (fs, object, flags); if (!glfd) { gf_log (this->name, GF_LOG_ERROR, "glfs_h_open on %s failed " "(gfid: %s)", loc->name, uuid_utoa (loc->inode->gfid)); op_ret = -1; op_errno = errno; goto out; } sfd = svs_fd_ctx_get_or_new (this, fd); if (!sfd) { gf_log (this->name, GF_LOG_ERROR, "failed to allocate fd " "context for %s (gfid: %s)", loc->name, uuid_utoa (loc->inode->gfid)); op_ret = -1; op_errno = ENOMEM; glfs_close (glfd); goto out; } sfd->fd = glfd; op_ret = 0; out: STACK_UNWIND_STRICT (open, frame, op_ret, op_errno, fd, NULL); return 0; } int32_t svs_readv (call_frame_t *frame, xlator_t *this, fd_t *fd, size_t size, off_t offset, uint32_t flags, dict_t *xdata) { int32_t op_ret = -1; int32_t op_errno = 0; svs_private_t *priv = NULL; struct iobuf *iobuf = NULL; struct iobref *iobref = NULL; struct iovec vec = {0,}; svs_fd_t *sfd = NULL; int ret = -1; struct stat fstatbuf = {0, }; glfs_fd_t *glfd = NULL; struct iatt stbuf = {0, }; GF_VALIDATE_OR_GOTO ("snap-view-daemon", this, out); GF_VALIDATE_OR_GOTO (this->name, frame, out); GF_VALIDATE_OR_GOTO (this->name, fd, out); GF_VALIDATE_OR_GOTO (this->name, fd->inode, out); priv = this->private; VALIDATE_OR_GOTO (priv, out); sfd = svs_fd_ctx_get_or_new (this, fd); if (!sfd) { gf_log (this->name, GF_LOG_ERROR, "failed to get the fd " "context for %s", uuid_utoa (fd->inode->gfid)); op_ret = -1; op_errno = EBADFD; goto out; } glfd = sfd->fd; iobuf = iobuf_get2 (this->ctx->iobuf_pool, size); if (!iobuf) { op_ret = -1; op_errno = ENOMEM; goto out; } ret = glfs_pread (glfd, iobuf->ptr, size, offset, 0); if (ret < 0) { op_ret = -1; op_errno = errno; gf_log (this->name, GF_LOG_ERROR, "glfs_read failed (%s)", strerror (op_errno)); goto out; } vec.iov_base = iobuf->ptr; vec.iov_len = ret; iobref = iobref_new (); iobref_add (iobref, iobuf); ret = glfs_fstat (glfd, &fstatbuf); if (ret) { op_ret = -1; op_errno = errno; gf_log (this->name, GF_LOG_ERROR, "glfs_fstat failed after " "readv on %s", uuid_utoa (fd->inode->gfid)); goto out; } iatt_from_stat (&stbuf, &fstatbuf); uuid_copy (stbuf.ia_gfid, fd->inode->gfid); svs_fill_ino_from_gfid (&stbuf); /* Hack to notify higher layers of EOF. */ if (!stbuf.ia_size || (offset + vec.iov_len) >= stbuf.ia_size) op_errno = ENOENT; op_ret = vec.iov_len; out: STACK_UNWIND_STRICT (readv, frame, op_ret, op_errno, &vec, 1, &stbuf, iobref, NULL); if (iobref) iobref_unref (iobref); if (iobuf) iobuf_unref (iobuf); return 0; } int32_t svs_readlink (call_frame_t *frame, xlator_t *this, loc_t *loc, size_t size, dict_t *xdata) { svs_inode_t *inode_ctx = NULL; glfs_t *fs = NULL; glfs_object_t *object = NULL; int op_ret = -1; int op_errno = EINVAL; char *buf = NULL; struct iatt stbuf = {0, }; int ret = -1; struct stat stat = {0, }; GF_VALIDATE_OR_GOTO ("snap-view-daemon", this, out); GF_VALIDATE_OR_GOTO (this->name, frame, out); GF_VALIDATE_OR_GOTO (this->name, loc, out); GF_VALIDATE_OR_GOTO (this->name, loc->inode, out); inode_ctx = svs_inode_ctx_get (this, loc->inode); if (!inode_ctx) { gf_log (this->name, GF_LOG_ERROR, "failed to get inode context " "for %s (gfid: %s)", loc->name, uuid_utoa (loc->inode->gfid)); op_ret = -1; op_errno = EINVAL; goto out; } fs = inode_ctx->fs; if (!fs) { gf_log (this->name, GF_LOG_ERROR, "failed to get the fs " "instance for %s (gfid: %s)", loc->name, uuid_utoa (loc->inode->gfid)); op_ret = -1; op_errno = ESTALE; goto out; } object = inode_ctx->object; if (!object) { gf_log (this->name, GF_LOG_ERROR, "failed to get the object " "for %s (gfid: %s)", loc->name, uuid_utoa (loc->inode->gfid)); op_ret = -1; op_errno = ESTALE; goto out; } ret = glfs_h_stat (fs, object, &stat); if (ret) { gf_log (this->name, GF_LOG_ERROR, "glfs_h_stat on %s " "(gfid: %s) failed", loc->name, uuid_utoa (loc->inode->gfid)); op_ret = -1; op_errno = errno; goto out; } iatt_from_stat (&stbuf, &stat); uuid_copy (stbuf.ia_gfid, loc->inode->gfid); svs_fill_ino_from_gfid (&stbuf); buf = alloca (size + 1); op_ret = glfs_h_readlink (fs, object, buf, size); if (op_ret == -1) { gf_log (this->name, GF_LOG_ERROR, "readlink on %s failed " "(gfid: %s)", loc->name, uuid_utoa (loc->inode->gfid)); op_errno = errno; goto out; } buf[op_ret] = 0; out: STACK_UNWIND_STRICT (readlink, frame, op_ret, op_errno, buf, &stbuf, NULL); return 0; } int32_t svs_access (call_frame_t *frame, xlator_t *this, loc_t *loc, int mask, dict_t *xdata) { int ret = -1; int32_t op_ret = -1; int32_t op_errno = EINVAL; svs_private_t *priv = NULL; glfs_t *fs = NULL; glfs_object_t *object = NULL; svs_inode_t *inode_ctx = NULL; gf_boolean_t is_fuse_call = 0; int perm = 0; int mode = 0; GF_VALIDATE_OR_GOTO ("svs", this, out); GF_VALIDATE_OR_GOTO (this->name, this->private, out); GF_VALIDATE_OR_GOTO (this->name, loc, out); GF_VALIDATE_OR_GOTO (this->name, loc->inode, out); priv = this->private; inode_ctx = svs_inode_ctx_get (this, loc->inode); if (!inode_ctx) { gf_log (this->name, GF_LOG_ERROR, "inode context not found for" " %s", uuid_utoa (loc->inode->gfid)); op_ret = -1; op_errno = EINVAL; goto out; } is_fuse_call = __is_fuse_call (frame); if (inode_ctx->type == SNAP_VIEW_ENTRY_POINT_INODE) { if (is_fuse_call) { op_ret = 0; op_errno = 0; } else { op_ret = 0; mode |= POSIX_ACL_READ; mode |= POSIX_ACL_EXECUTE; op_errno = mode; } goto out; } fs = inode_ctx->fs; object = inode_ctx->object; if (!is_fuse_call) syncopctx_setfspid (&frame->root->pid); ret = glfs_h_access (fs, object, mask); if (ret < 0) { gf_log (this->name, GF_LOG_ERROR, "failed to access %s " "(gfid: %s)", loc->path, uuid_utoa (loc->inode->gfid)); op_ret = -1; op_errno = errno; goto out; } /* The actual posix_acl xlator does acl checks differently for fuse and nfs. In this case how to send the information of whether the call came from fuse or nfs to the snapshot volume via gfapi? */ op_ret = 0; op_errno = ret; out: STACK_UNWIND_STRICT (access, frame, op_ret, op_errno, NULL); return 0; } int32_t mem_acct_init (xlator_t *this) { int ret = -1; if (!this) return ret; ret = xlator_mem_acct_init (this, gf_svs_mt_end + 1); if (ret != 0) { gf_log (this->name, GF_LOG_WARNING, "Memory accounting" " init failed"); return ret; } return ret; } int32_t init (xlator_t *this) { svs_private_t *priv = NULL; int ret = -1; /* This can be the top of graph in certain cases */ if (!this->parents) { gf_log (this->name, GF_LOG_DEBUG, "dangling volume. check volfile "); } /* TODO: define a mem-type structure */ priv = GF_CALLOC (1, sizeof (*priv), gf_svs_mt_priv_t); if (!priv) goto out; this->private = priv; /* get the list of snaps first to return to client xlator */ ret = svs_get_snapshot_list (this, priv); ret = 0; out: if (ret && priv) { GF_FREE (priv); } return ret; } void fini (xlator_t *this) { svs_private_t *priv = NULL; priv = this->private; this->private = NULL; if (priv) { GF_FREE (priv); } return; } struct xlator_fops fops = { .lookup = svs_lookup, .stat = svs_stat, .opendir = svs_opendir, .readdirp = svs_readdirp, .readdir = svs_readdir, .open = svs_open, .readv = svs_readv, .flush = svs_flush, .fstat = svs_fstat, .getxattr = svs_getxattr, .access = svs_access, /* entry fops */ }; struct xlator_cbks cbks = { .release = svs_release, .releasedir = svs_releasedir, .forget = svs_forget, }; struct volume_options options[] = { /* This translator doesn't take any options, or provide any options */ { .key = {NULL} }, };