diff options
21 files changed, 4489 insertions, 12 deletions
diff --git a/api/src/glfs-handleops.c b/api/src/glfs-handleops.c index f7ad454bf62..247b8aebf8b 100644 --- a/api/src/glfs-handleops.c +++ b/api/src/glfs-handleops.c @@ -945,6 +945,57 @@ out:  	return glfd;  } +int +glfs_h_access (struct glfs *fs, struct glfs_object *object, int mask) +{ +	int              ret = -1; +	xlator_t        *subvol = NULL; +	inode_t         *inode = NULL; +	loc_t            loc = {0, }; + +	/* validate in args */ +	if ((fs == NULL) || (object == NULL)) { +		errno = EINVAL; +		goto out; +	} + +	__glfs_entry_fs (fs); + +	/* get the active volume */ +	subvol = glfs_active_subvol (fs); +	if (!subvol) { +		ret = -1; +		errno = EIO; +		goto out; +	} + +	/* get/refresh the in arg objects inode in correlation to the xlator */ +	inode = glfs_resolve_inode (fs, subvol, object); +	if (!inode) { +		errno = ESTALE; +		goto out; +	} + + +	GLFS_LOC_FILL_INODE (inode, loc, out); + +	/* fop/op */ + +	ret = syncop_access (subvol, &loc, mask); +        DECODE_SYNCOP_ERR (ret); + +out: +	loc_wipe (&loc); + +	if (inode) +		inode_unref (inode); + + +	glfs_subvol_done (fs, subvol); + +	return ret; +} +  ssize_t  glfs_h_extract_handle (struct glfs_object *object, unsigned char *handle,  		       int len) diff --git a/api/src/glfs-handles.h b/api/src/glfs-handles.h index a6c6c0a3443..ad963455e85 100644 --- a/api/src/glfs-handles.h +++ b/api/src/glfs-handles.h @@ -162,6 +162,9 @@ struct glfs_fd *glfs_h_opendir (struct glfs *fs,  struct glfs_fd *glfs_h_open (struct glfs *fs, struct glfs_object *object,  			     int flags) __THROW; +int +glfs_h_access (struct glfs *fs, struct glfs_object *object, int mask) __THROW; +  __END_DECLS  #endif /* !_GLFS_HANDLES_H */ diff --git a/configure.ac b/configure.ac index 39bea64caa7..27b76004e93 100644 --- a/configure.ac +++ b/configure.ac @@ -139,6 +139,10 @@ AC_CONFIG_FILES([Makefile                  xlators/features/protect/src/Makefile                  xlators/features/gfid-access/Makefile                  xlators/features/gfid-access/src/Makefile +                xlators/features/snapview-server/Makefile +                xlators/features/snapview-server/src/Makefile +                xlators/features/snapview-client/Makefile +                xlators/features/snapview-client/src/Makefile                  xlators/playground/Makefile                  xlators/playground/template/Makefile                  xlators/playground/template/src/Makefile diff --git a/libglusterfs/src/syncop.c b/libglusterfs/src/syncop.c index 9705a7d5452..3dd622c8e99 100644 --- a/libglusterfs/src/syncop.c +++ b/libglusterfs/src/syncop.c @@ -2174,6 +2174,21 @@ syncop_access_cbk (call_frame_t *frame, void *cookie, xlator_t *this,          return 0;  } +/* posix_acl xlator will respond in different ways for access calls from +   fuse and access calls from nfs. For fuse, checking op_ret is sufficient +   to check whether the access call is successful or not. But for nfs the +   mode of the access that is permitted is put into op_errno before unwind. +   With syncop, the caller of syncop_access will not be able to get the +   mode of the access despite call being successul (since syncop_access +   returns only the op_ret collected in args). +   Now, if access call is failed, then args.op_ret is returned to recognise +   the failure. But if op_ret is zero, then the mode of access which is +   set in args.op_errno is returned. Thus the caller of syncop_access +   has to check whether the return value is less than zero or not. If the +   return value it got is less than zero, then access call is failed. +   If it is not, then the access call is successful and the value the caller +   got is the mode of the access. +*/  int  syncop_access (xlator_t *subvol, loc_t *loc, int32_t mask)  { @@ -2184,7 +2199,7 @@ syncop_access (xlator_t *subvol, loc_t *loc, int32_t mask)          if (args.op_ret < 0)                  return -args.op_errno; -        return args.op_ret; +        return args.op_errno;  } diff --git a/libglusterfs/src/xlator.h b/libglusterfs/src/xlator.h index 1daa06ec2ed..47ae6cdd6b6 100644 --- a/libglusterfs/src/xlator.h +++ b/libglusterfs/src/xlator.h @@ -29,6 +29,7 @@  #include "latency.h"  #define FIRST_CHILD(xl) (xl->children->xlator) +#define SECOND_CHILD(xl) (xl->children->next->xlator)  #define GF_SET_ATTR_MODE  0x1  #define GF_SET_ATTR_UID   0x2 diff --git a/tests/bugs/bug-859581.t b/tests/bugs/bug-859581.t index f31e8b311ef..0aab48bb02b 100755 --- a/tests/bugs/bug-859581.t +++ b/tests/bugs/bug-859581.t @@ -22,7 +22,7 @@ TEST rm -f $(gf_get_gfid_backend_file_path $B0/${V0}1 "dir1")  TEST rmdir $B0/${V0}1/dir1/dir2  TEST $CLI volume heal $V0 full -sleep 5 +EXPECT_WITHIN $HEAL_TIMEOUT "0" afr_get_pending_heal_count $V0  TEST [ -d $B0/${V0}1/dir1/dir2 ]  TEST [ ! -d $(gf_get_gfid_backend_file_path $B0/${V0}1 "dir1") ] @@ -35,7 +35,15 @@ TEST $CLI volume start $V0  EXPECT 'Started' volinfo_field $V0 'Status';  TEST glusterfs --entry-timeout=0 --attribute-timeout=0 -s $H0 --volfile-id $V0 $M0; -ls -l $M0/ +# Till now, protocol/server was not doing inode linking as part of readdirp. +# But pas part of user servicable snapshots patcth, changes to do inode linking +# in protocol/server in readdirp,  were introduced. So now to make sure +# the gfid handle of dir1 is healed, explicit lookup has to be sent on it. +# Otherwise, whenever ls -l is done just on the mount point $M0, lookup on the +# entries received as part of readdirp, is not sent, because the inodes for +# those entries were linked as part of readdirp itself. i.e instead of doing +# "ls -l $M0", it has to be the below command. +ls -l $M0/dir1;  TEST [ -h $(gf_get_gfid_backend_file_path $B0/${V0}1 "dir1") ] diff --git a/xlators/features/Makefile.am b/xlators/features/Makefile.am index 1fdd474c20a..8093441a043 100644 --- a/xlators/features/Makefile.am +++ b/xlators/features/Makefile.am @@ -1,4 +1,4 @@  SUBDIRS = locks quota read-only mac-compat quiesce marker index barrier \ -	  protect compress changelog gfid-access $(GLUPY_SUBDIR) qemu-block # trash path-converter # filter +          protect compress changelog gfid-access $(GLUPY_SUBDIR) qemu-block snapview-client snapview-server # trash path-converter # filter  CLEANFILES = diff --git a/xlators/features/quota/src/quota.c b/xlators/features/quota/src/quota.c index 913741e0fda..c3e730bd1fd 100644 --- a/xlators/features/quota/src/quota.c +++ b/xlators/features/quota/src/quota.c @@ -1013,7 +1013,8 @@ quota_fill_inodectx (xlator_t *this, inode_t *inode, dict_t *dict,                          goto unlock;                  } -                if (loc->name == NULL) +                /* do nothing if it is a nameless lookup */ +                if (loc->name == NULL || !loc->parent)                          goto unlock;                  list_for_each_entry (dentry, &ctx->parents, next) { diff --git a/xlators/features/snapview-client/Makefile.am b/xlators/features/snapview-client/Makefile.am new file mode 100644 index 00000000000..af437a64d6d --- /dev/null +++ b/xlators/features/snapview-client/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = src diff --git a/xlators/features/snapview-client/src/Makefile.am b/xlators/features/snapview-client/src/Makefile.am new file mode 100644 index 00000000000..8a3f6fe4ea2 --- /dev/null +++ b/xlators/features/snapview-client/src/Makefile.am @@ -0,0 +1,15 @@ +xlator_LTLIBRARIES = snapview-client.la +xlatordir = $(libdir)/glusterfs/$(PACKAGE_VERSION)/xlator/features + +snapview_client_la_LDFLAGS = -module -avoid-version + +snapview_client_la_SOURCES = snapview-client.c +snapview_client_la_LIBADD = $(top_builddir)/libglusterfs/src/libglusterfs.la + +noinst_HEADERS = snapview-client.h snapview-client-mem-types.h + +AM_CPPFLAGS = $(GF_CPPFLAGS) -I$(top_srcdir)/libglusterfs/src + +AM_CFLAGS = -Wall $(GF_CFLAGS) + +CLEANFILES = diff --git a/xlators/features/snapview-client/src/snapview-client-mem-types.h b/xlators/features/snapview-client/src/snapview-client-mem-types.h new file mode 100644 index 00000000000..b6fa0ef7cfb --- /dev/null +++ b/xlators/features/snapview-client/src/snapview-client-mem-types.h @@ -0,0 +1,23 @@ +/* +   Copyright (c) 2014 Red Hat, Inc. <http://www.redhat.com> +   This file is part of GlusterFS. + +   This file is licensed to you under your choice of the GNU Lesser +   General Public License, version 3 or any later version (LGPLv3 or +   later), or the GNU General Public License, version 2 (GPLv2), in all +   cases as published by the Free Software Foundation. +*/ + +#ifndef _SVC_MEM_TYPES_H +#define _SVC_MEM_TYPES_H + +#include "mem-types.h" + +enum svc_mem_types { +        gf_svc_mt_svc_private_t = gf_common_mt_end + 1, +        gf_svc_mt_svc_local_t, +        gf_svc_mt_svc_inode_t, +        gf_svc_mt_end +}; + +#endif diff --git a/xlators/features/snapview-client/src/snapview-client.c b/xlators/features/snapview-client/src/snapview-client.c new file mode 100644 index 00000000000..a86339901b9 --- /dev/null +++ b/xlators/features/snapview-client/src/snapview-client.c @@ -0,0 +1,1687 @@ +/* +   Copyright (c) 2014 Red Hat, Inc. <http://www.redhat.com> +   This file is part of GlusterFS. + +   This file is licensed to you under your choice of the GNU Lesser +   General Public License, version 3 or any later version (LGPLv3 or +   later), or the GNU General Public License, version 2 (GPLv2), in all +   cases as published by the Free Software Foundation. +*/ + +#ifndef _CONFIG_H +#define _CONFIG_H +#include "config.h" +#endif + +#include "snapview-client.h" +#include "inode.h" +#include "byte-order.h" + + +void +svc_local_free (svc_local_t *local) +{ +        if (local) +                loc_wipe (&local->loc); +} + +xlator_t * +svc_get_subvolume (xlator_t *this, int inode_type) +{ +        xlator_t *subvolume = NULL; + +        GF_VALIDATE_OR_GOTO ("snapview-client", this, out); + +        if (inode_type == VIRTUAL_INODE) +                subvolume = SECOND_CHILD (this); +        else +                subvolume = FIRST_CHILD (this); + +out: +        return subvolume; +} + +int32_t +__svc_inode_ctx_set (xlator_t *this, inode_t *inode, int inode_type) +{ +        uint64_t    value = 0; +        int32_t       ret = -1; + +        GF_VALIDATE_OR_GOTO ("snapview-client", this, out); +        GF_VALIDATE_OR_GOTO (this->name, inode, out); + +        value = inode_type; + +        ret = __inode_ctx_set (inode, this, &value); + +out: +        return ret; +} + +int +__svc_inode_ctx_get (xlator_t *this, inode_t *inode, int *inode_type) +{ +        uint64_t    value      = 0; +        int         ret        = -1; + +        GF_VALIDATE_OR_GOTO ("snapview-client", this, out); +        GF_VALIDATE_OR_GOTO (this->name, inode, out); + +        ret = __inode_ctx_get (inode, this, &value); +        if (ret < 0) +                goto out; + +        *inode_type = (int)(value); + +out: +        return ret; +} + +int +svc_inode_ctx_get (xlator_t *this, inode_t *inode, int *inode_type) +{ +        int          ret        = -1; + +        GF_VALIDATE_OR_GOTO ("snapview-client", this, out); +        GF_VALIDATE_OR_GOTO (this->name, inode, out); + +        LOCK (&inode->lock); +        { +                ret = __svc_inode_ctx_get (this, inode, inode_type); +        } +        UNLOCK (&inode->lock); + +out: +        return ret; +} + +int32_t +svc_inode_ctx_set (xlator_t *this, inode_t *inode, int inode_type) +{ +        int32_t   ret = -1; + +        GF_VALIDATE_OR_GOTO ("snapview-client", this, out); +        GF_VALIDATE_OR_GOTO (this->name, inode, out); + +        LOCK (&inode->lock); +        { +                ret = __svc_inode_ctx_set (this, inode, inode_type); +        } +        UNLOCK (&inode->lock); + +out: +        return ret; +} + +int32_t +svc_lookup_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +                int32_t op_ret, int32_t op_errno, inode_t *inode, +                struct iatt *buf, dict_t *xdata, struct iatt *postparent) +{ +        svc_local_t     *local       = NULL; +        inode_t         *parent      = NULL; +        xlator_t        *subvolume   = NULL; +        gf_boolean_t     do_unwind   = _gf_true; +        int              inode_type  = -1; +        int              parent_type = -1; +        int              ret         = -1; + +        local = frame->local; +        subvolume = local->subvolume; +        if (!subvolume) { +                gf_log_callingfn (this->name, GF_LOG_ERROR, "path: %s, " +                                  "gfid: %s ", local->loc.path, +                                  inode?uuid_utoa (inode->gfid):""); +                GF_ASSERT (0); +        } + +        /* There is a possibility that, the client process just came online +           and does not have the inode on which the lookup came. In that case, +           the fresh inode created from fuse for the lookup fop, wont have +           the inode context set without which svc cannot decide where to +           STACK_WIND to. So by default it decides to send the fop to the +           regular subvolume (i.e first child of the xlator). If lookup fails +           on the regular volume, then there is a possibility that the lookup +           is happening on a virtual inode (i.e history data residing in snaps). +           So if lookup fails with ENOENT and the inode context is not there, +           then send the lookup to the 2nd child of svc. +        */ +        ret = svc_inode_ctx_get (this, inode, &inode_type); +        if (op_ret) { +                if (op_errno == ENOENT && (ret < 0) && +                    !uuid_is_null (local->loc.gfid) && +                    !__is_root_gfid (local->loc.gfid)) { +                        if (subvolume == FIRST_CHILD (this)) { +                                subvolume = SECOND_CHILD (this); +                                STACK_WIND (frame, svc_lookup_cbk, subvolume, +                                            subvolume->fops->lookup, +                                            &local->loc, xdata); +                                do_unwind = _gf_false; +                        } +                } +                goto out; +        } + +        if (local->loc.parent) +                parent = inode_ref (local->loc.parent); +        else { +                parent = inode_parent (inode, NULL, NULL); +                if (!parent && !uuid_is_null (local->loc.pargfid)) { +                        parent = inode_find (inode->table, +                                             local->loc.pargfid); +                } +        } + +        if (!__is_root_gfid (buf->ia_gfid) && parent) { +                ret = svc_inode_ctx_get (this, parent, &parent_type); +                if (ret < 0) { +                        op_ret = -1; +                        op_errno = EINVAL; +                        goto out; +                } +        } + +        if (subvolume == FIRST_CHILD (this)) +                inode_type = NORMAL_INODE; +        else +                inode_type = VIRTUAL_INODE; + +        ret = svc_inode_ctx_set (this, inode, inode_type); +        if (ret) +                gf_log (this->name, GF_LOG_ERROR, "failed to set inode type" +                        "into the context"); + +out: +        if (do_unwind) { +                SVC_STACK_UNWIND (lookup, frame, op_ret, op_errno, inode, buf, +                                  xdata, postparent); +        } + +        if (parent) +                inode_unref (parent); + +        return 0; +} + +int32_t +svc_lookup (call_frame_t *frame, xlator_t *this, loc_t *loc, +            dict_t *xdata) +{ +        int32_t        ret         =   -1; +        svc_local_t   *local       = NULL; +        xlator_t      *subvolume   = NULL; +        int            op_ret      = -1; +        int            op_errno    = EINVAL; +        inode_t       *parent      = NULL; +        svc_private_t *priv        = NULL; +        dict_t        *new_xdata   = NULL; +        int            inode_type  = -1; +        int            parent_type = -1; +        gf_boolean_t   wind        = _gf_false; + +        GF_VALIDATE_OR_GOTO ("svc", this, out); +        GF_VALIDATE_OR_GOTO (this->name, this->private, out); +        GF_VALIDATE_OR_GOTO (this->name, frame, out); +        GF_VALIDATE_OR_GOTO (this->name, loc, out); +        GF_VALIDATE_OR_GOTO (this->name, loc->inode, out); + +        priv = this->private; + +        ret = svc_inode_ctx_get (this, loc->inode, &inode_type); +        if (!__is_root_gfid (loc->gfid)) { +                if (loc->parent) { +                        parent = inode_ref (loc->parent); +                        ret = svc_inode_ctx_get (this, loc->parent, +                                                 &parent_type); +                } else { +                        parent = inode_parent (loc->inode, loc->pargfid, NULL); +                        if (parent) +                                ret = svc_inode_ctx_get (this, parent, +                                                         &parent_type); +                } +        } + +        local = mem_get0 (this->local_pool); +        if (!local) { +                gf_log (this->name, GF_LOG_ERROR, "failed to allocate local"); +                op_ret = -1; +                op_errno = ENOMEM; +                goto out; +        } + +        frame->local = local; +        loc_copy (&local->loc, loc); + +        if (__is_root_gfid (loc->inode->gfid)) { +                subvolume = FIRST_CHILD (this); +                GF_ASSERT (subvolume); +                local->subvolume = subvolume; +                wind = _gf_true; +                goto out; +        } + +        /* nfs sends nameless lookups directly using the gfid. In that case +           loc->name will be NULL. So check if loc->name is NULL. If so, then +           try to get the subvolume using inode context. But if the inode has +           not been looked up yet, then send the lookup call to the first +           subvolume. +        */ + +        if (!loc->name) { +                if (uuid_is_null (loc->inode->gfid)) { +                        subvolume = FIRST_CHILD (this); +                        local->subvolume = subvolume; +                        wind = _gf_true; +                        goto out; +                } else { +                        if (inode_type >= 0) +                                subvolume = svc_get_subvolume (this, +                                                               inode_type); +                        else +                                subvolume = FIRST_CHILD (this); +                        local->subvolume = subvolume; +                        wind = _gf_true; +                        goto out; +                } +        } + +        if (strcmp (loc->name, priv->path)) { +                if (parent_type == NORMAL_INODE) { +                        subvolume = FIRST_CHILD (this); +                        local->subvolume = subvolume; +                } else { +                        subvolume = SECOND_CHILD (this); +                        local->subvolume = subvolume; +                } +        } else { +                if (parent_type == NORMAL_INODE) { +                        subvolume = SECOND_CHILD (this); +                        local->subvolume = subvolume; +                        /* Indication of whether the lookup is happening on the +                           entry point or not, to the snapview-server. +                        */ +                        SVC_ENTRY_POINT_SET (this, xdata, op_ret, op_errno, +                                             new_xdata, priv, ret, out); +                } else { +                        /* Either error can be sent to application as +                           the entry point directory can exist only within +                           real directories and here the parent is a virtual +                           directory or send the call all the way to svs and +                           let it send the error back. For now it is sending +                           the error to application itself. (Saves the +                           network latency) +                        */ +                        op_ret = -1; +                        op_errno = ENOENT; +                        goto out; +                } +        } + +        wind = _gf_true; + +out: +        if (wind) +                STACK_WIND (frame, svc_lookup_cbk, +                             subvolume, subvolume->fops->lookup, loc, xdata); +        else +                SVC_STACK_UNWIND (lookup, frame, op_ret, op_errno, NULL, +                                  NULL, NULL, NULL); +        if (new_xdata) +                dict_unref (new_xdata); + +        if (parent) +                inode_unref (parent); + +        return 0; +} + +/* should all the fops be handled like lookup is supposed to be +   handled? i.e just based on inode type decide where the call should +   be sent and in the call back update the contexts. +*/ +int32_t +svc_stat (call_frame_t *frame, xlator_t *this, loc_t *loc, +          dict_t *xdata) +{ +        int32_t      ret        = -1; +        int          inode_type = -1; +        xlator_t    *subvolume  = NULL; +        int32_t      op_ret     = -1; +        int32_t      op_errno   = EINVAL; +        gf_boolean_t wind       = _gf_false; + +        GF_VALIDATE_OR_GOTO ("svc", this, out); +        GF_VALIDATE_OR_GOTO (this->name, frame, out); +        GF_VALIDATE_OR_GOTO (this->name, loc, out); +        GF_VALIDATE_OR_GOTO (this->name, loc->inode, out); + +        SVC_GET_SUBVOL_FROM_CTX (this, op_ret, op_errno, inode_type, ret, +                                 loc->inode, subvolume, out); + +        STACK_WIND_TAIL (frame,subvolume, subvolume->fops->stat, loc, xdata); + +        wind = _gf_true; + +out: +        if (!wind) +                SVC_STACK_UNWIND (stat, frame, op_ret, op_errno, +                                  NULL, NULL); +        return 0; +} + +int32_t +svc_fstat (call_frame_t *frame, xlator_t *this, fd_t *fd, dict_t *xdata) +{ +        int32_t      ret        = -1; +        int          inode_type = -1; +        xlator_t    *subvolume  = NULL; +        int32_t      op_ret     = -1; +        int32_t      op_errno   = EINVAL; +        gf_boolean_t wind       = _gf_false; + +        GF_VALIDATE_OR_GOTO ("svc", this, out); +        GF_VALIDATE_OR_GOTO (this->name, frame, out); +        GF_VALIDATE_OR_GOTO (this->name, fd, out); +        GF_VALIDATE_OR_GOTO (this->name, fd->inode, out); + +        SVC_GET_SUBVOL_FROM_CTX (this, op_ret, op_errno, inode_type, ret, +                                 fd->inode, subvolume, out); + +        STACK_WIND_TAIL (frame, subvolume, subvolume->fops->fstat, fd, xdata); + +        wind = _gf_true; + +out: +        if (!wind) +                SVC_STACK_UNWIND (fstat, frame, op_ret, op_errno, NULL, NULL); + +        return ret; +} + +/* If the inode represents a directory which is actually +   present in a snapshot, then opendir on that directory +   should be sent to the snap-view-server which opens +   the directory in the corresponding graph. +   In fact any opendir call on a virtual directory +   should be sent to svs. Because if it fakes success +   here, then later when readdir on that fd comes, there +   will not be any corresponding fd opened on svs and +   svc has to do things that open-behind is doing. +*/ +int32_t +svc_opendir (call_frame_t *frame, xlator_t *this, loc_t *loc, fd_t *fd, +             dict_t *xdata) +{ +        int32_t        ret        = -1; +        int            inode_type = -1; +        xlator_t      *subvolume  = NULL; +        int            op_ret     = -1; +        int            op_errno   = EINVAL; +        gf_boolean_t   wind       = _gf_false; + +        GF_VALIDATE_OR_GOTO ("svc", this, out); +        GF_VALIDATE_OR_GOTO (this->name, frame, out); +        GF_VALIDATE_OR_GOTO (this->name, loc, out); +        GF_VALIDATE_OR_GOTO (this->name, loc->inode, out); +        GF_VALIDATE_OR_GOTO (this->name, fd, out); + +        SVC_GET_SUBVOL_FROM_CTX (this, op_ret, op_errno, inode_type, ret, +                                 loc->inode, subvolume, out); + +        STACK_WIND_TAIL (frame, subvolume, subvolume->fops->opendir, loc, fd, +                         xdata); + +        wind = _gf_true; + +out: +        if (!wind) +                SVC_STACK_UNWIND (opendir, frame, op_ret, op_errno, NULL, NULL); + +        return 0; +} + +int32_t +svc_setattr (call_frame_t *frame, xlator_t *this, loc_t *loc, +             struct iatt *stbuf, int32_t valid, dict_t *xdata) +{ +        int32_t      ret        = -1; +        int          inode_type = -1; +        int          op_ret     = -1; +        int          op_errno   = EINVAL; +        gf_boolean_t wind       = _gf_false; + +        GF_VALIDATE_OR_GOTO ("svc", this, out); +        GF_VALIDATE_OR_GOTO (this->name, frame, out); +        GF_VALIDATE_OR_GOTO (this->name, loc, out); +        GF_VALIDATE_OR_GOTO (this->name, loc->inode, out); + +        ret = svc_inode_ctx_get (this, loc->inode, &inode_type); +        if (ret < 0) { +                op_ret = -1; +                op_errno = EINVAL; +                gf_log (this->name, GF_LOG_ERROR, "failed to get the inode " +                        "context for %s (gfid: %s)", loc->path, +                        uuid_utoa (loc->inode->gfid)); +                goto out; +        } + +        if (inode_type == NORMAL_INODE) { +                STACK_WIND_TAIL (frame, FIRST_CHILD (this), +                                 FIRST_CHILD (this)->fops->setattr, loc, stbuf, +                                 valid, xdata); +        } else { +                op_ret = -1; +                op_errno = EPERM; +                goto out; +        } + +        wind = _gf_true; + +out: +        if (!wind) +                SVC_STACK_UNWIND (setattr, frame, op_ret, op_errno, +                                  NULL, NULL, NULL); +        return 0; +} + +int32_t +svc_fsetattr (call_frame_t *frame, xlator_t *this, fd_t *fd, struct iatt *stbuf, +             int32_t valid, dict_t *xdata) +{ +        int32_t      ret        = -1; +        int          inode_type = -1; +        int          op_ret     = -1; +        int          op_errno   = EINVAL; +        gf_boolean_t wind       = _gf_false; + +        GF_VALIDATE_OR_GOTO ("svc", this, out); +        GF_VALIDATE_OR_GOTO (this->name, frame, out); +        GF_VALIDATE_OR_GOTO (this->name, fd, out); +        GF_VALIDATE_OR_GOTO (this->name, fd->inode, out); + +        ret = svc_inode_ctx_get (this, fd->inode, &inode_type); +        if (ret < 0) { +                gf_log (this->name, GF_LOG_ERROR, "failed to get the inode " +                        "context for %s", uuid_utoa (fd->inode->gfid)); +                op_ret = -1; +                op_errno = EINVAL; +                goto out; +        } + +        if (inode_type == NORMAL_INODE) { +                STACK_WIND_TAIL (frame, FIRST_CHILD (this), +                                 FIRST_CHILD (this)->fops->fsetattr, fd, stbuf, +                                 valid, xdata); +        } else { +                op_ret = -1; +                op_errno = EPERM; +                goto out; +        } + +        wind = _gf_true; + +out: +        if (!wind) +                SVC_STACK_UNWIND (fsetattr, frame, op_ret, op_errno, +                                  NULL, NULL, NULL); +        return 0; +} + +int32_t +svc_getxattr (call_frame_t *frame, xlator_t *this, loc_t *loc, const char *name, +              dict_t *xdata) +{ +        int32_t        ret        = -1; +        int            inode_type = -1; +        xlator_t      *subvolume  = NULL; +        int            op_ret     = -1; +        int            op_errno   = EINVAL; +        gf_boolean_t   wind       = _gf_false; + +        GF_VALIDATE_OR_GOTO ("svc", this, out); +        GF_VALIDATE_OR_GOTO (this->name, frame, out); +        GF_VALIDATE_OR_GOTO (this->name, loc, out); +        GF_VALIDATE_OR_GOTO (this->name, loc->inode, out); + +        SVC_GET_SUBVOL_FROM_CTX (this, op_ret, op_errno, inode_type, ret, +                                 loc->inode, subvolume, out); + +        STACK_WIND_TAIL (frame, subvolume, subvolume->fops->getxattr, loc, name, +                         xdata); + +        wind = _gf_true; + +out: +        if (!wind) +                SVC_STACK_UNWIND (getxattr, frame, op_ret, op_errno, +                                  NULL, NULL); + +        return 0; +} + +int32_t +svc_fgetxattr (call_frame_t *frame, xlator_t *this, fd_t *fd, const char *name, +              dict_t *xdata) +{ +        int32_t       ret        = -1; +        int           inode_type = -1; +        xlator_t     *subvolume  = NULL; +        gf_boolean_t  wind       = _gf_false; +        int           op_ret     = -1; +        int           op_errno   = EINVAL; + +        GF_VALIDATE_OR_GOTO ("svc", this, out); +        GF_VALIDATE_OR_GOTO (this->name, frame, out); +        GF_VALIDATE_OR_GOTO (this->name, fd, out); +        GF_VALIDATE_OR_GOTO (this->name, fd->inode, out); + +        SVC_GET_SUBVOL_FROM_CTX (this, op_ret, op_errno, inode_type, ret, +                                 fd->inode, subvolume, out); + +        STACK_WIND_TAIL (frame, subvolume, +                         subvolume->fops->fgetxattr, fd, name, xdata); + +        wind = _gf_true; + +out: +        if (!wind) +                SVC_STACK_UNWIND (fgetxattr, frame, op_ret, op_errno, +                                  NULL, NULL); +        return 0; +} + +int32_t +svc_setxattr (call_frame_t *frame, xlator_t *this, loc_t *loc, dict_t *dict, +              int32_t flags, dict_t *xdata) +{ +        int32_t      ret         = -1; +        int          inode_type  = -1; +        int          op_ret      = -1; +        int          op_errno    = EINVAL; +        gf_boolean_t wind        = _gf_false; + +        GF_VALIDATE_OR_GOTO ("svc", this, out); +        GF_VALIDATE_OR_GOTO (this->name, frame, out); +        GF_VALIDATE_OR_GOTO (this->name, loc, out); +        GF_VALIDATE_OR_GOTO (this->name, loc->inode, out); + +        ret = svc_inode_ctx_get (this, loc->inode, &inode_type); +        if (ret < 0) { +                gf_log (this->name, GF_LOG_ERROR, "failed to get inode context " +                        "for %s (gfid: %s)", loc->name, +                        uuid_utoa (loc->inode->gfid)); +                op_ret = -1; +                op_errno = EINVAL; +                goto out; +        } + +        if (inode_type == NORMAL_INODE) { +                STACK_WIND_TAIL (frame, FIRST_CHILD (this), +                                 FIRST_CHILD (this)->fops->setxattr, loc, dict, +                                 flags, xdata); +        } else { +                op_ret = -1; +                op_errno = EPERM; +                goto out; +        } + +        wind = _gf_true; + +out: +        if (!wind) +                SVC_STACK_UNWIND (setxattr, frame, op_ret, op_errno, +                                  NULL); + +        return 0; +} + +int32_t +svc_fsetxattr (call_frame_t *frame, xlator_t *this, fd_t *fd, dict_t *dict, +              int32_t flags, dict_t *xdata) +{ +        int32_t      ret        = -1; +        int          inode_type = -1; +        int          op_ret     = -1; +        int          op_errno   = EINVAL; +        gf_boolean_t wind       = _gf_false; + +        GF_VALIDATE_OR_GOTO ("svc", this, out); +        GF_VALIDATE_OR_GOTO (this->name, frame, out); +        GF_VALIDATE_OR_GOTO (this->name, fd, out); +        GF_VALIDATE_OR_GOTO (this->name, fd->inode, out); + +        ret = svc_inode_ctx_get (this, fd->inode, &inode_type); +        if (ret < 0) { +                gf_log (this->name, GF_LOG_ERROR, "failed to get inode context " +                        "for %s", uuid_utoa (fd->inode->gfid)); +                op_ret = -1; +                op_errno = EINVAL; +                goto out; +        } + +        if (inode_type == NORMAL_INODE) { +                STACK_WIND_TAIL (frame, FIRST_CHILD (this), +                                 FIRST_CHILD (this)->fops->fsetxattr, fd, dict, +                                 flags, xdata); +        } else { +                op_ret = -1; +                op_errno = EPERM; +                goto out; +        } + +        wind = _gf_true; + +out: +        if (!wind) +                STACK_UNWIND_STRICT (fsetxattr, frame, op_ret, op_errno, +                                     NULL); + +        return 0; +} + +int32_t +svc_rmdir (call_frame_t *frame, xlator_t *this, loc_t *loc, int flags, +           dict_t *xdata) +{ +        int          inode_type = -1; +        int          ret        = -1; +        int          op_ret     = -1; +        int          op_errno   = EINVAL; +        gf_boolean_t wind       = _gf_false; + +        GF_VALIDATE_OR_GOTO ("svc", this, out); +        GF_VALIDATE_OR_GOTO (this->name, frame, out); +        GF_VALIDATE_OR_GOTO (this->name, loc, out); +        GF_VALIDATE_OR_GOTO (this->name, loc->inode, out); + +        ret = svc_inode_ctx_get (this, loc->inode, &inode_type); +        if (ret < 0) { +                gf_log (this->name, GF_LOG_ERROR, "failed to get the inode " +                        "context for %s (gfid: %s)", loc->name, +                        uuid_utoa (loc->inode->gfid)); +                op_ret = -1; +                op_errno = EINVAL; +                goto out; +        } + +        if (inode_type == NORMAL_INODE) { +                STACK_WIND_TAIL (frame, FIRST_CHILD (this), +                                 FIRST_CHILD (this)->fops->rmdir, loc, flags, +                                 xdata); +        } else { +                op_ret = -1; +                op_errno = EPERM; +                goto out; +        } + +        wind = _gf_true; + +out: +        if (!wind) +                SVC_STACK_UNWIND (rmdir, frame, op_ret, op_errno, +                                  NULL, NULL, NULL); +        return 0; +} + +int32_t +svc_mkdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +               int32_t op_ret, int32_t op_errno, inode_t *inode, +               struct iatt *buf, struct iatt *preparent, +               struct iatt *postparent, dict_t *xdata) +{ +        int inode_type = -1; +        int ret        = -1; + +        if (op_ret < 0) +                goto out; + +        inode_type = NORMAL_INODE; +        ret = svc_inode_ctx_set (this, inode, inode_type); +        if (ret) +                gf_log (this->name, GF_LOG_ERROR, "failed to set inode " +                        "context"); + + +out: +        SVC_STACK_UNWIND (mkdir, frame, op_ret, op_errno, inode, +                          buf, preparent, postparent, xdata); +        return 0; +} + +int32_t +svc_mkdir (call_frame_t *frame, xlator_t *this, loc_t *loc, mode_t mode, +           mode_t umask, dict_t *xdata) +{ +        int            parent_type = -1; +        int            ret         = -1; +        int            op_ret      = -1; +        int            op_errno    = EINVAL; +        svc_private_t *priv        = NULL; +        gf_boolean_t   wind        = _gf_false; + +        GF_VALIDATE_OR_GOTO ("svc", this, out); +        GF_VALIDATE_OR_GOTO (this->name, this->private, out); +        GF_VALIDATE_OR_GOTO (this->name, frame, out); +        GF_VALIDATE_OR_GOTO (this->name, loc, out); +        GF_VALIDATE_OR_GOTO (this->name, loc->inode, out); + +        priv = this->private; + +        ret = svc_inode_ctx_get (this, loc->parent, &parent_type); +        if (ret < 0) { +                gf_log (this->name, GF_LOG_ERROR, "failed to get the inode " +                        "context for %s", uuid_utoa (loc->parent->gfid)); +                op_ret = -1; +                op_errno = EINVAL; +                goto out; +        } + +        if (strcmp (loc->name, priv->path) && parent_type == NORMAL_INODE) { +                STACK_WIND (frame, svc_mkdir_cbk, FIRST_CHILD (this), +                            FIRST_CHILD (this)->fops->mkdir, loc, mode, +                            umask, xdata); +        } else { +                op_ret = -1; +                op_errno = EPERM; +                goto out; +        } + +        wind = _gf_true; + +out: +        if (!wind) +                SVC_STACK_UNWIND (mkdir, frame, op_ret, op_errno, NULL, NULL, +                                  NULL, NULL, NULL); +        return 0; +} + +int32_t +svc_mknod_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +               int32_t op_ret, int32_t op_errno, inode_t *inode, +               struct iatt *buf, struct iatt *preparent, +               struct iatt *postparent, dict_t *xdata) +{ +        int inode_type = -1; +        int ret        = -1; + +        if (op_ret < 0) +                goto out; + +        inode_type = NORMAL_INODE; +        ret = svc_inode_ctx_set (this, inode, inode_type); +        if (ret) +                gf_log (this->name, GF_LOG_ERROR, "failed to set inode " +                        "context"); + +out: +        SVC_STACK_UNWIND (mknod, frame, op_ret, op_errno, inode, +                          buf, preparent, postparent, xdata); +        return 0; +} + +int32_t +svc_mknod (call_frame_t *frame, xlator_t *this, loc_t *loc, mode_t mode, +           dev_t rdev, mode_t umask, dict_t *xdata) +{ +        int            parent_type = -1; +        int            ret         = -1; +        int            op_ret      = -1; +        int            op_errno    = EINVAL; +        svc_private_t *priv        = NULL; +        gf_boolean_t   wind        = _gf_false; + +        GF_VALIDATE_OR_GOTO ("svc", this, out); +        GF_VALIDATE_OR_GOTO (this->name, this->private, out); +        GF_VALIDATE_OR_GOTO (this->name, frame, out); +        GF_VALIDATE_OR_GOTO (this->name, loc, out); +        GF_VALIDATE_OR_GOTO (this->name, loc->inode, out); + +        priv = this->private; + +        ret = svc_inode_ctx_get (this, loc->parent, &parent_type); +        if (ret < 0) { +                gf_log (this->name, GF_LOG_ERROR, "failed to get the inode " +                        "context for %s", uuid_utoa (loc->parent->gfid)); +                op_ret = -1; +                op_errno = EINVAL; +                goto out; +        } + +        if (strcmp (loc->name, priv->path) && parent_type == NORMAL_INODE) { +                STACK_WIND (frame, svc_mknod_cbk, FIRST_CHILD (this), +                            FIRST_CHILD (this)->fops->mknod, loc, mode, +                            rdev, umask, xdata); +        } else { +                op_ret = -1; +                op_errno = EPERM; +                goto out; +        } + +        wind = _gf_true; + +out: +        if (!wind) +                SVC_STACK_UNWIND (mknod, frame, op_ret, op_errno, NULL, NULL, +                                  NULL, NULL, NULL); +        return 0; +} + +/* If the flags of the open call contain O_WRONLY or O_RDWR and the inode is +   a virtual inode, then unwind the call back with EPERM. Otherwise simply +   STACK_WIND the call to the first child of svc xlator. +*/ +int32_t +svc_open (call_frame_t *frame, xlator_t *this, loc_t *loc, int32_t flags, +          fd_t *fd, dict_t *xdata) +{ +        xlator_t    *subvolume  = NULL; +        int          inode_type = -1; +        int          op_ret     = -1; +        int          op_errno   = EINVAL; +        int          ret        = -1; +        gf_boolean_t wind       = _gf_false; + +        GF_VALIDATE_OR_GOTO ("svc", this, out); +        GF_VALIDATE_OR_GOTO (this->name, frame, out); +        GF_VALIDATE_OR_GOTO (this->name, loc, out); +        GF_VALIDATE_OR_GOTO (this->name, loc->inode, out); +        GF_VALIDATE_OR_GOTO (this->name, fd, out); + +        /* Another way is to STACK_WIND to normal subvolume, if inode +           type is not there in the context. If the file actually resides +           in snapshots, then ENOENT would be returned. Needs more analysis. +        */ +        SVC_GET_SUBVOL_FROM_CTX (this, op_ret, op_errno, inode_type, ret, +                                 loc->inode, subvolume, out); + +        if (((flags & O_ACCMODE) == O_WRONLY) || +            ((flags & O_ACCMODE) == O_RDWR)) { +                if (subvolume != FIRST_CHILD (this)) { +                        op_ret = -1; +                        op_errno = EINVAL; +                        goto out; +                } +        } + +        STACK_WIND_TAIL (frame, subvolume, subvolume->fops->open, loc, +                         flags, fd, xdata); + +        wind = _gf_true; + +out: +        if (!wind) +                SVC_STACK_UNWIND (open, frame, op_ret, op_errno, NULL, +                                  NULL); +        return 0; +} + +int32_t +svc_create_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +                int32_t op_ret, int32_t op_errno, fd_t *fd, inode_t *inode, +                struct iatt *stbuf, struct iatt *preparent, +                struct iatt *postparent, dict_t *xdata) +{ +        int  inode_type = -1; +        int  ret        = -1; + +        if (op_ret < 0) +                goto out; + +        inode_type = NORMAL_INODE; +        ret = svc_inode_ctx_set (this, inode, inode_type); +        if (ret) +                gf_log (this->name, GF_LOG_ERROR, "failed to set inode " +                        "context"); + +out: +        SVC_STACK_UNWIND (create, frame, op_ret, op_errno, fd, +                          inode, stbuf, preparent, postparent, xdata); + +        return 0; +} + +int32_t +svc_create (call_frame_t *frame, xlator_t *this, +            loc_t *loc, int32_t flags, mode_t mode, +            mode_t umask, fd_t *fd, dict_t *xdata) +{ +        int            parent_type = -1; +        int            ret         = -1; +        int            op_ret      = -1; +        int            op_errno    = EINVAL; +        svc_private_t *priv        = NULL; +        gf_boolean_t   wind        = _gf_false; + +        GF_VALIDATE_OR_GOTO ("svc", this, out); +        GF_VALIDATE_OR_GOTO (this->name, this->private, out); +        GF_VALIDATE_OR_GOTO (this->name, frame, out); +        GF_VALIDATE_OR_GOTO (this->name, loc, out); +        GF_VALIDATE_OR_GOTO (this->name, loc->inode, out); +        GF_VALIDATE_OR_GOTO (this->name, fd, out); + +        priv = this->private; + +        ret = svc_inode_ctx_get (this, loc->parent, &parent_type); +        if (ret < 0) { +                gf_log (this->name, GF_LOG_ERROR, "failed to get the inode " +                        "context for %s", uuid_utoa (loc->parent->gfid)); +                op_ret = -1; +                op_errno = EINVAL; +                goto out; +        } + +        if (strcmp (loc->name, priv->path) && parent_type == NORMAL_INODE) { +                STACK_WIND (frame, svc_create_cbk, FIRST_CHILD (this), +                            FIRST_CHILD (this)->fops->create, loc, flags, +                            mode, umask, fd, xdata); +        } else { +                op_ret = -1; +                op_errno = EPERM; +                goto out; +        } + +        wind = _gf_true; + +out: +        if (!wind) +                SVC_STACK_UNWIND (create, frame, op_ret, op_errno, +                                  NULL, NULL, NULL, NULL, NULL, NULL); +        return 0; +} + +int32_t +svc_symlink_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +                     int32_t op_ret, int32_t op_errno, inode_t *inode, +                     struct iatt *buf, struct iatt *preparent, +                     struct iatt *postparent, dict_t *xdata) +{ +        int inode_type = -1; +        int ret        = -1; + +        if (op_ret < 0) +                goto out; + +        inode_type = NORMAL_INODE; +        ret = svc_inode_ctx_set (this, inode, inode_type); +        if (ret) +                gf_log (this->name, GF_LOG_ERROR, "failed to set inode " +                        "context"); + +out: +        SVC_STACK_UNWIND (symlink, frame, op_ret, op_errno, inode, +                          buf, preparent, postparent, xdata); + +        return 0; +} + +int32_t +svc_symlink (call_frame_t *frame, xlator_t *this, const char *linkpath, +             loc_t *loc, mode_t umask, dict_t *xdata) +{ +        int            parent_type = -1; +        int            op_ret      = -1; +        int            op_errno    = EINVAL; +        int            ret         = -1; +        svc_private_t *priv        = NULL; +        gf_boolean_t   wind        = _gf_false; + +        GF_VALIDATE_OR_GOTO ("svc", this, out); +        GF_VALIDATE_OR_GOTO (this->name, frame, out); +        GF_VALIDATE_OR_GOTO (this->name, this->private, out); +        GF_VALIDATE_OR_GOTO (this->name, loc, out); +        GF_VALIDATE_OR_GOTO (this->name, loc->inode, out); + +        priv = this->private; + +        ret = svc_inode_ctx_get (this, loc->parent, &parent_type); +        if (ret < 0) { +                gf_log (this->name, GF_LOG_ERROR, "failed to get the inode " +                        "context for %s", uuid_utoa (loc->parent->gfid)); +                op_ret = -1; +                op_errno = EINVAL; +                goto out; +        } + +        if (strcmp (loc->name, priv->path) && parent_type == NORMAL_INODE) { +                STACK_WIND (frame, svc_symlink_cbk, FIRST_CHILD (this), +                            FIRST_CHILD (this)->fops->symlink, linkpath, loc, +                            umask, xdata); +        } else { +                op_ret = -1; +                op_errno = EPERM; +                goto out; +        } + +        wind = _gf_true; + +out: +        if (!wind) +                SVC_STACK_UNWIND (symlink, frame, op_ret, op_errno, +                                  NULL, NULL, NULL, NULL, NULL); +        return 0; +} + +int32_t +svc_unlink (call_frame_t *frame, xlator_t *this, loc_t *loc, int flags, +            dict_t *xdata) +{ +        int            inode_type   = -1; +        int            op_ret       = -1; +        int            op_errno     = EINVAL; +        int            ret          = -1; +        gf_boolean_t   wind         = _gf_false; + +        GF_VALIDATE_OR_GOTO ("svc", this, out); +        GF_VALIDATE_OR_GOTO (this->name, frame, out); +        GF_VALIDATE_OR_GOTO (this->name, loc, out); +        GF_VALIDATE_OR_GOTO (this->name, loc->inode, out); + +        ret = svc_inode_ctx_get (this, loc->inode, &inode_type); +        if (ret < 0) { +                gf_log (this->name, GF_LOG_ERROR, "failed to get the inode " +                        "context for %s", uuid_utoa (loc->parent->gfid)); +                op_ret = -1; +                op_errno = EINVAL; +                goto out; +        } + +        if (inode_type == NORMAL_INODE) { +                STACK_WIND_TAIL (frame, FIRST_CHILD (this), +                                 FIRST_CHILD (this)->fops->unlink, loc, flags, +                                 xdata); +        } else { +                op_ret = -1; +                op_errno = EPERM; +                goto out; +        } + +        wind = _gf_true; + +out: +        if (!wind) +                SVC_STACK_UNWIND (unlink, frame, op_ret, op_errno, NULL, NULL, +                                  NULL); +        return 0; +} + +int32_t +svc_readv (call_frame_t *frame, xlator_t *this, fd_t *fd, size_t size, +           off_t offset, uint32_t flags, dict_t *xdata) +{ +        int           inode_type = -1; +        xlator_t     *subvolume  = NULL; +        int           ret        = -1; +        int           op_ret     = -1; +        int           op_errno   = EINVAL; +        gf_boolean_t  wind       = _gf_false; + +        GF_VALIDATE_OR_GOTO ("svc", this, out); +        GF_VALIDATE_OR_GOTO (this->name, frame, out); +        GF_VALIDATE_OR_GOTO (this->name, fd, out); +        GF_VALIDATE_OR_GOTO (this->name, fd->inode, out); + +        SVC_GET_SUBVOL_FROM_CTX (this, op_ret, op_errno, inode_type, ret, +                                 fd->inode, subvolume, out); + +        STACK_WIND_TAIL (frame, subvolume, subvolume->fops->readv, +                         fd, size, offset, flags, xdata); + +        wind = _gf_true; + +out: +        if (!wind) +                SVC_STACK_UNWIND (readv, frame, op_ret, op_errno, NULL, 0, NULL, +                                  NULL, NULL); +        return 0; +} + +int32_t +svc_readlink (call_frame_t *frame, xlator_t *this, +              loc_t *loc, size_t size, dict_t *xdata) +{ +        int              inode_type = -1; +        xlator_t        *subvolume  = NULL; +        int              ret        = -1; +        int              op_ret     = -1; +        int              op_errno   = EINVAL; +        gf_boolean_t     wind       = _gf_false; + +        GF_VALIDATE_OR_GOTO ("svc", this, out); +        GF_VALIDATE_OR_GOTO (this->name, frame, out); +        GF_VALIDATE_OR_GOTO (this->name, loc, out); +        GF_VALIDATE_OR_GOTO (this->name, loc->inode, out); + +        SVC_GET_SUBVOL_FROM_CTX (this, op_ret, op_errno, inode_type, ret, +                                 loc->inode, subvolume, out); + +        STACK_WIND_TAIL (frame, subvolume, subvolume->fops->readlink, loc, size, +                         xdata); + +        wind = _gf_true; + +out: +        if (!wind) +                STACK_UNWIND_STRICT (readlink, frame, op_ret, op_errno, NULL, NULL, +                                     NULL); +        return 0; +} + +int32_t +svc_access (call_frame_t *frame, xlator_t *this, loc_t *loc, int32_t mask, +            dict_t *xdata) +{ +        int            ret        = -1; +        int            inode_type = -1; +        xlator_t      *subvolume  = NULL; +        int            op_ret     = -1; +        int            op_errno   = EINVAL; +        gf_boolean_t   wind       = _gf_false; + +        GF_VALIDATE_OR_GOTO ("svc", this, out); +        GF_VALIDATE_OR_GOTO (this->name, frame, out); +        GF_VALIDATE_OR_GOTO (this->name, loc, out); +        GF_VALIDATE_OR_GOTO (this->name, loc->inode, out); + +        SVC_GET_SUBVOL_FROM_CTX (this, op_ret, op_errno, inode_type, ret, +                                 loc->inode, subvolume, out); + +        STACK_WIND_TAIL (frame, subvolume, subvolume->fops->access, loc, mask, +                         xdata); + +        wind = _gf_true; + +out: +        if (!wind) +                SVC_STACK_UNWIND (access, frame, op_ret, op_errno, NULL); + +        return 0; +} + +int32_t +svc_readdir (call_frame_t *frame, xlator_t *this, fd_t *fd, +             size_t size, off_t off, +             dict_t *xdata) +{ +        int           inode_type = -1; +        xlator_t     *subvolume  = NULL; +        int           ret        = -1; +        int           op_ret     = -1; +        int           op_errno   = EINVAL; +        gf_boolean_t  wind       = _gf_false; + +        GF_VALIDATE_OR_GOTO ("svc", this, out); +        GF_VALIDATE_OR_GOTO (this->name, frame, out); +        GF_VALIDATE_OR_GOTO (this->name, fd, out); +        GF_VALIDATE_OR_GOTO (this->name, fd->inode, out); + +        SVC_GET_SUBVOL_FROM_CTX (this, op_ret, op_errno, inode_type, ret, +                                 fd->inode, subvolume, out); + +        STACK_WIND_TAIL (frame, subvolume, subvolume->fops->readdir, fd, size, +                         off, xdata); + +        wind = _gf_true; + +out: +        if (!wind) +                SVC_STACK_UNWIND (readdir, frame, op_ret, op_errno, NULL, +                                  NULL); +        return 0; +} + +int32_t +svc_readdirp_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +                  int32_t op_ret, int32_t op_errno, +                  gf_dirent_t *entries, dict_t *xdata) +{ +        gf_dirent_t   *entry      = NULL; +        svc_local_t   *local      = NULL; +        gf_boolean_t   real       = _gf_true; +        int            inode_type = -1; +        int            ret        = -1; + +        if (op_ret < 0) +                goto out; + +        local = frame->local; +        frame->local = NULL; + +        if (local->subvolume == FIRST_CHILD (this)) +                real = _gf_true; +        else +                real = _gf_false; + +        list_for_each_entry (entry, &entries->list, list) { +                if (!entry->inode) +                        continue; + +                if (real) +                        inode_type = NORMAL_INODE; +                else +                        inode_type = VIRTUAL_INODE; + +                ret = svc_inode_ctx_set (this, entry->inode, inode_type); +                if (ret) +                        gf_log (this->name, GF_LOG_ERROR, "failed to set inode " +                                "context"); +        } + + +out: +        SVC_STACK_UNWIND (readdirp, frame, op_ret, op_errno, entries, xdata); + +        return 0; +} + +int32_t +svc_readdirp (call_frame_t *frame, xlator_t *this, fd_t *fd, +              size_t size, off_t off, +              dict_t *xdata) +{ +        int            inode_type = -1; +        xlator_t      *subvolume  = NULL; +        svc_local_t   *local      = NULL; +        int            ret        = -1; +        int            op_ret     = -1; +        int            op_errno   = EINVAL; +        gf_boolean_t   wind       = _gf_false; + +        GF_VALIDATE_OR_GOTO ("svc", this, out); +        GF_VALIDATE_OR_GOTO (this->name, frame, out); +        GF_VALIDATE_OR_GOTO (this->name, fd, out); +        GF_VALIDATE_OR_GOTO (this->name, fd->inode, out); + +        local = mem_get0 (this->local_pool); +        if (!local) { +                gf_log (this->name, GF_LOG_ERROR, "failed to allocate local"); +                goto out; +        } + +        SVC_GET_SUBVOL_FROM_CTX (this, op_ret, op_errno, inode_type, ret, +                                 fd->inode, subvolume, out); + +        local->subvolume = subvolume; +        frame->local = local; + +        STACK_WIND (frame, svc_readdirp_cbk, subvolume, +                    subvolume->fops->readdirp, fd, size, off, xdata); + +        wind = _gf_true; + +out: +        if (!wind) +                SVC_STACK_UNWIND (readdirp, frame, op_ret, op_errno, NULL, NULL); + +        return 0; +} + +/* Renaming the entries from or to snapshots is not allowed as the snapshots +   are read-only. +*/ +int32_t +svc_rename (call_frame_t *frame, xlator_t *this, loc_t *oldloc, +            loc_t *newloc, dict_t *xdata) +{ +        int          src_inode_type  = -1; +        int          dst_inode_type  = -1; +        int          dst_parent_type = -1; +        int32_t      op_ret          = -1; +        int32_t      op_errno        = 0; +        int32_t      ret             = -1; +        gf_boolean_t wind            = _gf_false; + +        GF_VALIDATE_OR_GOTO ("svc", this, out); +        GF_VALIDATE_OR_GOTO (this->name, frame, out); +        GF_VALIDATE_OR_GOTO (this->name, oldloc, out); +        GF_VALIDATE_OR_GOTO (this->name, oldloc->inode, out); +        GF_VALIDATE_OR_GOTO (this->name, newloc, out); + +        ret = svc_inode_ctx_get (this, oldloc->inode, &src_inode_type); +        if (ret < 0) { +                gf_log (this->name, GF_LOG_ERROR, "failed to get the inode " +                        "context for the inode %s", +                        uuid_utoa (oldloc->inode->gfid)); +                op_ret = -1; +                op_errno = EINVAL; +                goto out; +        } + +        if (src_inode_type == VIRTUAL_INODE) { +                gf_log (this->name, GF_LOG_ERROR, "rename happening on a entry" +                        " %s residing in snapshot", oldloc->name); +                op_ret = -1; +                op_errno = EPERM; +                goto out; +        } + +        if (newloc->inode) { +                ret = svc_inode_ctx_get (this, newloc->inode, &dst_inode_type); +                if (!ret && dst_inode_type == VIRTUAL_INODE) { +                        gf_log (this->name, GF_LOG_ERROR, "rename of %s " +                                "happening to a entry %s residing in snapshot", +                                oldloc->name, newloc->name); +                        op_ret = -1; +                        op_errno = EPERM; +                        goto out; +                } +        } + +        if (dst_inode_type < 0) { +                ret = svc_inode_ctx_get (this, newloc->parent, +                                         &dst_parent_type); +                if (!ret && dst_parent_type == VIRTUAL_INODE) { +                        gf_log (this->name, GF_LOG_ERROR, "rename of %s " +                                "happening to a entry %s residing in snapshot", +                                oldloc->name, newloc->name); +                        op_ret = -1; +                        op_errno = EPERM; +                        goto out; +                } +        } + +        STACK_WIND_TAIL (frame, FIRST_CHILD (this), +                         FIRST_CHILD (this)->fops->rename, oldloc, newloc, +                         xdata); + +        wind = _gf_true; + +out: +        if (!wind) +                SVC_STACK_UNWIND (rename, frame, op_ret, op_errno, NULL, +                                  NULL, NULL, NULL, NULL, NULL); +        return 0; +} + +/* Creating hardlinks for the files from the snapshot is not allowed as it +   will be equivalent of creating hardlinks across different filesystems. +   And so is vise versa. +*/ +int32_t +svc_link (call_frame_t *frame, xlator_t *this, loc_t *oldloc, loc_t *newloc, +          dict_t *xdata) +{ +        int          src_inode_type  = -1; +        int          dst_parent_type = -1; +        int32_t      op_ret          = -1; +        int32_t      op_errno        = 0; +        int32_t      ret             = -1; +        gf_boolean_t wind            = _gf_false; + +        GF_VALIDATE_OR_GOTO ("svc", this, out); +        GF_VALIDATE_OR_GOTO (this->name, frame, out); +        GF_VALIDATE_OR_GOTO (this->name, oldloc, out); +        GF_VALIDATE_OR_GOTO (this->name, oldloc->inode, out); +        GF_VALIDATE_OR_GOTO (this->name, newloc, out); + +        ret = svc_inode_ctx_get (this, oldloc->inode, &src_inode_type); +        if (!ret && src_inode_type == VIRTUAL_INODE) { +                gf_log (this->name, GF_LOG_ERROR, "rename happening on a entry" +                        " %s residing in snapshot", oldloc->name); +                op_ret = -1; +                op_errno = EPERM; +                goto out; +        } + +        ret = svc_inode_ctx_get (this, newloc->parent, &dst_parent_type); +        if (!ret && dst_parent_type == VIRTUAL_INODE) { +                gf_log (this->name, GF_LOG_ERROR, "rename of %s " +                        "happening to a entry %s residing in snapshot", +                        oldloc->name, newloc->name); +                op_ret = -1; +                op_errno = EPERM; +                goto out; +        } + +        STACK_WIND_TAIL (frame, FIRST_CHILD (this), +                         FIRST_CHILD (this)->fops->link, oldloc, newloc, xdata); + +        wind = _gf_true; + +out: +        if (!wind) +                SVC_STACK_UNWIND (link, frame, op_ret, op_errno, +                                  NULL, NULL, NULL, NULL, NULL); +        return 0; +} + +int32_t +svc_removexattr (call_frame_t *frame, xlator_t *this, loc_t *loc, +                 const char *name, dict_t *xdata) +{ +        int          ret        = -1; +        int          inode_type = -1; +        int          op_ret     = -1; +        int          op_errno   = EINVAL; +        gf_boolean_t wind       = _gf_false; + +        GF_VALIDATE_OR_GOTO ("svc", this, out); +        GF_VALIDATE_OR_GOTO (this->name, frame, out); +        GF_VALIDATE_OR_GOTO (this->name, loc, out); +        GF_VALIDATE_OR_GOTO (this->name, loc->inode, out); + +        ret = svc_inode_ctx_get (this, loc->inode, &inode_type); +        if (ret < 0) { +                gf_log (this->name, GF_LOG_ERROR, "failed to get te inode " +                        "context for %s (gfid: %s)", loc->path, +                        uuid_utoa (loc->inode->gfid)); +                op_ret = -1; +                op_errno = EINVAL; +                goto out; +        } + +        if (inode_type == NORMAL_INODE) { +                STACK_WIND_TAIL (frame, FIRST_CHILD (this), +                                 FIRST_CHILD (this)->fops->removexattr, loc, +                                 name, xdata); +        } else { +                op_ret = -1; +                op_errno = EPERM; +                goto out; +        } + +        wind = _gf_true; + +out: +        if (!wind) +                SVC_STACK_UNWIND (removexattr, frame, op_ret, op_errno, +                                  NULL); + +        return 0; +} + +int32_t +svc_flush (call_frame_t *frame, xlator_t *this, +           fd_t *fd, dict_t *xdata) +{ +        int32_t           op_ret     = -1; +        int32_t           op_errno   = 0; +        int               ret        = -1; +        int               inode_type = -1; +        xlator_t         *subvolume  = NULL; +        gf_boolean_t      wind       = _gf_false; + +        GF_VALIDATE_OR_GOTO ("svc", this, out); +        GF_VALIDATE_OR_GOTO (this->name, frame, out); +        GF_VALIDATE_OR_GOTO (this->name, fd, out); +        GF_VALIDATE_OR_GOTO (this->name, fd->inode, out); + +        SVC_GET_SUBVOL_FROM_CTX (this, op_ret, op_errno, inode_type, ret, +                                 fd->inode, subvolume, out); + +        STACK_WIND_TAIL (frame, subvolume, subvolume->fops->flush, fd, xdata); + +        wind = _gf_true; + +out: +        if (!wind) +                SVC_STACK_UNWIND (flush, frame, op_ret, op_errno, NULL); + +        return 0; +} + +int32_t +svc_forget (xlator_t *this, inode_t *inode) +{ +        int            ret      = -1; +        uint64_t       value    = 0; + +        GF_VALIDATE_OR_GOTO ("svc", this, out); +        GF_VALIDATE_OR_GOTO (this->name, inode, out); + +        ret = inode_ctx_del (inode, this, &value); +        if (ret) { +                gf_log (this->name, GF_LOG_ERROR, "failed to delete inode " +                        "context for %s", uuid_utoa (inode->gfid)); +                goto out; +        } + +out: +        return 0; +} + +int +reconfigure (xlator_t *this, dict_t *options) +{ +        svc_private_t  *priv = NULL; + +        priv = this->private; + +        GF_OPTION_RECONF ("snapshot-directory", priv->path, options, str, out); + +out: +        return 0; +} + +int32_t +mem_acct_init (xlator_t *this) +{ +        int32_t     ret = -1; + +        if (!this) +                return ret; + +        ret = xlator_mem_acct_init (this, gf_svc_mt_end + 1); + +        if (ret != 0) { +                gf_log (this->name, GF_LOG_WARNING, "Memory accounting" +                        " init failed"); +                return ret; +        } + +        return ret; +} + +int32_t +init (xlator_t *this) +{ +        svc_private_t *private  = NULL; +        int            ret      = -1; +        int            children = 0; +        xlator_list_t *xl       = NULL; + +        if (!this->children) { +                gf_log (this->name, GF_LOG_ERROR, +                        "configured without any child"); +                goto out; +        } + +        xl = this->children; +        while (xl) { +                children++; +                xl = xl->next; +        } + +        if (children != 2) { +                gf_log (this->name, GF_LOG_ERROR, "snap-view-client has got " +                        "%d subvolumes. It can have only 2 subvolumes.", +                        children); +                goto out; +        } + +        /* This can be the top of graph in certain cases */ +        if (!this->parents) { +                gf_log (this->name, GF_LOG_DEBUG, +                        "dangling volume. check volfile "); +        } + +        private = GF_CALLOC (1, sizeof (*private), gf_svc_mt_svc_private_t); +        if (!private) +                goto out; + +        GF_OPTION_INIT ("snapshot-directory", private->path, str, out); + +        this->private = private; +        this->local_pool = mem_pool_new (svc_local_t, 128); +        if (!this->local_pool) { +                gf_log (this->name, GF_LOG_ERROR, "could not get mem pool for " +                        "frame->local"); +                goto out; +        } + +        ret = 0; + +out: +        if (ret) +                GF_FREE (private); + +        return ret; +} + +void +fini (xlator_t *this) +{ +        svc_private_t *priv = NULL; + +        if (!this) +                return; + +        priv = this->private; +        if (!priv) +                return; + +        this->private = NULL; + +        GF_FREE (priv->path); +        GF_FREE (priv); + +        return; +} + +struct xlator_fops fops = { +        .lookup        = svc_lookup, +        .opendir       = svc_opendir, +        .stat          = svc_stat, +        .fstat         = svc_fstat, +        .rmdir         = svc_rmdir, +        .rename        = svc_rename, +        .mkdir         = svc_mkdir, +        .open          = svc_open, +        .unlink        = svc_unlink, +        .setattr       = svc_setattr, +        .getxattr      = svc_getxattr, +        .setxattr      = svc_setxattr, +        .fsetxattr     = svc_fsetxattr, +        .readv         = svc_readv, +        .readdir       = svc_readdir, +        .readdirp      = svc_readdirp, +        .create        = svc_create, +        .readlink      = svc_readlink, +        .mknod         = svc_mknod, +        .symlink       = svc_symlink, +        .flush         = svc_flush, +        .link          = svc_link, +        .access        = svc_access, +        .removexattr   = svc_removexattr, +}; + +struct xlator_cbks cbks = { +        .forget = svc_forget, +}; + +struct volume_options options[] = { +        { .key = {"snapshot-directory"}, +          .type = GF_OPTION_TYPE_STR, +          .default_value = ".snaps", +        }, +        { .key  = {NULL} }, +}; diff --git a/xlators/features/snapview-client/src/snapview-client.h b/xlators/features/snapview-client/src/snapview-client.h new file mode 100644 index 00000000000..000490393c1 --- /dev/null +++ b/xlators/features/snapview-client/src/snapview-client.h @@ -0,0 +1,110 @@ + /* +   Copyright (c) 2014 Red Hat, Inc. <http://www.redhat.com> +   This file is part of GlusterFS. + +   This file is licensed to you under your choice of the GNU Lesser +   General Public License, version 3 or any later version (LGPLv3 or +   later), or the GNU General Public License, version 2 (GPLv2), in all +   cases as published by the Free Software Foundation. +*/ +#ifndef __SNAP_VIEW_CLIENT_H__ +#define __SNAP_VIEW_CLIENT_H__ + +#ifndef _CONFIG_H +#define _CONFIG_H +#include "config.h" +#endif + +#include "glusterfs.h" +#include "logging.h" +#include "dict.h" +#include "xlator.h" +#include "defaults.h" +#include "snapview-client-mem-types.h" + +struct __svc_local { +        loc_t loc; +        xlator_t *subvolume; +}; +typedef struct __svc_local svc_local_t; + +void +svc_local_free (svc_local_t *local); + +#define SVC_STACK_UNWIND(fop, frame, params ...) do {           \ +                svc_local_t *__local = NULL;                    \ +                if (frame) {                                    \ +                        __local      = frame->local;            \ +                        frame->local = NULL;                    \ +                }                                               \ +                STACK_UNWIND_STRICT (fop, frame, params);       \ +                svc_local_free (__local);                       \ +        } while (0) + +#define SVC_ENTRY_POINT_SET(this, xdata, op_ret, op_errno, new_xdata,   \ +                            priv, ret, label)                           \ +        do {                                                            \ +                if (!xdata) {                                           \ +                        xdata = new_xdata = dict_new ();                \ +                        if (!new_xdata) {                               \ +                                gf_log (this->name, GF_LOG_ERROR,       \ +                                        "failed to allocate new dict"); \ +                                op_ret = -1;                            \ +                                op_errno = ENOMEM;                      \ +                                goto label;                             \ +                        }                                               \ +                }                                                       \ +                ret = dict_set_str (xdata, "entry-point", "true");      \ +                if (ret) {                                              \ +                        gf_log (this->name, GF_LOG_ERROR,               \ +                                "failed to set dict");                  \ +                        op_ret = -1;                                    \ +                        op_errno = ENOMEM;                              \ +                        goto label;                                     \ +                }                                                       \ +        } while (0); + +#define SVC_GET_SUBVOL_FROM_CTX(this, op_ret, op_errno, inode_type, ret, \ +                                inode, subvolume, label)                \ +        do {                                                            \ +                ret = svc_inode_ctx_get (this, inode, &inode_type);     \ +                if (ret < 0) {                                          \ +                        gf_log (this->name, GF_LOG_ERROR,               \ +                                "inode context not found for gfid %s",  \ +                                uuid_utoa (inode->gfid));               \ +                        op_ret = -1;                                    \ +                        op_errno = EINVAL;                              \ +                        goto label;                                     \ +                }                                                       \ +                                                                        \ +                subvolume = svc_get_subvolume (this, inode_type);       \ +        }  while (0); + +struct svc_private { +        char *path; //might be helpful for samba +}; +typedef struct svc_private svc_private_t; + +typedef enum { +        NORMAL_INODE = 1, +        VIRTUAL_INODE +} inode_type_t; + +void svc_local_free (svc_local_t *local); + +xlator_t * +svc_get_subvolume (xlator_t *this, int inode_type); + +int +__svc_inode_ctx_get (xlator_t *this, inode_t *inode, int *inode_type); + +int +svc_inode_ctx_get (xlator_t *this, inode_t *inode, int *inode_type); + +int32_t +svc_inode_ctx_set (xlator_t *this, inode_t *inode, int inode_type); + +void +svc_local_free (svc_local_t *local); + +#endif /* __SNAP_VIEW_CLIENT_H__ */ diff --git a/xlators/features/snapview-server/Makefile.am b/xlators/features/snapview-server/Makefile.am new file mode 100644 index 00000000000..af437a64d6d --- /dev/null +++ b/xlators/features/snapview-server/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = src diff --git a/xlators/features/snapview-server/src/Makefile.am b/xlators/features/snapview-server/src/Makefile.am new file mode 100644 index 00000000000..0966ae4cc56 --- /dev/null +++ b/xlators/features/snapview-server/src/Makefile.am @@ -0,0 +1,22 @@ +xlator_LTLIBRARIES = snapview-server.la +xlatordir = $(libdir)/glusterfs/$(PACKAGE_VERSION)/xlator/features + +snapview_server_la_LDFLAGS = -module -avoid-version + +snapview_server_la_SOURCES = snapview-server.c +snapview_server_la_LIBADD = $(top_builddir)/libglusterfs/src/libglusterfs.la\ +                            $(top_builddir)/api/src/libgfapi.la\ +                            $(RLLIBS) $(top_builddir)/rpc/xdr/src/libgfxdr.la \ +                            $(top_builddir)/rpc/rpc-lib/src/libgfrpc.la + +noinst_HEADERS = snapview-server.h snapview-server-mem-types.h + +AM_CPPFLAGS = $(GF_CPPFLAGS) -I$(top_srcdir)/libglusterfs/src \ +               -I$(top_srcdir)/api/src \ +               -I$(top_srcdir)/rpc/rpc-lib/src \ +               -I$(top_srcdir)/rpc/xdr/src \ +	       -DDATADIR=\"$(localstatedir)\" + +AM_CFLAGS = -Wall $(GF_CFLAGS) + +CLEANFILES = diff --git a/xlators/features/snapview-server/src/snapview-server-mem-types.h b/xlators/features/snapview-server/src/snapview-server-mem-types.h new file mode 100644 index 00000000000..6820b5e16f1 --- /dev/null +++ b/xlators/features/snapview-server/src/snapview-server-mem-types.h @@ -0,0 +1,25 @@ +/* +   Copyright (c) 2014 Red Hat, Inc. <http://www.redhat.com> +   This file is part of GlusterFS. + +   This file is licensed to you under your choice of the GNU Lesser +   General Public License, version 3 or any later version (LGPLv3 or +   later), or the GNU General Public License, version 2 (GPLv2), in all +   cases as published by the Free Software Foundation. +*/ + +#ifndef __SNAP_VIEW_MEM_TYPES_H +#define __SNAP_VIEW_MEM_TYPES_H + +#include "mem-types.h" + +enum snapview_mem_types { +        gf_svs_mt_priv_t = gf_common_mt_end + 1, +        gf_svs_mt_svs_inode_t, +        gf_svs_mt_dirents_t, +        gf_svs_mt_svs_fd_t, +        gf_svs_mt_end +}; + +#endif + diff --git a/xlators/features/snapview-server/src/snapview-server.c b/xlators/features/snapview-server/src/snapview-server.c new file mode 100644 index 00000000000..655372347fb --- /dev/null +++ b/xlators/features/snapview-server/src/snapview-server.c @@ -0,0 +1,2395 @@ +/* +   Copyright (c) 2014 Red Hat, Inc. <http://www.redhat.com> +   This file is part of GlusterFS. + +   This file is licensed to you under your choice of the GNU Lesser +   General Public License, version 3 or any later version (LGPLv3 or +   later), or the GNU General Public License, version 2 (GPLv2), in all +   cases as published by the Free Software Foundation. +*/ +#ifndef _CONFIG_H +#define _CONFIG_H +#include "config.h" +#endif + +#include "snapview-server.h" +#include "snapview-server-mem-types.h" + +/* + * Helper functions + */ + +int +__svs_inode_ctx_set (xlator_t *this, inode_t *inode, svs_inode_t *svs_inode) +{ +        uint64_t    value = 0; +        int         ret   = -1; + +        GF_VALIDATE_OR_GOTO ("snapview-server", this, out); +        GF_VALIDATE_OR_GOTO (this->name, inode, out); +        GF_VALIDATE_OR_GOTO (this->name, svs_inode, out); + +        value = (uint64_t)(long) svs_inode; + +        ret = __inode_ctx_set (inode, this, &value); + +out: +        return ret; +} + + +svs_inode_t * +__svs_inode_ctx_get (xlator_t *this, inode_t *inode) +{ +        svs_inode_t *svs_inode = NULL; +        uint64_t     value     = 0; +        int          ret       = -1; + +        GF_VALIDATE_OR_GOTO ("snapview-server", this, out); +        GF_VALIDATE_OR_GOTO (this->name, inode, out); + +        ret = __inode_ctx_get (inode, this, &value); +        if (ret) +                goto out; + +        svs_inode = (svs_inode_t *) ((long) value); + +out: +        return svs_inode; +} + + +svs_inode_t * +svs_inode_ctx_get (xlator_t *this, inode_t *inode) +{ +        svs_inode_t *svs_inode = NULL; + +        GF_VALIDATE_OR_GOTO ("snapview-server", this, out); +        GF_VALIDATE_OR_GOTO (this->name, inode, out); + +        LOCK (&inode->lock); +        { +                svs_inode = __svs_inode_ctx_get (this, inode); +        } +        UNLOCK (&inode->lock); + +out: +        return svs_inode; +} + +int32_t +svs_inode_ctx_set (xlator_t *this, inode_t *inode, svs_inode_t *svs_inode) +{ +        int32_t    ret = -1; + +        GF_VALIDATE_OR_GOTO ("snapview-server", this, out); +        GF_VALIDATE_OR_GOTO (this->name, inode, out); +        GF_VALIDATE_OR_GOTO (this->name, svs_inode, out); + +        LOCK (&inode->lock); +        { +                ret = __svs_inode_ctx_set (this, inode, svs_inode); +        } +        UNLOCK (&inode->lock); + +out: +        return ret; +} + +svs_inode_t * +svs_inode_new () +{ +        svs_inode_t    *svs_inode = NULL; + +        svs_inode = GF_CALLOC (1, sizeof (*svs_inode), gf_svs_mt_svs_inode_t); + +        return svs_inode; +} + +svs_inode_t * +svs_inode_ctx_get_or_new (xlator_t *this, inode_t *inode) +{ +        svs_inode_t   *svs_inode = NULL; +        int            ret       = -1; + +        GF_VALIDATE_OR_GOTO ("snapview-server", this, out); +        GF_VALIDATE_OR_GOTO (this->name, inode, out); + +        LOCK (&inode->lock); +        { +                svs_inode = __svs_inode_ctx_get (this, inode); +                if (!svs_inode) { +                        svs_inode = svs_inode_new (this, inode); +                        if (svs_inode) { +                                ret = __svs_inode_ctx_set (this, inode, +                                                           svs_inode); +                                if (ret) { +                                        GF_FREE (svs_inode); +                                        svs_inode = NULL; +                                } +                        } +                } +        } +        UNLOCK (&inode->lock); + +out: +        return svs_inode; +} + +svs_fd_t * +svs_fd_new () +{ +        svs_fd_t    *svs_fd = NULL; + +        svs_fd = GF_CALLOC (1, sizeof (*svs_fd), gf_svs_mt_svs_fd_t); + +        return svs_fd; +} + +int +__svs_fd_ctx_set (xlator_t *this, fd_t *fd, svs_fd_t *svs_fd) +{ +        uint64_t    value = 0; +        int         ret   = -1; + +        GF_VALIDATE_OR_GOTO ("snapview-server", this, out); +        GF_VALIDATE_OR_GOTO (this->name, fd, out); +        GF_VALIDATE_OR_GOTO (this->name, svs_fd, out); + +        value = (uint64_t)(long) svs_fd; + +        ret = __fd_ctx_set (fd, this, value); + +out: +        return ret; +} + + +svs_fd_t * +__svs_fd_ctx_get (xlator_t *this, fd_t *fd) +{ +        svs_fd_t *svs_fd = NULL; +        uint64_t  value  = 0; +        int       ret    = -1; + +        GF_VALIDATE_OR_GOTO ("snapview-server", this, out); +        GF_VALIDATE_OR_GOTO (this->name, fd, out); + +        ret = __fd_ctx_get (fd, this, &value); +        if (ret) +                return NULL; + +        svs_fd = (svs_fd_t *) ((long) value); + +out: +        return svs_fd; +} + + +svs_fd_t * +svs_fd_ctx_get (xlator_t *this, fd_t *fd) +{ +        svs_fd_t *svs_fd = NULL; + +        GF_VALIDATE_OR_GOTO ("snapview-server", this, out); +        GF_VALIDATE_OR_GOTO (this->name, fd, out); + +        LOCK (&fd->lock); +        { +                svs_fd = __svs_fd_ctx_get (this, fd); +        } +        UNLOCK (&fd->lock); + +out: +        return svs_fd; +} + +int32_t +svs_fd_ctx_set (xlator_t *this, fd_t *fd, svs_fd_t *svs_fd) +{ +        int32_t    ret = -1; + +        GF_VALIDATE_OR_GOTO ("snapview-server", this, out); +        GF_VALIDATE_OR_GOTO (this->name, fd, out); +        GF_VALIDATE_OR_GOTO (this->name, svs_fd, out); + +        LOCK (&fd->lock); +        { +                ret = __svs_fd_ctx_set (this, fd, svs_fd); +        } +        UNLOCK (&fd->lock); + +out: +        return ret; +} + +svs_fd_t * +__svs_fd_ctx_get_or_new (xlator_t *this, fd_t *fd) +{ +        svs_fd_t        *svs_fd    = NULL; +        int              ret       = -1; +        glfs_t          *fs        = NULL; +        glfs_object_t   *object    = NULL; +        svs_inode_t     *inode_ctx = NULL; +        glfs_fd_t       *glfd      = NULL; +        inode_t         *inode     = NULL; + +        GF_VALIDATE_OR_GOTO ("snapview-server", this, out); +        GF_VALIDATE_OR_GOTO (this->name, fd, out); + +        inode = fd->inode; +        svs_fd = __svs_fd_ctx_get (this, fd); +        if (svs_fd) { +                ret = 0; +                goto out; +        } + +        svs_fd = svs_fd_new (this, fd); +        if (!svs_fd) { +                gf_log (this->name, GF_LOG_ERROR, "failed to allocate new fd " +                        "context for gfid %s", uuid_utoa (inode->gfid)); +                goto out; +        } + +        if (fd_is_anonymous (fd)) { +                inode_ctx = svs_inode_ctx_get (this, inode); +                if (!inode_ctx) { +                        gf_log (this->name, GF_LOG_ERROR, "failed to get inode " +                                "context for %s", uuid_utoa (inode->gfid)); +                        goto out; +                } + +                fs = inode_ctx->fs; +                object = inode_ctx->object; + +                if (inode->ia_type == IA_IFDIR) { +                        glfd = glfs_h_opendir (fs, object); +                        if (!glfd) { +                                gf_log (this->name, GF_LOG_ERROR, "failed to " +                                        "open the directory %s", +                                        uuid_utoa (inode->gfid)); +                                goto out; +                        } +                } + +                if (inode->ia_type == IA_IFREG) { +                        glfd = glfs_h_open (fs, object, O_RDONLY|O_LARGEFILE); +                        if (!glfd) { +                                gf_log (this->name, GF_LOG_ERROR, "failed to " +                                        "open the file %s", +                                        uuid_utoa (inode->gfid)); +                                goto out; +                        } +                } + +                svs_fd->fd = glfd; +        } + +        ret = __svs_fd_ctx_set (this, fd, svs_fd); +        if (ret) { +                gf_log (this->name, GF_LOG_ERROR, "failed to set fd context " +                        "for gfid %s", uuid_utoa (inode->gfid)); +                if (svs_fd->fd) { +                        if (inode->ia_type == IA_IFDIR) { +                                ret = glfs_closedir (svs_fd->fd); +                                if (ret) +                                        gf_log (this->name, GF_LOG_ERROR, +                                                "failed to close the fd for %s", +                                                uuid_utoa (inode->gfid)); +                        } +                        if (inode->ia_type == IA_IFREG) { +                                ret = glfs_close (svs_fd->fd); +                                if (ret) +                                        gf_log (this->name, GF_LOG_ERROR, +                                                "failed to close the fd for %s", +                                                uuid_utoa (inode->gfid)); +                        } +                } +                ret = -1; +        } + +out: +        if (ret) { +                GF_FREE (svs_fd); +                svs_fd = NULL; +        } + +        return svs_fd; +} + +svs_fd_t * +svs_fd_ctx_get_or_new (xlator_t *this, fd_t *fd) +{ +        svs_fd_t  *svs_fd = NULL; + +        GF_VALIDATE_OR_GOTO ("snapview-server", this, out); +        GF_VALIDATE_OR_GOTO (this->name, fd, out); + +        LOCK (&fd->lock); +        { +                svs_fd = __svs_fd_ctx_get_or_new (this, fd); +        } +        UNLOCK (&fd->lock); + +out: +        return svs_fd; +} + +void +svs_fill_ino_from_gfid (struct iatt *buf) +{ +        uint64_t  temp_ino = 0; +        int       j        = 0; +        int       i        = 0; +        xlator_t *this     = NULL; + +        this = THIS; + +        GF_VALIDATE_OR_GOTO ("snapview-server", this, out); +        GF_VALIDATE_OR_GOTO (this->name, buf, out); + +        /* consider least significant 8 bytes of value out of gfid */ +        if (uuid_is_null (buf->ia_gfid)) { +                buf->ia_ino = -1; +                goto out; +        } +        for (i = 15; i > (15 - 8); i--) { +                temp_ino += (uint64_t)(buf->ia_gfid[i]) << j; +                j += 8; +        } +        buf->ia_ino = temp_ino; +out: +        return; +} + +void +svs_iatt_fill (uuid_t gfid, struct iatt *buf) +{ +        struct timeval  tv    = {0, }; +        xlator_t       *this  = NULL; + +        this = THIS; + +        GF_VALIDATE_OR_GOTO ("snapview-server", this, out); +        GF_VALIDATE_OR_GOTO (this->name, buf, out); + +        buf->ia_type = IA_IFDIR; +        buf->ia_uid  = 0; +        buf->ia_gid  = 0; +        buf->ia_size = 0; +        buf->ia_nlink = 2; +        buf->ia_blocks = 8; +        buf->ia_size = 4096; + +        uuid_copy (buf->ia_gfid, gfid); +        svs_fill_ino_from_gfid (buf); + +        buf->ia_prot = ia_prot_from_st_mode (0755); + +        gettimeofday (&tv, 0); + +        buf->ia_mtime = buf->ia_atime = buf->ia_ctime = tv.tv_sec; +        buf->ia_mtime_nsec = buf->ia_atime_nsec = buf->ia_ctime_nsec = +                (tv.tv_usec * 1000); + +out: +        return; +} + +snap_dirent_t * +svs_get_snap_dirent (xlator_t *this, const char *name) +{ +        svs_private_t      *private     = NULL; +        int                 i           = 0; +        snap_dirent_t      *dirents     = NULL; +        snap_dirent_t      *tmp_dirent  = NULL; +        snap_dirent_t      *dirent      = NULL; + +        GF_VALIDATE_OR_GOTO ("snapview-server", this, out); +        GF_VALIDATE_OR_GOTO (this->name, this->private, out); +        GF_VALIDATE_OR_GOTO (this->name, name, out); + +        private = this->private; + +        dirents = private->dirents; + +        tmp_dirent = dirents; +        for (i = 0; i < private->num_snaps; i++) { +                if (!strcmp (tmp_dirent->name, name)) { +                        dirent = tmp_dirent; +                        break; +                } +                tmp_dirent++; +        } + +out: +        return dirent; +} + +glfs_t * +svs_initialise_snapshot_volume (xlator_t *this, const char *name) +{ +        svs_private_t      *priv              = NULL; +        int32_t             ret               = -1; +        snap_dirent_t      *dirent            = NULL; +        char                volname[PATH_MAX] = {0, }; +        glfs_t             *fs                = NULL; +        int                 loglevel          = GF_LOG_INFO; +        char                logfile[PATH_MAX] = {0, }; + +        GF_VALIDATE_OR_GOTO ("snapview-server", this, out); +        GF_VALIDATE_OR_GOTO (this->name, this->private, out); +        GF_VALIDATE_OR_GOTO (this->name, name, out); + +        priv = this->private; + +        dirent = svs_get_snap_dirent (this, name); +        if (!dirent) { +                gf_log (this->name, GF_LOG_ERROR, "snap entry for name %s " +                        "not found", name); +                goto out; +        } + +        if (dirent->fs) { +                ret = 0; +                fs = dirent->fs; +                goto out; +        } + +        snprintf (volname, sizeof (volname), "/snaps/%s/%s", +                  dirent->name, dirent->uuid); + +        fs = glfs_new (volname); +        if (!fs) { +                gf_log (this->name, GF_LOG_ERROR, +                        "glfs instance for snap volume %s " +                        "failed", dirent->name); +                goto out; +        } + +        ret = glfs_set_volfile_server (fs, "tcp", "localhost", +                                       24007); +        if (ret) { +                gf_log (this->name, GF_LOG_ERROR, "setting the " +                        "volfile srever for snap volume %s " +                        "failed", dirent->name); +                goto out; +        } + +        ret = glfs_init (fs); +        if (ret) { +                gf_log (this->name, GF_LOG_ERROR, "initing the " +                        "fs for %s failed", dirent->name); +                goto out; +        } + +        snprintf (logfile, sizeof (logfile), +                  DEFAULT_SVD_LOG_FILE_DIRECTORY "/%s-%s.log", name, dirent->uuid); + +        ret = glfs_set_logging(fs, logfile, loglevel); +        if (ret) { +                gf_log (this->name, GF_LOG_ERROR, "failed to set the " +                        "log file path"); +                goto out; +        } + +        ret = 0; + +out: +        if (ret && fs) { +                glfs_fini (fs); +                fs = NULL; +        } + +        if (fs) +                dirent->fs = fs; + +        return fs; +} + +snap_dirent_t * +svs_get_latest_snap_entry (xlator_t *this) +{ +        svs_private_t *priv       = NULL; +        snap_dirent_t *dirents    = NULL; +        snap_dirent_t *dirent     = NULL; +        snap_dirent_t *tmp_dirent = NULL; + +        GF_VALIDATE_OR_GOTO ("svs", this, out); + +        priv = this->private; + +        dirents = priv->dirents; + +        if (priv->num_snaps) { +                tmp_dirent = &dirents[priv->num_snaps - 1]; +                dirent = tmp_dirent; +        } + +out: +        return dirent; +} + +glfs_t * +svs_get_latest_snapshot (xlator_t *this) +{ +        glfs_t        *fs         = NULL; +        snap_dirent_t *dirent     = NULL; + +        GF_VALIDATE_OR_GOTO ("svs", this, out); + +        dirent = svs_get_latest_snap_entry (this); + +        if (dirent) +                fs = dirent->fs; + +out: +        return fs; +} + +int32_t +svs_lookup_entry_point (xlator_t *this, loc_t *loc, inode_t *parent, +                        struct iatt *buf, struct iatt *postparent, +                        int32_t *op_errno) +{ +        uuid_t         gfid; +        svs_inode_t   *inode_ctx = NULL; +        int            op_ret    = -1; + +        GF_VALIDATE_OR_GOTO ("snapview-server", this, out); +        GF_VALIDATE_OR_GOTO (this->name, loc, out); +        GF_VALIDATE_OR_GOTO (this->name, loc->inode, out); +        GF_VALIDATE_OR_GOTO (this->name, buf, out); +        GF_VALIDATE_OR_GOTO (this->name, postparent, out); + +        if (uuid_is_null (loc->inode->gfid)) { +                uuid_generate (gfid); +                svs_iatt_fill (gfid, buf); + +                /* Here the inode context of the entry point directory +                   is filled with just the type of the inode and the gfid +                   of the parent from where the entry point was entered. +                   The glfs object and the fs instance will be NULL. +                */ +                if (parent) +                        svs_iatt_fill (parent->gfid, postparent); +                else { +                        svs_iatt_fill (buf->ia_gfid, postparent); +                } + +                inode_ctx = svs_inode_ctx_get_or_new (this, loc->inode); +                if (!inode_ctx) { +                        gf_log (this->name, GF_LOG_ERROR, "failed to " +                                "allocate inode context for entry point " +                                "directory"); +                        op_ret = -1; +                        *op_errno = ENOMEM; +                        goto out; +                } +                uuid_copy (inode_ctx->pargfid, loc->pargfid); +                memcpy (&inode_ctx->buf, buf, sizeof (*buf)); +                inode_ctx->type = SNAP_VIEW_ENTRY_POINT_INODE; +        } else { +                if (inode_ctx) { +                        memcpy (buf, &inode_ctx->buf, sizeof (*buf)); +                        svs_iatt_fill (inode_ctx->pargfid, postparent); +                } else { +                        svs_iatt_fill (loc->inode->gfid, buf); +                        if (parent) +                                svs_iatt_fill (parent->gfid, +                                               postparent); +                        else { +                                svs_iatt_fill (loc->inode->gfid, +                                               postparent); +                        } +                } +        } +                op_ret = 0; +                goto out; + +out: +        return op_ret; +} + +/* When lookup comes from client and the protocol/server tries to resolve +   the pargfid via just sending the gfid as part of lookup, if the inode +   for the parent gfid is not found. But since that gfid has not yet been +   looked  up yet, inode will not be having inode context and parent is not +   there (as it is the parent of the entry that is being resolved). So +   without parent and inode context, svs cannot know which snapshot +   to look into. In such cases, the amguity is handled by looking +   into the latest snapshot. If the directory is there in the latest +   snapshot, lookup is successful, otherwise it is a failure. So for +   any directory created after taking the latest snapshot, entry into +   snapshot world is denied. i.e you have to be part of snapshot world +   to enter it. If the gfid is not found there, then unwind with +   ESTALE +   This gets executed mainly in the situation where the snapshot entry +   point is entered from a non-root directory and that non-root directory's +   inode (or gfid) is not yet looked up. And in each case when a gfid has to +   be looked up (without any inode contex and parent context present), last +   snapshot is referred and a random gfid is not generated. +*/ +int32_t +svs_lookup_gfid (xlator_t *this, loc_t *loc, struct iatt *buf, +                 struct iatt *postparent, int32_t *op_errno) +{ +        int32_t         op_ret                          = -1; +        unsigned char   handle_obj[GFAPI_HANDLE_LENGTH] = {0, }; +        glfs_t         *fs                              = NULL; +        glfs_object_t  *object                          = NULL; +        struct stat     statbuf                         = {0, }; +        svs_inode_t    *inode_ctx                       = NULL; + +        GF_VALIDATE_OR_GOTO ("snapview-server", this, out); +        GF_VALIDATE_OR_GOTO (this->name, loc, out); +        GF_VALIDATE_OR_GOTO (this->name, loc->inode, out); +        GF_VALIDATE_OR_GOTO (this->name, buf, out); +        GF_VALIDATE_OR_GOTO (this->name, postparent, out); + +        if (uuid_is_null (loc->gfid) && uuid_is_null (loc->inode->gfid)) { +                gf_log (this->name, GF_LOG_ERROR, "gfid is NULL"); +                goto out; +        } + +        if (!uuid_is_null (loc->inode->gfid)) +                memcpy (handle_obj, loc->inode->gfid, +                        GFAPI_HANDLE_LENGTH); +        else +                memcpy (handle_obj, loc->gfid, +                        GFAPI_HANDLE_LENGTH); + +        fs = svs_get_latest_snapshot (this); +        if (!fs) { +                gf_log (this->name, GF_LOG_ERROR, "failed to get the latest " +                        "snapshot"); +                op_ret = -1; +                *op_errno = EINVAL; +                goto out; +        } + + +        object = glfs_h_create_from_handle (fs, handle_obj, GFAPI_HANDLE_LENGTH, +                                            &statbuf); +        if (!object) { +                gf_log (this->name, GF_LOG_ERROR, "failed to do lookup and get " +                        "the handle on the snapshot %s (path: %s, gfid: %s)", +                        loc->name, loc->path, uuid_utoa (loc->gfid)); +                op_ret = -1; +                *op_errno = ESTALE; +                goto out; +        } + +        inode_ctx = svs_inode_ctx_get_or_new (this, loc->inode); +        if (!inode_ctx) { +                gf_log (this->name, GF_LOG_ERROR, "failed to allocate inode " +                        "context"); +                op_ret = -1; +                *op_errno = ENOMEM; +                goto out; +        } + +        iatt_from_stat (buf, &statbuf); +        if (loc->gfid) +                uuid_copy (buf->ia_gfid, loc->gfid); +        else +                uuid_copy (buf->ia_gfid, loc->inode->gfid); + +        inode_ctx->type = SNAP_VIEW_VIRTUAL_INODE; +        inode_ctx->fs = fs; +        inode_ctx->object = object; +        memcpy (&inode_ctx->buf, buf, sizeof (*buf)); +        svs_iatt_fill (buf->ia_gfid, postparent); + +        op_ret = 0; + +out: +        return op_ret; +} + +/* If the parent is an entry point inode, then create the handle for the +   snapshot on which lookup came. i.e in reality lookup came on +   the directory from which the entry point directory was entered, but +   lookup is into the past. So create the handle for it by doing +   the name-less lookup on the gfid (which can be obtained from +   parent's context +*/ +int32_t +svs_lookup_snapshot (xlator_t *this, loc_t *loc, struct iatt *buf, +                     struct iatt *postparent, inode_t *parent, +                     svs_inode_t *parent_ctx, int32_t *op_errno) +{ +        int32_t         op_ret                          = -1; +        unsigned char   handle_obj[GFAPI_HANDLE_LENGTH] = {0, }; +        glfs_t         *fs                              = NULL; +        glfs_object_t  *object                          = NULL; +        struct stat     statbuf                         = {0, }; +        svs_inode_t    *inode_ctx                       = NULL; +        uuid_t          gfid; + +        GF_VALIDATE_OR_GOTO ("snapview-server", this, out); +        GF_VALIDATE_OR_GOTO (this->name, loc, out); +        GF_VALIDATE_OR_GOTO (this->name, loc->inode, out); +        GF_VALIDATE_OR_GOTO (this->name, buf, out); +        GF_VALIDATE_OR_GOTO (this->name, postparent, out); +        GF_VALIDATE_OR_GOTO (this->name, parent_ctx, out); +        GF_VALIDATE_OR_GOTO (this->name, parent, out); + +        fs = svs_initialise_snapshot_volume (this, loc->name); +        if (!fs) { +                gf_log (this->name, GF_LOG_ERROR, "failed to " +                        "create the fs instance for snap %s", +                        loc->name); +                op_ret = -1; +                *op_errno = ESTALE; +                goto out; +        } + +        memcpy (handle_obj, parent_ctx->pargfid, +                GFAPI_HANDLE_LENGTH); +        object = glfs_h_create_from_handle (fs, handle_obj, GFAPI_HANDLE_LENGTH, +                                            &statbuf); +        if (!object) { +                gf_log (this->name, GF_LOG_ERROR, "failed to do lookup and " +                        "get the handle on the snapshot %s", loc->name); +                op_ret = -1; +                *op_errno = errno; +                goto out; +        } + +        inode_ctx = svs_inode_ctx_get_or_new (this, loc->inode); +        if (!inode_ctx) { +                gf_log (this->name, GF_LOG_ERROR, "failed to " +                        "allocate inode context"); +                op_ret = -1; +                *op_errno = ENOMEM; +                goto out; +        } + +        if (uuid_is_null (loc->gfid) && +            uuid_is_null (loc->inode->gfid)) +                uuid_generate (gfid); +        else { +                if (!uuid_is_null (loc->inode->gfid)) +                        uuid_copy (gfid, loc->inode->gfid); +                else +                        uuid_copy (gfid, loc->gfid); +        } +        iatt_from_stat (buf, &statbuf); +        uuid_copy (buf->ia_gfid, gfid); +        svs_fill_ino_from_gfid (buf); +        inode_ctx->type = SNAP_VIEW_VIRTUAL_INODE; +        inode_ctx->fs = fs; +        inode_ctx->object = object; +        memcpy (&inode_ctx->buf, buf, sizeof (*buf)); +        svs_iatt_fill (parent->gfid, postparent); + +        op_ret = 0; + +out: +        return op_ret; +} + +/* Both parent and entry are from snapshot world */ +int32_t +svs_lookup_entry (xlator_t *this, loc_t *loc, struct iatt *buf, +                  struct iatt *postparent, inode_t *parent, +                  svs_inode_t *parent_ctx, int32_t *op_errno) +{ +        int32_t         op_ret                          = -1; +        glfs_t         *fs                              = NULL; +        glfs_object_t  *object                          = NULL; +        struct stat     statbuf                         = {0, }; +        svs_inode_t    *inode_ctx                       = NULL; +        glfs_object_t  *parent_object                   = NULL; +        uuid_t          gfid; + +        GF_VALIDATE_OR_GOTO ("snapview-server", this, out); +        GF_VALIDATE_OR_GOTO (this->name, loc, out); +        GF_VALIDATE_OR_GOTO (this->name, loc->inode, out); +        GF_VALIDATE_OR_GOTO (this->name, buf, out); +        GF_VALIDATE_OR_GOTO (this->name, postparent, out); +        GF_VALIDATE_OR_GOTO (this->name, parent_ctx, out); +        GF_VALIDATE_OR_GOTO (this->name, parent, out); + +        parent_object = parent_ctx->object; +        fs = parent_ctx->fs; + +        object = glfs_h_lookupat (fs, parent_object, loc->name, +                                  &statbuf); +        if (!object) { +                gf_log (this->name, GF_LOG_ERROR, "failed to do lookup and " +                        "get the handle for entry %s (path: %s)", loc->name, +                        loc->path); +                op_ret = -1; +                *op_errno = errno; +                goto out; +        } + +        inode_ctx = svs_inode_ctx_get_or_new (this, loc->inode); +        if (!inode_ctx) { +                gf_log (this->name, GF_LOG_ERROR, "failed to " +                        "allocate inode context"); +                op_ret = -1; +                *op_errno = ENOMEM; +                goto out; +        } + +        if (uuid_is_null (loc->gfid) && +            uuid_is_null (loc->inode->gfid)) +                uuid_generate (gfid); +        else { +                if (!uuid_is_null (loc->inode->gfid)) +                        uuid_copy (gfid, loc->inode->gfid); +                else +                        uuid_copy (gfid, loc->gfid); +        } + +        iatt_from_stat (buf, &statbuf); +        uuid_copy (buf->ia_gfid, gfid); +        svs_fill_ino_from_gfid (buf); +        inode_ctx->type = SNAP_VIEW_VIRTUAL_INODE; +        inode_ctx->fs = fs; +        inode_ctx->object = object; +        memcpy (&inode_ctx->buf, buf, sizeof (*buf)); +        svs_iatt_fill (parent->gfid, postparent); + +        op_ret = 0; + +out: +        return op_ret; +} + +/* inode context is there means lookup has come on an object which was +   built either as part of lookup or as part of readdirp. But in readdirp +   we would not have got the handle to access the object in the gfapi +   world. +   So if inode context contains glfs_t instance for the right +   gfapi world and glfs_object_t handle for accessing it in the gfapi +   world, then unwind with success as the snapshots as of now are +   read-only. +   If the above condition is not met, then send lookup call again to +   the gfapi world. It can happen only if both parent context and +   the name of the entry are present. + +   If parent is an entry point to snapshot world: +   * parent is needed for getting the gfid on which lookup has to be done +     (the gfid present in the inode is a virtual gfid) in the snapshot +     world. +   * name is required to get the right glfs_t instance on which lookup +     has to be done + +   If parent is a directory from snapshot world: +   * parent context is needed to get the glfs_t instance and to get the +     handle to parent directory in the snapshot world. +   * name is needed to do the lookup on the right entry in the snapshot +     world +*/ +int32_t +svs_revalidate (xlator_t *this, loc_t *loc, inode_t *parent, +                svs_inode_t *inode_ctx, svs_inode_t *parent_ctx, +                struct iatt *buf, struct iatt *postparent, int32_t *op_errno) +{ +        int32_t         op_ret                          = -1; +        glfs_t         *fs                              = NULL; +        glfs_object_t  *object                          = NULL; +        unsigned char   handle_obj[GFAPI_HANDLE_LENGTH] = {0, }; + +        GF_VALIDATE_OR_GOTO ("snapview-server", this, out); +        GF_VALIDATE_OR_GOTO (this->name, buf, out); +        GF_VALIDATE_OR_GOTO (this->name, postparent, out); +        GF_VALIDATE_OR_GOTO (this->name, inode_ctx, out); + +        if (inode_ctx->type == SNAP_VIEW_ENTRY_POINT_INODE) { +                svs_iatt_fill (loc->inode->gfid, buf); +                if (parent) +                        svs_iatt_fill (parent->gfid, +                                       postparent); +                else +                        svs_iatt_fill (loc->inode->gfid, postparent); +                op_ret = 0; +                goto out; +        } else { +                if (inode_ctx->fs && inode_ctx->object) { +                        memcpy (buf, &inode_ctx->buf, sizeof (*buf)); +                        if (parent) +                                svs_iatt_fill (parent->gfid, postparent); +                        else +                                svs_iatt_fill (buf->ia_gfid, postparent); +                        op_ret = 0; +                        goto out; +                } + +                /* To send the lookup to gfapi world, both the name of the +                   entry as well as the parent context is needed. +                */ +                if (!loc->name || !parent_ctx) { +                        *op_errno = ESTALE; +                        goto out; +                } + +                if (parent_ctx->type == SNAP_VIEW_ENTRY_POINT_INODE) +                        op_ret = svs_lookup_snapshot (this, loc, buf, +                                                      postparent, parent, +                                                      parent_ctx, op_errno); +                else +                        op_ret = svs_lookup_entry (this, loc, buf, postparent, +                                                   parent, parent_ctx, +                                                   op_errno); +                if (op_ret) +                        *op_errno = ESTALE; + +                goto out; +        } + +out: +        return op_ret; +} + +int32_t +svs_lookup (call_frame_t *frame, xlator_t *this, loc_t *loc, dict_t *xdata) +{ +        struct iatt    buf                            = {0, }; +        int32_t        op_ret                         = -1; +        int32_t        op_errno                       = EINVAL; +        struct iatt    postparent                     = {0,}; +        svs_inode_t   *inode_ctx                      = NULL; +        svs_inode_t   *parent_ctx                     = NULL; +        int32_t        ret                            = -1; +        svs_private_t *private                        = NULL; +        inode_t       *parent                         = NULL; +        glfs_t        *fs                             = NULL; +        snap_dirent_t *dirent                         = NULL; +        gf_boolean_t   entry_point                    = _gf_false; + +        GF_VALIDATE_OR_GOTO ("svs", this, out); +        GF_VALIDATE_OR_GOTO (this->name, this->private, out); +        GF_VALIDATE_OR_GOTO (this->name, frame, out); +        GF_VALIDATE_OR_GOTO (this->name, loc, out); +        GF_VALIDATE_OR_GOTO (this->name, loc->inode, out); + +        private = this->private; + +        if (loc->name) { +                ret = dict_get_str_boolean (xdata, "entry-point", _gf_false); +                if (ret == -1) { +                        gf_log (this->name, GF_LOG_ERROR, "failed to get the " +                                "entry point info"); +                        entry_point = _gf_false; +                } else { +                        entry_point = ret; +                } +        } + +        if (loc->parent) +                parent = inode_ref (loc->parent); +        else { +                parent = inode_find (loc->inode->table, loc->pargfid); +                if (!parent) +                        parent = inode_parent (loc->inode, NULL, NULL); +        } +        if (parent) +                parent_ctx = svs_inode_ctx_get (this, parent); + +        inode_ctx = svs_inode_ctx_get (this, loc->inode); + +        /* Initialize latest snapshot, which is used for nameless lookups */ +        dirent = svs_get_latest_snap_entry (this); +        if (!dirent->fs) +                fs = svs_initialise_snapshot_volume (this, dirent->name); + +        /* lookup is on the entry point to the snapshot world */ +        if (entry_point) { +                op_ret = svs_lookup_entry_point (this, loc, parent, &buf, +                                                 &postparent, &op_errno); +                goto out; +        } + +        /* revalidate */ +        if (inode_ctx) { +                op_ret = svs_revalidate (this, loc, parent, inode_ctx, +                                         parent_ctx, &buf, &postparent, +                                         &op_errno); +                goto out; +        } + +        /* This can happen when entry point directory is entered from non-root +           directory. (ex: if /mnt/glusterfs is the mount point, then entry +           point (say .snaps) is entered from /mnt/glusterfs/dir/.snaps). Also +           it can happen when client sends a nameless lookup on just a gfid and +           the server does not have the inode in the inode table. +        */ +        if (!inode_ctx && !parent_ctx) { +                op_ret = svs_lookup_gfid (this, loc, &buf, &postparent, +                                          &op_errno); +                goto out; +        } + +        if (parent_ctx) { +                if (parent_ctx->type == SNAP_VIEW_ENTRY_POINT_INODE) +                        op_ret = svs_lookup_snapshot (this, loc, &buf, +                                                      &postparent, parent, +                                                      parent_ctx, &op_errno); +                else +                        op_ret = svs_lookup_entry (this, loc, &buf, +                                                   &postparent, parent, +                                                   parent_ctx, &op_errno); +                goto out; +        } + +out: +        STACK_UNWIND_STRICT (lookup, frame, op_ret, op_errno, +                             loc?loc->inode:NULL, &buf, xdata, &postparent); + +        if (parent) +                inode_unref (parent); + +        return 0; +} + +int32_t +svs_opendir (call_frame_t *frame, xlator_t *this, loc_t *loc, fd_t *fd, +             dict_t *xdata) +{ +        int32_t        ret        = -1; +        svs_inode_t   *inode_ctx  = NULL; +        int32_t        op_ret     = -1; +        int32_t        op_errno   = EINVAL; +        svs_fd_t      *svs_fd     = NULL; +        glfs_fd_t     *glfd       = NULL; +        glfs_t        *fs         = NULL; +        glfs_object_t *object     = NULL; + +        GF_VALIDATE_OR_GOTO ("snap-view-daemon", this, out); +        GF_VALIDATE_OR_GOTO (this->name, frame, out); +        GF_VALIDATE_OR_GOTO (this->name, fd, out); +        GF_VALIDATE_OR_GOTO (this->name, loc, out); +        GF_VALIDATE_OR_GOTO (this->name, loc->inode, out); + +        inode_ctx = svs_inode_ctx_get (this, loc->inode); +        if (!inode_ctx) { +                gf_log (this->name, GF_LOG_ERROR, "inode context not found " +                        "for the inode %s", uuid_utoa (loc->inode->gfid)); +                op_ret = -1; +                op_errno = ESTALE; +                goto out; +        } + +        /* Fake success is sent if the opendir is on the entry point directory +           or the inode is SNAP_VIEW_ENTRY_POINT_INODE +        */ +        if  (inode_ctx->type == SNAP_VIEW_ENTRY_POINT_INODE) { +                op_ret = 0; +                op_errno = 0; +                goto out; +        } else if (inode_ctx->type == SNAP_VIEW_VIRTUAL_INODE) { +                fs = inode_ctx->fs; +                object = inode_ctx->object; +                glfd = glfs_h_opendir (fs, object); +                if (!glfd) { +                        op_ret = -1; +                        op_errno = errno; +                        gf_log (this->name, GF_LOG_ERROR, "opendir on %s " +                                "failed (gfid: %s)", loc->name, +                                uuid_utoa (loc->inode->gfid)); +                        goto out; +                } +                svs_fd = svs_fd_ctx_get_or_new (this, fd); +                if (!svs_fd) { +                        gf_log (this->name, GF_LOG_ERROR, "failed to allocate " +                                "fd context %s (gfid: %s)", loc->name, +                                uuid_utoa (fd->inode->gfid)); +                        op_ret = -1; +                        op_errno = ENOMEM; +                        glfs_closedir (glfd); +                        goto out; +                } +                svs_fd->fd = glfd; + +                op_ret = 0; +                op_errno = 0; +        } + +out: +        STACK_UNWIND_STRICT (opendir, frame, op_ret, op_errno, fd, NULL); + +        return 0; +} + +int32_t +svs_getxattr (call_frame_t *frame, xlator_t *this, loc_t *loc, const char *name, +              dict_t *xdata) +{ +        svs_inode_t   *inode_ctx  = NULL; +        int32_t        op_ret     = -1; +        int32_t        op_errno   = EINVAL; +        glfs_t        *fs         = NULL; +        glfs_object_t *object     = NULL; +        char          *value      = 0; +        ssize_t        size       = 0; +        dict_t        *dict       = NULL; +        int            ret        = -1; + +        GF_VALIDATE_OR_GOTO ("snap-view-daemon", this, out); +        GF_VALIDATE_OR_GOTO ("snap-view-daemon", frame, out); +        GF_VALIDATE_OR_GOTO ("snap-view-daemon", loc, out); +        GF_VALIDATE_OR_GOTO ("snap-view-daemon", loc->inode, out); + +        inode_ctx = svs_inode_ctx_get (this, loc->inode); +        if (!inode_ctx) { +                gf_log (this->name, GF_LOG_ERROR, "inode context not found " +                        "for the inode %s", uuid_utoa (loc->inode->gfid)); +                op_ret = -1; +                op_errno = ESTALE; +                goto out; +        } + +        /* Fake success is sent if the getxattr is on entry point directory +           or the inode is SNAP_VIEW_ENTRY_POINT_INODE +        */ +        if (inode_ctx->type == SNAP_VIEW_ENTRY_POINT_INODE) { +                op_ret = -1; +                op_errno = EINVAL; +                goto out; +        } else if (inode_ctx->type == SNAP_VIEW_VIRTUAL_INODE) { +                fs = inode_ctx->fs; +                object = inode_ctx->object; +                size = glfs_h_getxattrs (fs, object, name, NULL, 0); +                if (!size == -1) { +                        gf_log (this->name, GF_LOG_ERROR, "getxattr on %s " +                                "failed (key: %s)", loc->name, name); +                        op_ret = -1; +                        op_errno = errno; +                        goto out; +                } +                value = GF_CALLOC (size + 1, sizeof (char), gf_common_mt_char); +                if (!value) { +                        gf_log (this->name, GF_LOG_ERROR, "failed to allocate " +                                "memory for getxattr on %s (key: %s)", +                                loc->name, name); +                        op_ret = -1; +                        op_errno = ENOMEM; +                        goto out; +                } + +                size = glfs_h_getxattrs (fs, object, name, value, size); +                if (size == -1) { +                        gf_log (this->name, GF_LOG_ERROR, "failed to get the " +                                "xattr %s for entry %s", name, loc->name); +                        op_ret = -1; +                        op_errno = errno; +                        goto out; +                } +                value[size] = '\0'; + +                dict = dict_new (); +                if (!dict) { +                        gf_log (this->name, GF_LOG_ERROR, "failed to allocate " +                                "dict"); +                        op_ret = -1; +                        op_errno = ENOMEM; +                        goto out; +                } + +                op_ret = dict_set_dynptr (dict, (char *)name, value, size); +                if (op_ret < 0) { +                        op_errno = -op_ret; +                        gf_log (this->name, GF_LOG_ERROR, "dict set operation " +                                "for %s for the key %s failed.", loc->path, +                                name); +                        GF_FREE (value); +                        goto out; +                } + +                op_ret = 0; +                op_errno = 0; +        } + +out: +        if (op_ret) +                GF_FREE (value); + +        STACK_UNWIND_STRICT (getxattr, frame, op_ret, op_errno, dict, NULL); + +        return 0; +} + +int32_t +svs_fgetxattr (call_frame_t *frame, xlator_t *this, fd_t *fd, const char *name, +               dict_t *xdata) +{ +        svs_inode_t *inode_ctx  = NULL; +        int32_t      op_ret     = -1; +        int32_t      op_errno   = EINVAL; +        char        *value      = 0; +        ssize_t      size       = 0; +        dict_t      *dict       = NULL; +        svs_fd_t    *sfd        = NULL; +        glfs_fd_t   *glfd       = NULL; + +        GF_VALIDATE_OR_GOTO ("snap-view-daemon", this, out); +        GF_VALIDATE_OR_GOTO ("snap-view-daemon", frame, out); +        GF_VALIDATE_OR_GOTO ("snap-view-daemon", fd, out); +        GF_VALIDATE_OR_GOTO ("snap-view-daemon", fd->inode, out); + +        inode_ctx = svs_inode_ctx_get (this, fd->inode); +        if (!inode_ctx) { +                gf_log (this->name, GF_LOG_ERROR, "inode context not found " +                        "for the inode %s", uuid_utoa (fd->inode->gfid)); +                op_ret = -1; +                op_errno = ESTALE; +                goto out; +        } + +        sfd = svs_fd_ctx_get_or_new (this, fd); +        if (!sfd) { +                gf_log (this->name, GF_LOG_ERROR, "failed to get the fd " +                        "context for %s", uuid_utoa (fd->inode->gfid)); +                op_ret = -1; +                op_errno = EBADFD; +                goto out; +        } + +        glfd = sfd->fd; +        /* Fake success is sent if the getxattr is on entry point directory +           or the inode is SNAP_VIEW_ENTRY_POINT_INODE +        */ +        if (inode_ctx->type == SNAP_VIEW_ENTRY_POINT_INODE) { +                op_ret = -1; +                op_errno = EINVAL; +                goto out; +        } + +        if (inode_ctx->type == SNAP_VIEW_VIRTUAL_INODE) { +                size = glfs_fgetxattr (glfd, name, NULL, 0); +                if (!size == -1) { +                        gf_log (this->name, GF_LOG_ERROR, "getxattr on %s " +                                "failed (key: %s)", uuid_utoa (fd->inode->gfid), +                                name); +                        op_ret = -1; +                        op_errno = errno; +                        goto out; +                } +                value = GF_CALLOC (size + 1, sizeof (char), gf_common_mt_char); +                if (!value) { +                        gf_log (this->name, GF_LOG_ERROR, "failed to allocate " +                                "memory for getxattr on %s (key: %s)", +                                uuid_utoa (fd->inode->gfid), name); +                        op_ret = -1; +                        op_errno = ENOMEM; +                        goto out; +                } + +                size = glfs_fgetxattr (glfd, name, value, size); +                if (size == -1) { +                        gf_log (this->name, GF_LOG_ERROR, "failed to get the " +                                "xattr %s for inode %s", name, +                                uuid_utoa (fd->inode->gfid)); +                        op_ret = -1; +                        op_errno = errno; +                        goto out; +                } +                value[size] = '\0'; + +                dict = dict_new (); +                if (!dict) { +                        gf_log (this->name, GF_LOG_ERROR, "failed to allocate " +                                "dict"); +                        op_ret = -1; +                        op_errno = ENOMEM; +                        goto out; +                } + +                op_ret = dict_set_dynptr (dict, (char *)name, value, size); +                if (op_ret < 0) { +                        op_errno = -op_ret; +                        gf_log (this->name, GF_LOG_ERROR, "dict set operation " +                                "for gfid %s for the key %s failed.", +                                uuid_utoa (fd->inode->gfid), name); +                        GF_FREE (value); +                        goto out; +                } + +                op_ret = 0; +                op_errno = 0; +        } + +out: +        if (op_ret) +                GF_FREE (value); + +        STACK_UNWIND_STRICT (fgetxattr, frame, op_ret, op_errno, dict, NULL); + +        return 0; +} + +int32_t +svs_releasedir (xlator_t *this, fd_t *fd) +{ +        svs_fd_t *sfd      = NULL; +        uint64_t          tmp_pfd  = 0; +        int               ret      = 0; + +        GF_VALIDATE_OR_GOTO ("snapview-server", this, out); +        GF_VALIDATE_OR_GOTO (this->name, fd, out); + +        ret = fd_ctx_del (fd, this, &tmp_pfd); +        if (ret < 0) { +                gf_log (this->name, GF_LOG_DEBUG, +                        "pfd from fd=%p is NULL", fd); +                goto out; +        } + +        sfd = (svs_fd_t *)(long)tmp_pfd; +        if (sfd->fd) { +                ret = glfs_closedir (sfd->fd); +                if (ret) +                        gf_log (this->name, GF_LOG_WARNING, "failed to close " +                                "the glfd for directory %s", +                                uuid_utoa (fd->inode->gfid)); +        } + +out: +        return 0; +} + +int32_t +svs_flush (call_frame_t *frame, xlator_t *this, +           fd_t *fd, dict_t *xdata) +{ +        int32_t           op_ret   = -1; +        int32_t           op_errno = 0; +        int               ret      = -1; +        uint64_t          value    = 0; + +        GF_VALIDATE_OR_GOTO ("snapview-server", this, out); +        GF_VALIDATE_OR_GOTO (this->name, frame, out); +        GF_VALIDATE_OR_GOTO (this->name, fd, out); + +        ret = fd_ctx_get (fd, this, &value); +        if (ret < 0) { +                op_errno = EINVAL; +                gf_log (this->name, GF_LOG_WARNING, +                        "pfd is NULL on fd=%p", fd); +                goto out; +        } + +        op_ret = 0; + +out: +        STACK_UNWIND_STRICT (flush, frame, op_ret, op_errno, NULL); + +        return 0; +} + +int32_t +svs_release (xlator_t *this, fd_t *fd) +{ +        svs_fd_t *sfd      = NULL; +        uint64_t          tmp_pfd  = 0; +        int               ret      = 0; + +        GF_VALIDATE_OR_GOTO ("snapview-server", this, out); +        GF_VALIDATE_OR_GOTO (this->name, fd, out); + +        ret = fd_ctx_del (fd, this, &tmp_pfd); +        if (ret < 0) { +                gf_log (this->name, GF_LOG_DEBUG, +                        "pfd from fd=%p is NULL", fd); +                goto out; +        } + +        sfd = (svs_fd_t *)(long)tmp_pfd; +        if (sfd->fd) { +                ret = glfs_close (sfd->fd); +                if (ret) { +                        gf_log (this->name, GF_LOG_ERROR, "failed to close " +                                "the glfd for %s", +                                uuid_utoa (fd->inode->gfid)); +                } +        } + +out: +        return 0; +} + +int32_t +svs_forget  (xlator_t *this, inode_t *inode) +{ +        int       ret = -1; +        uint64_t  value = 0; +        svs_inode_t  *inode_ctx = NULL; + +        GF_VALIDATE_OR_GOTO ("snapview-server", this, out); +        GF_VALIDATE_OR_GOTO (this->name, inode, out); + +        ret = inode_ctx_del (inode, this, &value); +        if (ret) { +                gf_log (this->name, GF_LOG_ERROR, "failed to delte the inode " +                        "context of %s", uuid_utoa (inode->gfid)); +                goto out; +        } + +        inode_ctx = (svs_inode_t *)value; + +        if (inode_ctx->object) +                glfs_h_close (inode_ctx->object); + +        GF_FREE (inode_ctx); + +out: +        return 0; +} + +/* As of now, the list of snapshots is obtained by reading a predefined file +   (which the user has to generate using these commands) +* /usr/local/sbin/gluster snapshot info | grep  -i snap | grep -i Volume | +   grep -i Name | cut -d':' -f 2 > /tmp/tmp-snap-uuids +   /usr/local/sbin/gluster snapshot info | grep Snapshot  | cut -d':' -f 2 +   > /tmp/tmp-snap_names +   This is a temporary workaround which will be changed to a notification +   based mechanism where snapd gets the list of snapshots from glusterd +*/ +int +svs_get_snapshot_list (xlator_t *this, svs_private_t *priv) +{ +        int            ret           = -1; +        char           str_uuid[256] = {'\0'}; +        char           str_name[256] = {'\0'}; +        int            snap_count    = 0; +        snap_dirent_t *dirents       = NULL; +        FILE          *fpn           = NULL; +        FILE          *fpu           = NULL; +        int            i             = 0; + +        GF_VALIDATE_OR_GOTO ("snapview-server", this, out); +        GF_VALIDATE_OR_GOTO (this->name, priv, out); + +        dirents = GF_CALLOC (sizeof (*dirents), SNAP_VIEW_MAX_NUM_SNAPS, +                             gf_svs_mt_dirents_t); +        if (!dirents) { +                gf_log (this->name, GF_LOG_ERROR, "failed to allocate memory"); +                goto out; +                /* error, bail */ +        } +        priv->dirents = dirents; + +        fpu = fopen ("/tmp/tmp-snap-uuids", "r+"); +        fpn = fopen ("/tmp/tmp-snap_names", "r+"); + +        if (!fpu || !fpn) { +                gf_log (this->name, GF_LOG_ERROR, "failed to open the file"); +                goto out; +        } + +        while ((fscanf (fpu, "%s", str_uuid) != -1) && +               (fscanf (fpn, "%s", str_name) != -1)) { +                strncpy (dirents[i].uuid, str_uuid, strlen (str_uuid) + 1); +                strncpy (dirents[i].name, str_name, strlen (str_name) + 1); +                ++snap_count; +                ++i; +        } +        priv->num_snaps = snap_count; + +        fclose (fpn); +        fclose (fpu); + +        ret = 0; + +out: +        return ret; +} + +int +svs_fill_readdir (xlator_t *this, gf_dirent_t *entries, size_t size, off_t off) +{ +        gf_dirent_t             *entry          = NULL; +        svs_private_t           *priv           = NULL; +        int                     i               = 0; +        snap_dirent_t           *dirents        = NULL; +        int                     this_size       = 0; +        int                     filled_size     = 0; +        int                     count           = 0; + +        GF_VALIDATE_OR_GOTO ("snap-view-daemon", this, out); +        GF_VALIDATE_OR_GOTO ("snap-view-daemon", entries, out); + +        priv = this->private; +        /* create the dir entries */ +        dirents = priv->dirents; + +        for (i = off; i < priv->num_snaps; ) { +                this_size = sizeof (gf_dirent_t) + +                        strlen (dirents[i].name) + 1; +                if (this_size + filled_size > size ) +                        goto out; + +                entry = gf_dirent_for_name (dirents[i].name); +                if (!entry) { +                        gf_log (this->name, GF_LOG_ERROR, "failed to allocate " +                                "dentry for %s", dirents[i].name); +                        goto out; +                } + +                entry->d_off = i + 1; +                entry->d_ino = i + 2*42; +                entry->d_type = DT_DIR; +                list_add_tail (&entry->list, &entries->list); +                ++i; +                count++; +                filled_size += this_size; +        } + +out: +        return count; +} + +int32_t +svs_glfs_readdir (xlator_t *this, glfs_fd_t *glfd, gf_dirent_t *entries, +                  int32_t *op_errno, struct iatt *buf, gf_boolean_t readdirplus, +                  size_t size) +{ +        int              filled_size    = 0; +        int              this_size      = 0; +        int32_t          ret            = -1; +        int32_t          count          = 0; +        gf_dirent_t     *entry          = NULL; +        struct   dirent *dirents        = NULL; +        struct   dirent  de             = {0, }; +        struct stat      statbuf        = {0, }; +        off_t            in_case        = -1; + +        GF_VALIDATE_OR_GOTO ("svs", this, out); +        GF_VALIDATE_OR_GOTO (this->name, glfd, out); +        GF_VALIDATE_OR_GOTO (this->name, entries, out); +        GF_VALIDATE_OR_GOTO (this->name, buf, out); + +        while (filled_size < size) { +                in_case = glfs_telldir (glfd); +                if (in_case == -1) { +                        gf_log (this->name, GF_LOG_ERROR, "telldir failed"); +                        break; +                } + +                if (readdirplus) +                        ret = glfs_readdirplus_r (glfd, &statbuf, &de, +                                                  &dirents); +                else +                        ret = glfs_readdir_r (glfd, &de, &dirents); + +                if (ret == 0 && dirents != NULL) { +                        if (readdirplus) +                                this_size = max (sizeof (gf_dirent_t), +                                                 sizeof (gfs3_dirplist)) +                                        + strlen (de.d_name) + 1; +                        else +                                this_size = sizeof (gf_dirent_t) +                                        + strlen (de.d_name) + 1; + +                        if (this_size + filled_size > size) { +                                glfs_seekdir (glfd, in_case); +                                break; +                        } + +                        entry = gf_dirent_for_name (de.d_name); +                        if (!entry) { +                                gf_log (this->name, GF_LOG_ERROR, +                                        "could not create gf_dirent " +                                        "for entry %s: (%s)", +                                        entry->d_name, +                                        strerror (errno)); +                                break; +                        } +                        entry->d_off = de.d_off; +                        entry->d_ino = de.d_ino; +                        entry->d_type = de.d_type; +                        iatt_from_stat (buf, &statbuf); +                        if (readdirplus) +                                entry->d_stat = *buf; +                        list_add_tail (&entry->list, &entries->list); + +                        filled_size += this_size; +                        count++; +                } else if (ret == 0 && dirents == NULL) { +                        *op_errno = ENOENT; +                        break; +                } else if (ret != 0) { +                        *op_errno = errno; +                        break; +                } +                dirents = NULL; +                ret = -1; +        } + +out: +        return count; +} + +/* readdirp can be of 2 types. +   1) It can come on entry point directory where the list of snapshots +      is sent as dirents. In this case, the iatt structure is filled +      on the fly if the inode is not found for the entry or the inode +      context is NULL. Other wise if inode is found and inode context +      is there the iatt structure saved in the context is used. +   2) It can be on a directory in one of the snapshots. In this case, +      the readdirp call would have sent us a iatt structure. So the same +      structure is used with the exception that the gfid and the inode +      numbers will be newly generated and filled in. +*/ +void +svs_readdirp_fill (xlator_t *this, inode_t *parent, svs_inode_t *parent_ctx, +                   gf_dirent_t *entry) +{ +        inode_t                *inode          = NULL; +        uuid_t                  random_gfid    = {0,}; +        struct  iatt            buf            = {0, }; +        svs_inode_t            *inode_ctx      = NULL; + +        GF_VALIDATE_OR_GOTO ("snapview-server", this, out); +        GF_VALIDATE_OR_GOTO (this->name, parent, out); +        GF_VALIDATE_OR_GOTO (this->name, parent_ctx, out); +        GF_VALIDATE_OR_GOTO (this->name, entry, out); + +        inode = inode_grep (parent->table, parent, entry->d_name); +        if (inode) { +                entry->inode = inode; +                inode_ctx = svs_inode_ctx_get (this, inode); +                if (!inode_ctx) { +                        uuid_copy (buf.ia_gfid, inode->gfid); +                        svs_iatt_fill (inode->gfid, &buf); +                        buf.ia_type = inode->ia_type; +                } else { +                        buf = inode_ctx->buf; +                } + +                entry->d_ino = buf.ia_ino; + +                if (parent_ctx->type == SNAP_VIEW_ENTRY_POINT_INODE) +                        entry->d_stat = buf; +                else { +                        entry->d_stat.ia_ino = buf.ia_ino; +                        uuid_copy (entry->d_stat.ia_gfid, buf.ia_gfid); +                } +        } else { +                inode = inode_new (parent->table); +                entry->inode = inode; +                uuid_generate (random_gfid); +                uuid_copy (buf.ia_gfid, random_gfid); +                svs_fill_ino_from_gfid (&buf); +                entry->d_ino = buf.ia_ino; + +                /* If inode context allocation fails, then do not send the +                   inode for that particular entry as part of readdirp +                   response. Fuse and protocol/server will link the inodes +                   in readdirp only if the entry contains inode in it. +                */ +                inode_ctx = svs_inode_ctx_get_or_new (this, inode); +                if (!inode_ctx) { +                        gf_log (this->name, GF_LOG_ERROR, "failed to allocate " +                                "inode context for %s", entry->d_name); +                        inode_unref (entry->inode); +                        entry->inode = NULL; +                        goto out; +                } + +                inode_ctx->type = SNAP_VIEW_VIRTUAL_INODE; + +                if (parent_ctx->type == SNAP_VIEW_ENTRY_POINT_INODE) { +                        buf.ia_type = IA_IFDIR; +                        inode_ctx->buf = buf; +                        entry->d_stat = buf; +                } else { +                        uuid_copy (entry->d_stat.ia_gfid, buf.ia_gfid); +                        entry->d_stat.ia_ino = buf.ia_ino; +                        inode_ctx->buf = entry->d_stat; +                } +        } + +out: +        return; +} + +/* In readdirp, though new inode is created along with the generation of +   new gfid, the inode context created will not contain the glfs_t instance +   for the filesystem it belongs to and the handle for it in the gfapi +   world. (handle is obtained only by doing the lookup call on the entry +   and doing lookup on each entry received as part of readdir call is a +   costly operation. So the fs and handle is NULL in the inode context +   and is filled in when lookup comes on that object. +*/ +int32_t +svs_readdirp (call_frame_t *frame, xlator_t *this, fd_t *fd, size_t size, +              off_t off, dict_t *dict) +{ +        gf_dirent_t             entries; +        gf_dirent_t            *entry                           = NULL; +        struct  iatt            buf                             = {0, }; +        int                     count                           = 0; +        int                     op_ret                          = -1; +        int                     op_errno                        = EINVAL; +        svs_inode_t            *inode_ctx                       = NULL; +        svs_inode_t            *parent_ctx                      = NULL; +        svs_fd_t               *svs_fd                          = NULL; + +        GF_VALIDATE_OR_GOTO ("snap-view-daemon", this, unwind); +        GF_VALIDATE_OR_GOTO (this->name, frame, unwind); +        GF_VALIDATE_OR_GOTO (this->name, fd, unwind); +        GF_VALIDATE_OR_GOTO (this->name, fd->inode, unwind); + +        INIT_LIST_HEAD (&entries.list); + +        parent_ctx = svs_inode_ctx_get (this, fd->inode); +        if (!parent_ctx) { +                gf_log (this->name, GF_LOG_ERROR, "failed to get the inode " +                        "context for %s", uuid_utoa (fd->inode->gfid)); +                op_ret = -1; +                op_errno = EINVAL; +                goto unwind; +        } + +        if (parent_ctx->type == SNAP_VIEW_ENTRY_POINT_INODE) { +                LOCK (&fd->lock); +                { +                        count = svs_fill_readdir (this, &entries, size, off); +                } +                UNLOCK (&fd->lock); + +                op_ret = count; + +                list_for_each_entry (entry, &entries.list, list) { +                        svs_readdirp_fill (this, fd->inode, parent_ctx, entry); +                } + +                goto unwind; +        } else { +                svs_fd = svs_fd_ctx_get_or_new (this, fd); +                if (!svs_fd) { +                        gf_log (this->name, GF_LOG_ERROR, "failed to get the " +                                "fd context %s", uuid_utoa (fd->inode->gfid)); +                        op_ret = -1; +                        op_errno = EBADFD; +                        goto unwind; +                } + +                glfs_seekdir (svs_fd->fd, off); + +                LOCK (&fd->lock); +                { +                        count = svs_glfs_readdir (this, svs_fd->fd, &entries, +                                                  &op_errno, &buf, _gf_true, +                                                  size); +                } +                UNLOCK (&fd->lock); + +                op_ret = count; + +                list_for_each_entry (entry, &entries.list, list) { +                        svs_readdirp_fill (this, fd->inode, parent_ctx, entry); +                } + +                goto unwind; +        } + +unwind: +        STACK_UNWIND_STRICT (readdirp, frame, op_ret, op_errno, &entries, dict); + +        gf_dirent_free (&entries); + +        return 0; +} + +int32_t +svs_readdir (call_frame_t *frame, xlator_t *this, fd_t *fd, size_t size, +             off_t off, dict_t *xdata) +{ +        svs_private_t *priv      = NULL; +        gf_dirent_t    entries; +        int            count     = 0; +        svs_inode_t   *inode_ctx = NULL; +        int            op_errno  = EINVAL; +        int            op_ret    = -1; +        svs_fd_t      *svs_fd    = NULL; +        glfs_fd_t     *glfd      = NULL; + +        GF_VALIDATE_OR_GOTO ("snap-view-server", this, unwind); +        GF_VALIDATE_OR_GOTO (this->name, frame, unwind); +        GF_VALIDATE_OR_GOTO (this->name, fd, unwind); +        GF_VALIDATE_OR_GOTO (this->name, fd->inode, unwind); + +        priv = this->private; + +        inode_ctx = svs_inode_ctx_get (this, fd->inode); +        if (!inode_ctx) { +                gf_log (this->name, GF_LOG_ERROR, "inode context not found in " +                        "the inode %s", uuid_utoa (fd->inode->gfid)); +                op_ret = -1; +                op_errno = EINVAL; +                goto unwind; +        } + +        if (inode_ctx->type == SNAP_VIEW_ENTRY_POINT_INODE) { +                LOCK (&fd->lock); +                { +                        count = svs_fill_readdir (this, &entries, size, off); +                } +                UNLOCK (&fd->lock); +        } else { +                svs_fd = svs_fd_ctx_get_or_new (this, fd); +                if (!svs_fd) { +                        gf_log (this->name, GF_LOG_ERROR, "failed to get the " +                                "fd context %s", uuid_utoa (fd->inode->gfid)); +                        op_ret = -1; +                        op_errno = EBADFD; +                        goto unwind; +                } + +                glfd = svs_fd->fd; + +                LOCK (&fd->lock); +                { +                        count = svs_glfs_readdir (this, glfd, &entries, +                                                  &op_errno, NULL, _gf_false, +                                                  size); +                } +                UNLOCK (&fd->lock); +        } + +        op_ret = count; + +unwind: +        STACK_UNWIND_STRICT (readdir, frame, op_ret, op_errno, &entries, xdata); + +        gf_dirent_free (&entries); + +        return 0; +} + +int32_t +svs_stat (call_frame_t *frame, xlator_t *this, loc_t *loc, dict_t *xdata) +{ +        svs_private_t *priv         = NULL; +        struct iatt    buf          = {0, }; +        int32_t        op_errno     = EINVAL; +        int32_t        op_ret       = -1; +        svs_inode_t   *inode_ctx    = NULL; +        glfs_t        *fs           = NULL; +        glfs_object_t *object       = NULL; +        struct stat    stat         = {0, }; +        int            ret          = -1; + +        GF_VALIDATE_OR_GOTO ("snap-view-daemon", this, out); +        GF_VALIDATE_OR_GOTO (this->name, frame, out); +        GF_VALIDATE_OR_GOTO (this->name, loc, out); +        GF_VALIDATE_OR_GOTO (this->name, loc->inode, out); + +        priv = this->private; + +        /* Instead of doing the check of whether it is a entry point directory +           or not by checking the name of the entry and then deciding what +           to do, just check the inode context and decide what to be done. +        */ + +        inode_ctx = svs_inode_ctx_get (this, loc->inode); +        if (!inode_ctx) { +                gf_log (this->name, GF_LOG_ERROR, "inode context not found for" +                        " %s", uuid_utoa (loc->inode->gfid)); +                op_ret = -1; +                op_errno = EINVAL; +                goto out; +        } + +        if (inode_ctx->type == SNAP_VIEW_ENTRY_POINT_INODE) { +                svs_iatt_fill (loc->inode->gfid, &buf); +                op_ret = 0; +        } else if (inode_ctx->type == SNAP_VIEW_VIRTUAL_INODE) { +                fs = inode_ctx->fs; +                object = inode_ctx->object; +                ret = glfs_h_stat (fs, object, &stat); +                if (ret) { +                        gf_log (this->name, GF_LOG_ERROR, "glfs_h_stat on %s " +                                "(gfid: %s) failed", loc->name, +                                uuid_utoa (loc->inode->gfid)); +                        op_ret = -1; +                        op_errno = errno; +                        goto out; +                } + +                iatt_from_stat (&buf, &stat); +                uuid_copy (buf.ia_gfid, loc->inode->gfid); +                svs_fill_ino_from_gfid (&buf); +                op_ret = ret; +        } + +out: +        STACK_UNWIND_STRICT (stat, frame, op_ret, op_errno, &buf, xdata); +        return 0; +} + +int32_t +svs_fstat (call_frame_t *frame, xlator_t *this, fd_t *fd, dict_t *xdata) +{ +        svs_private_t *priv        = NULL; +        struct iatt    buf         = {0, }; +        int32_t        op_errno    = EINVAL; +        int32_t        op_ret      = -1; +        svs_inode_t   *inode_ctx   = NULL; +        struct stat    stat        = {0, }; +        int            ret         = -1; +        glfs_fd_t     *glfd        = NULL; +        svs_fd_t      *sfd         = NULL; + +        GF_VALIDATE_OR_GOTO ("snap-view-daemon", this, out); +        GF_VALIDATE_OR_GOTO (this->name, frame, out); +        GF_VALIDATE_OR_GOTO (this->name, fd, out); +        GF_VALIDATE_OR_GOTO (this->name, fd->inode, out); + +        priv = this->private; + +        /* Instead of doing the check of whether it is a entry point directory +           or not by checking the name of the entry and then deciding what +           to do, just check the inode context and decide what to be done. +        */ + +        inode_ctx = svs_inode_ctx_get (this, fd->inode); +        if (!inode_ctx) { +                gf_log (this->name, GF_LOG_ERROR, "inode context not found for" +                        " the inode %s", uuid_utoa (fd->inode->gfid)); +                op_ret = -1; +                op_errno = EINVAL; +                goto out; +        } + +        if (inode_ctx->type == SNAP_VIEW_ENTRY_POINT_INODE) { +                svs_iatt_fill (fd->inode->gfid, &buf); +                op_ret = 0; +        } else if (inode_ctx->type == SNAP_VIEW_VIRTUAL_INODE) { +                sfd = svs_fd_ctx_get_or_new (this, fd); +                if (!sfd) { +                        gf_log (this->name, GF_LOG_ERROR, "failed to get the " +                                "fd context for %s", +                                uuid_utoa (fd->inode->gfid)); +                        op_ret = -1; +                        op_errno = EBADFD; +                        goto out; +                } + +                glfd = sfd->fd; +                ret = glfs_fstat (glfd, &stat); +                if (ret) { +                        gf_log (this->name, GF_LOG_ERROR, "glfs_fstat on " +                                "gfid: %s failed", uuid_utoa (fd->inode->gfid)); +                        op_ret = -1; +                        op_errno = errno; +                        goto out; +                } + +                iatt_from_stat (&buf, &stat); +                uuid_copy (buf.ia_gfid, fd->inode->gfid); +                svs_fill_ino_from_gfid (&buf); +                op_ret = ret; +        } + +out: +        STACK_UNWIND_STRICT (fstat, frame, op_ret, op_errno, &buf, xdata); +        return 0; +} + +int32_t +svs_open (call_frame_t *frame, xlator_t *this, loc_t *loc, int32_t flags, +          fd_t *fd, dict_t *xdata) +{ +        int32_t        ret       = -1; +        svs_inode_t   *inode_ctx = NULL; +        svs_fd_t      *sfd       = NULL; +        int32_t        op_ret    = -1; +        int32_t        op_errno  = EINVAL; +        glfs_fd_t     *glfd      = NULL; +        glfs_t        *fs        = NULL; +        glfs_object_t *object    = NULL; + + +        GF_VALIDATE_OR_GOTO ("snap-view-daemon", this, out); +        GF_VALIDATE_OR_GOTO (this->name, frame, out); +        GF_VALIDATE_OR_GOTO (this->name, fd, out); +        GF_VALIDATE_OR_GOTO (this->name, loc, out); +        GF_VALIDATE_OR_GOTO (this->name, loc->inode, out); + +        inode_ctx = svs_inode_ctx_get (this, loc->inode); +        if (!inode_ctx) { +                gf_log (this->name, GF_LOG_ERROR, "inode context for %s " +                        "(gfid: %s) not found", loc->name, +                        uuid_utoa (loc->inode->gfid)); +                goto out; +        } + +        if (inode_ctx->type == SNAP_VIEW_ENTRY_POINT_INODE) +                GF_ASSERT (0); // on entry point it should always be opendir + +        fs = inode_ctx->fs; +        object = inode_ctx->object; + +        glfd = glfs_h_open (fs, object, flags); +        if (!glfd) { +                gf_log (this->name, GF_LOG_ERROR, "glfs_h_open on %s failed " +                        "(gfid: %s)", loc->name, uuid_utoa (loc->inode->gfid)); +                op_ret = -1; +                op_errno = errno; +                goto out; +        } + +        sfd = svs_fd_ctx_get_or_new (this, fd); +        if (!sfd) { +                gf_log (this->name, GF_LOG_ERROR, "failed to allocate fd " +                        "context for %s (gfid: %s)", loc->name, +                        uuid_utoa (loc->inode->gfid)); +                op_ret = -1; +                op_errno = ENOMEM; +                glfs_close (glfd); +                goto out; +        } +        sfd->fd = glfd; + +        op_ret = 0; + +out: +        STACK_UNWIND_STRICT (open, frame, op_ret, op_errno, fd, NULL); +        return 0; +} + +int32_t +svs_readv (call_frame_t *frame, xlator_t *this, +           fd_t *fd, size_t size, off_t offset, uint32_t flags, dict_t *xdata) +{ +        int32_t                op_ret     = -1; +        int32_t                op_errno   = 0; +        svs_private_t         *priv       = NULL; +        struct iobuf          *iobuf      = NULL; +        struct iobref         *iobref     = NULL; +        struct iovec           vec        = {0,}; +        svs_fd_t              *sfd        = NULL; +        int                    ret        = -1; +        struct stat            fstatbuf   = {0, }; +        glfs_fd_t             *glfd       = NULL; +        struct iatt            stbuf      = {0, }; + +        GF_VALIDATE_OR_GOTO ("snap-view-daemon", this, out); +        GF_VALIDATE_OR_GOTO (this->name, frame, out); +        GF_VALIDATE_OR_GOTO (this->name, fd, out); +        GF_VALIDATE_OR_GOTO (this->name, fd->inode, out); + +        priv = this->private; +        VALIDATE_OR_GOTO (priv, out); + +        sfd = svs_fd_ctx_get_or_new (this, fd); +        if (!sfd) { +                gf_log (this->name, GF_LOG_ERROR, "failed to get the fd " +                        "context for %s", uuid_utoa (fd->inode->gfid)); +                op_ret = -1; +                op_errno = EBADFD; +                goto out; +        } + +        glfd = sfd->fd; + +        iobuf = iobuf_get2 (this->ctx->iobuf_pool, size); +        if (!iobuf) { +                op_ret = -1; +                op_errno = ENOMEM; +                goto out; +        } + +        ret = glfs_pread (glfd, iobuf->ptr, size, offset, 0); +        if (ret < 0) { +                op_ret = -1; +                op_errno = errno; +                gf_log (this->name, GF_LOG_ERROR, "glfs_read failed (%s)", +                        strerror (op_errno)); +                goto out; +        } + +        vec.iov_base = iobuf->ptr; +        vec.iov_len  = ret; + +        iobref = iobref_new (); + +        iobref_add (iobref, iobuf); + +        ret = glfs_fstat (glfd, &fstatbuf); +        if (ret) { +                op_ret = -1; +                op_errno = errno; +                gf_log (this->name, GF_LOG_ERROR, "glfs_fstat failed after " +                        "readv on %s", uuid_utoa (fd->inode->gfid)); +                goto out; +        } + +        iatt_from_stat (&stbuf, &fstatbuf); +        uuid_copy (stbuf.ia_gfid, fd->inode->gfid); +        svs_fill_ino_from_gfid (&stbuf); + +        /* Hack to notify higher layers of EOF. */ +        if (!stbuf.ia_size || (offset + vec.iov_len) >= stbuf.ia_size) +                op_errno = ENOENT; + +        op_ret = vec.iov_len; + +out: + +        STACK_UNWIND_STRICT (readv, frame, op_ret, op_errno, +                             &vec, 1, &stbuf, iobref, NULL); + +        if (iobref) +                iobref_unref (iobref); +        if (iobuf) +                iobuf_unref (iobuf); + +        return 0; +} + +int32_t +svs_readlink (call_frame_t *frame, xlator_t *this, +              loc_t *loc, size_t size, dict_t *xdata) +{ +        svs_inode_t     *inode_ctx = NULL; +        glfs_t          *fs        = NULL; +        glfs_object_t   *object    = NULL; +        int              op_ret    = -1; +        int              op_errno  = EINVAL; +        char            *buf       = NULL; +        struct iatt      stbuf     = {0, }; +        int              ret       = -1; +        struct stat      stat      = {0, }; + +        GF_VALIDATE_OR_GOTO ("snap-view-daemon", this, out); +        GF_VALIDATE_OR_GOTO (this->name, frame, out); +        GF_VALIDATE_OR_GOTO (this->name, loc, out); +        GF_VALIDATE_OR_GOTO (this->name, loc->inode, out); + +        inode_ctx = svs_inode_ctx_get (this, loc->inode); +        if (!inode_ctx) { +                gf_log (this->name, GF_LOG_ERROR, "failed to get inode context " +                        "for %s (gfid: %s)", loc->name, +                        uuid_utoa (loc->inode->gfid)); +                op_ret = -1; +                op_errno = EINVAL; +                goto out; +        } + +        fs = inode_ctx->fs; +        if (!fs) { +                gf_log (this->name, GF_LOG_ERROR, "failed to get the fs " +                        "instance for %s (gfid: %s)", loc->name, +                        uuid_utoa (loc->inode->gfid)); +                op_ret = -1; +                op_errno = ESTALE; +                goto out; +        } + +        object = inode_ctx->object; +        if (!object) { +                gf_log (this->name, GF_LOG_ERROR, "failed to get the object " +                        "for %s (gfid: %s)", loc->name, +                        uuid_utoa (loc->inode->gfid)); +                op_ret = -1; +                op_errno = ESTALE; +                goto out; +        } + +        ret = glfs_h_stat (fs, object, &stat); +        if (ret) { +                gf_log (this->name, GF_LOG_ERROR, "glfs_h_stat on %s " +                        "(gfid: %s) failed", loc->name, +                        uuid_utoa (loc->inode->gfid)); +                op_ret = -1; +                op_errno = errno; +                goto out; +        } + +        iatt_from_stat (&stbuf, &stat); +        uuid_copy (stbuf.ia_gfid, loc->inode->gfid); +        svs_fill_ino_from_gfid (&stbuf); + +        buf = alloca (size + 1); +        op_ret = glfs_h_readlink (fs, object, buf, size); +        if (op_ret == -1) { +                gf_log (this->name, GF_LOG_ERROR, "readlink on %s failed " +                        "(gfid: %s)", loc->name, uuid_utoa (loc->inode->gfid)); +                op_errno = errno; +                goto out; +        } + +        buf[op_ret] = 0; + +out: +        STACK_UNWIND_STRICT (readlink, frame, op_ret, op_errno, buf, &stbuf, +                             NULL); + +        return 0; +} + +int32_t +svs_access (call_frame_t *frame, xlator_t *this, loc_t *loc, int mask, +            dict_t *xdata) +{ +        int             ret          = -1; +        int32_t         op_ret       = -1; +        int32_t         op_errno     = EINVAL; +        svs_private_t  *priv         = NULL; +        glfs_t         *fs           = NULL; +        glfs_object_t  *object       = NULL; +        svs_inode_t    *inode_ctx    = NULL; +        gf_boolean_t    is_fuse_call = 0; +        int             perm         = 0; +        int             mode         = 0; + +        GF_VALIDATE_OR_GOTO ("svs", this, out); +        GF_VALIDATE_OR_GOTO (this->name, this->private, out); +        GF_VALIDATE_OR_GOTO (this->name, loc, out); +        GF_VALIDATE_OR_GOTO (this->name, loc->inode, out); + +        priv = this->private; + +        inode_ctx = svs_inode_ctx_get (this, loc->inode); +        if (!inode_ctx) { +                gf_log (this->name, GF_LOG_ERROR, "inode context not found for" +                        " %s", uuid_utoa (loc->inode->gfid)); +                op_ret = -1; +                op_errno = EINVAL; +                goto out; +        } + +        is_fuse_call = __is_fuse_call (frame); + +        if (inode_ctx->type == SNAP_VIEW_ENTRY_POINT_INODE) { +                if (is_fuse_call) { +                        op_ret = 0; +                        op_errno = 0; +                } else { +                        op_ret = 0; +                        mode |= POSIX_ACL_READ; +                        mode |= POSIX_ACL_EXECUTE; +                        op_errno = mode; +                } +                goto out; +        } + +        fs = inode_ctx->fs; +        object = inode_ctx->object; + +        if (!is_fuse_call) +                syncopctx_setfspid (&frame->root->pid); + +        ret = glfs_h_access (fs, object, mask); +        if (ret < 0) { +                gf_log (this->name, GF_LOG_ERROR, "failed to access %s " +                        "(gfid: %s)", loc->path, uuid_utoa (loc->inode->gfid)); +                op_ret = -1; +                op_errno = errno; +                goto out; +        } + +        /* The actual posix_acl xlator does acl checks differently for +           fuse and nfs. In this case how to send the information of +           whether the call came from fuse or nfs to the snapshot volume +           via gfapi? +        */ + +        op_ret = 0; +        op_errno = ret; + +out: + +        STACK_UNWIND_STRICT (access, frame, op_ret, op_errno, NULL); +        return 0; +} + + +int32_t +mem_acct_init (xlator_t *this) +{ +        int     ret = -1; + +        if (!this) +                return ret; + +        ret = xlator_mem_acct_init (this, gf_svs_mt_end + 1); + +        if (ret != 0) { +                gf_log (this->name, GF_LOG_WARNING, "Memory accounting" +                        " init failed"); +                return ret; +        } + +        return ret; +} + +int32_t +init (xlator_t *this) +{ +        svs_private_t *priv = NULL; +        int ret = -1; + +        /* This can be the top of graph in certain cases */ +        if (!this->parents) { +                gf_log (this->name, GF_LOG_DEBUG, +                        "dangling volume. check volfile "); +        } + +        /* TODO: define a mem-type structure */ +        priv = GF_CALLOC (1, sizeof (*priv), gf_svs_mt_priv_t); +        if (!priv) +                goto out; + +        this->private = priv; + +        /* get the list of snaps first to return to client xlator */ +        ret = svs_get_snapshot_list (this, priv); + +        ret = 0; +out: +        if (ret && priv) { +                GF_FREE (priv); +        } + +        return ret; +} + +void +fini (xlator_t *this) +{ +        svs_private_t *priv = NULL; +        priv = this->private; +        this->private = NULL; + +        if (priv) { +                GF_FREE (priv); +        } + +        return; +} + +struct xlator_fops fops = { +        .lookup     = svs_lookup, +        .stat       = svs_stat, +        .opendir    = svs_opendir, +        .readdirp   = svs_readdirp, +        .readdir    = svs_readdir, +        .open       = svs_open, +        .readv      = svs_readv, +        .flush      = svs_flush, +        .fstat      = svs_fstat, +        .getxattr   = svs_getxattr, +        .access     = svs_access, +        /* entry fops */ +}; + +struct xlator_cbks cbks = { +        .release  = svs_release, +        .releasedir = svs_releasedir, +        .forget     = svs_forget, +}; + +struct volume_options options[] = { +        /* This translator doesn't take any options, or provide any options */ +        { .key  = {NULL} }, +}; diff --git a/xlators/features/snapview-server/src/snapview-server.h b/xlators/features/snapview-server/src/snapview-server.h new file mode 100644 index 00000000000..b0e051e0fc3 --- /dev/null +++ b/xlators/features/snapview-server/src/snapview-server.h @@ -0,0 +1,88 @@ +/* +   Copyright (c) 2014 Red Hat, Inc. <http://www.redhat.com> +   This file is part of GlusterFS. + +   This file is licensed to you under your choice of the GNU Lesser +   General Public License, version 3 or any later version (LGPLv3 or +   later), or the GNU General Public License, version 2 (GPLv2), in all +   cases as published by the Free Software Foundation. +*/ +#ifndef __SNAP_VIEW_H__ +#define __SNAP_VIEW_H__ + +#ifndef _CONFIG_H +#define _CONFIG_H +#include "config.h" +#endif + +#include "dict.h" +#include "defaults.h" +#include "mem-types.h" +#include "call-stub.h" +#include "inode.h" +#include "byte-order.h" +#include "iatt.h" +#include <ctype.h> +#include <sys/uio.h> +#include "glusterfs.h" +#include "xlator.h" +#include "logging.h" +#include "glfs.h" +#include "common-utils.h" +#include "glfs-handles.h" +#include "glfs-internal.h" +#include "glusterfs3-xdr.h" +#include "glusterfs-acl.h" +#include "syncop.h" +#include "list.h" + +/* + * The max number of snap entries we consider currently + */ +#define SNAP_VIEW_MAX_NUM_SNAPS 128 + +#define DEFAULT_SVD_LOG_FILE_DIRECTORY DATADIR "/log/glusterfs" + +typedef enum { +        SNAP_VIEW_ENTRY_POINT_INODE = 0, +        SNAP_VIEW_VIRTUAL_INODE +} inode_type_t; + +struct svs_inode { +        glfs_t *fs; +        glfs_object_t *object; +        inode_type_t type; + +        /* used only for entry point directory where gfid of the directory +           from where the entry point was entered is saved. +        */ +        uuid_t pargfid; +        struct iatt buf; +}; +typedef struct svs_inode svs_inode_t; + +struct svs_fd { +        glfs_fd_t *fd; +}; +typedef struct svs_fd svs_fd_t; + +struct snap_dirent { +	char name[NAME_MAX]; +	char uuid[UUID_CANONICAL_FORM_LEN + 1]; +        glfs_t *fs; +}; +typedef struct snap_dirent snap_dirent_t; + +struct svs_private { +	snap_dirent_t *dirents; +	int num_snaps; +}; +typedef struct svs_private svs_private_t; + +glfs_t * +svs_intialise_snapshot_volume (xlator_t *this, const char *name); + +snap_dirent_t * +svs_get_snap_dirent (xlator_t *this, const char *name); + +#endif /* __SNAP_VIEW_H__ */ diff --git a/xlators/mount/fuse/src/fuse-resolve.c b/xlators/mount/fuse/src/fuse-resolve.c index 76b1d9a72cc..fc04d2c8efa 100644 --- a/xlators/mount/fuse/src/fuse-resolve.c +++ b/xlators/mount/fuse/src/fuse-resolve.c @@ -47,6 +47,11 @@ fuse_resolve_loc_touchup (fuse_state_t *state)                  } else if (loc->inode) {                          ret = inode_path (loc->inode, NULL, &path);  			uuid_copy (loc->gfid, loc->inode->gfid); +                        if (path) { +                                loc->name = strrchr (path, '/'); +                                if (loc->name) +                                        loc->name++; +                        }                  }                  if (ret)                          gf_log (THIS->name, GF_LOG_TRACE, diff --git a/xlators/protocol/server/src/server-resolve.c b/xlators/protocol/server/src/server-resolve.c index b2bff5c531a..9384e765cca 100644 --- a/xlators/protocol/server/src/server-resolve.c +++ b/xlators/protocol/server/src/server-resolve.c @@ -45,8 +45,14 @@ resolve_loc_touchup (call_frame_t *frame)          if (!loc->path) {                  if (loc->parent && resolve->bname) {                          ret = inode_path (loc->parent, resolve->bname, &path); +                        loc->name = resolve->bname;                  } else if (loc->inode) {                          ret = inode_path (loc->inode, NULL, &path); +                        if (path) { +                                loc->name = strrchr (path, '/'); +                                if (loc->name) +                                        loc->name++; +                        }                  }                  if (ret)                          gf_log (frame->this->name, GF_LOG_TRACE, @@ -123,15 +129,29 @@ resolve_gfid_cbk (call_frame_t *frame, void *cookie, xlator_t *this,                  goto out;          } -        loc_wipe (resolve_loc); -          link_inode = inode_link (inode, NULL, NULL, buf); -        if (!link_inode) +        if (!link_inode) { +                loc_wipe (resolve_loc);                  goto out; +        }          inode_lookup (link_inode); +        /* wipe the loc only after the inode has been linked to the inode +           table. Otherwise before inode gets linked to the inode table, +           inode would have been unrefed (this might have been destroyed +           if refcount becomes 0, and put back to mempool). So once the +           inode gets destroyed, inode_link is a redundant operation. But +           without knowing that the destroyed inode's pointer is saved in +           the resolved_loc as parent (while constructing loc for resolving +           the entry) and the inode_new call for resolving the entry will +           return the same pointer to the inode as the parent (because in +           reality the inode is a free inode present in cold list of the +           inode mem-pool). +        */ +        loc_wipe (resolve_loc); +          if (uuid_is_null (resolve->pargfid)) {                  inode_unref (link_inode);                  goto out; @@ -143,13 +163,14 @@ resolve_gfid_cbk (call_frame_t *frame, void *cookie, xlator_t *this,          resolve_loc->name = resolve->bname;          resolve_loc->inode = inode_new (state->itable); +          inode_path (resolve_loc->parent, resolve_loc->name,                      (char **) &resolve_loc->path);          STACK_WIND (frame, resolve_gfid_entry_cbk,                      frame->root->client->bound_xl,                      frame->root->client->bound_xl->fops->lookup, -                    &resolve->resolve_loc, NULL); +                    &resolve->resolve_loc, state->xdata);          return 0;  out:          resolve_continue (frame); @@ -182,7 +203,7 @@ resolve_gfid (call_frame_t *frame)          STACK_WIND (frame, resolve_gfid_cbk,                      frame->root->client->bound_xl,                      frame->root->client->bound_xl->fops->lookup, -                    &resolve->resolve_loc, NULL); +                    &resolve->resolve_loc, state->xdata);          return 0;  } diff --git a/xlators/protocol/server/src/server-rpc-fops.c b/xlators/protocol/server/src/server-rpc-fops.c index c77748d69f1..f36bc16fc43 100644 --- a/xlators/protocol/server/src/server-rpc-fops.c +++ b/xlators/protocol/server/src/server-rpc-fops.c @@ -1897,6 +1897,8 @@ server_readdirp_cbk (call_frame_t *frame, void *cookie, xlator_t *this,          rpcsvc_request_t    *req   = NULL;          int                  ret   = 0; +        state = CALL_STATE (frame); +          GF_PROTOCOL_DICT_SERIALIZE (this, xdata, &rsp.xdata.xdata_val,                                      rsp.xdata.xdata_len, op_errno, out); @@ -1920,8 +1922,7 @@ server_readdirp_cbk (call_frame_t *frame, void *cookie, xlator_t *this,                  }          } -        /* TODO: need more clear thoughts before calling this function. */ -        /* gf_link_inodes_from_dirent (this, state->fd->inode, entries); */ +        gf_link_inodes_from_dirent (this, state->fd->inode, entries);  out:          rsp.op_ret    = op_ret;  | 
