diff options
7 files changed, 722 insertions, 9 deletions
diff --git a/doc/admin-guide/en-US/markdown/admin_managing_snapshots.md b/doc/admin-guide/en-US/markdown/admin_managing_snapshots.md index 820dcb18863..b155bba0555 100644 --- a/doc/admin-guide/en-US/markdown/admin_managing_snapshots.md +++ b/doc/admin-guide/en-US/markdown/admin_managing_snapshots.md @@ -248,4 +248,52 @@ world) can be changed using the below command.  *gluster volume set <volname> snapshot-directory <new-name>* +3) Accessing from windows: +The glusterfs volumes can be made accessible by windows via samba. (the +glusterfs plugin for samba helps achieve this, without having to re-export +a fuse mounted glusterfs volume). The snapshots of a glusterfs volume can +also be viewed in the windows explorer. + +There are 2 ways: +* Give the path of the entry point directory +(\\<hostname>\<samba-share>\<directory>\<entry-point path>) in the run command +window +* Go to the samba share via windows explorer. Make hidden files and folders +visible so that in the root of the samba share a folder icon for the entry point +can be seen. + +NOTE: From the explorer, snapshot world can be entered via entry point only from +the root of the samba share. If snapshots have to be seen from subfolders, then +the path should be provided in the run command window. + +For snapshots to be accessible from windows, below 2 options can be used. +A) The glusterfs plugin for samba should give the option "snapdir-entry-path" +while starting. The option is an indication to glusterfs, that samba is loading +it and the value of the option should be the path that is being used as the +share for windows. +Ex: Say, there is a glusterfs volume and a directory called "export" from the +root of the volume is being used as the samba share, then samba has to load +glusterfs with this option as well. + +        ret = glfs_set_xlator_option(fs, "*-snapview-client", +                                     "snapdir-entry-path", "/export"); +The xlator option "snapdir-entry-path" is not exposed via volume set options, +cannot be changed from CLI. Its an option that has to be provded at the time of +mounting glusterfs or when samba loads glusterfs. +B) The accessibility of snapshots via root of the samba share from windows +is configurable. By default it is turned off. It is a volume set option which can +be changed via CLI. + +gluster volume set <volname> features.show-snapshot-directory "on/off". By +default it is off. + +Only when both the above options have been provided (i.e snapdir-entry-path +contains a valid unix path that is exported and show-snapshot-directory option +is set to true), snapshots can accessed via windows explorer. + +If only 1st option (i.e. snapdir-entry-path) is set via samba and 2nd option +(i.e. show-snapshot-directory) is off, then snapshots can be accessed from +windows via the run command window, but not via the explorer. + +  -------------------------------------------------------------------------------------- diff --git a/tests/bugs/bug-1168875.t b/tests/bugs/bug-1168875.t new file mode 100644 index 00000000000..0a7476db87b --- /dev/null +++ b/tests/bugs/bug-1168875.t @@ -0,0 +1,96 @@ +#!/bin/bash + +. $(dirname $0)/../include.rc +. $(dirname $0)/../volume.rc +. $(dirname $0)/../snapshot.rc +. $(dirname $0)/../fileio.rc +. $(dirname $0)/../nfs.rc + +cleanup; + +function check_entry_point_exists () +{ +        local entry_point=$1; +        local _path=$2; + +        ls -a $_path | grep $entry_point; + +        if [ $? -eq 0 ]; then +            echo 'Y'; +        else +            echo 'N'; +        fi +} + +TEST init_n_bricks 3; +TEST setup_lvm 3; + +TEST glusterd; + +TEST pidof glusterd; + +TEST $CLI volume create $V0 $H0:$L1 $H0:$L2 $H0:$L3; + +TEST $CLI volume start $V0; + +TEST glusterfs --volfile-server=$H0 --volfile-id=$V0 --xlator-option *-snapview-client.snapdir-entry-path=/dir $M0; + +TEST glusterfs --volfile-server=$H0 --volfile-id=$V0 $N0; +for i in {1..10} ; do echo "file" > $M0/file$i ; done + + +for i in {11..20} ; do echo "file" > $M0/file$i ; done + +mkdir $M0/dir; + +for i in {1..10} ; do echo "file" > $M0/dir/file$i ; done + +mkdir $M0/dir1; +mkdir $M0/dir2; + +for i in {1..10} ; do echo "foo" > $M0/dir1/foo$i ; done +for i in {1..10} ; do echo "foo" > $M0/dir2/foo$i ; done + +for i in {11..20} ; do echo "foo" > $M0/dir1/foo$i ; done +for i in {11..20} ; do echo "foo" > $M0/dir2/foo$i ; done + +TEST $CLI snapshot create snap1 $V0; +TEST $CLI snapshot activate snap1; + +TEST $CLI volume set $V0 features.uss enable; + +EXPECT_WITHIN $PROCESS_UP_TIMEOUT 'Y' check_if_snapd_exist + +EXPECT_WITHIN $PROCESS_UP_TIMEOUT 'N' check_entry_point_exists .snaps $M0/dir +EXPECT_WITHIN $PROCESS_UP_TIMEOUT 'N' check_entry_point_exists .snaps $N0/dir + +EXPECT_WITHIN $PROCESS_UP_TIMEOUT 'N' check_entry_point_exists .snaps $M0/dir1 +EXPECT_WITHIN $PROCESS_UP_TIMEOUT 'N' check_entry_point_exists .snaps $N0/dir1 + +TEST $CLI volume set $V0 features.show-snapshot-directory enable; + +EXPECT_WITHIN $PROCESS_UP_TIMEOUT "0" STAT $M0/dir +EXPECT_WITHIN $PROCESS_UP_TIMEOUT "0" STAT $N0/dir +EXPECT_WITHIN $PROCESS_UP_TIMEOUT "0" STAT $M0/dir1 +EXPECT_WITHIN $PROCESS_UP_TIMEOUT "0" STAT $N0/dir1 + +EXPECT_WITHIN $PROCESS_UP_TIMEOUT 'Y' check_entry_point_exists ".snaps" $M0/dir +EXPECT_WITHIN $PROCESS_UP_TIMEOUT 'N' check_entry_point_exists ".snaps" $N0/dir + +EXPECT_WITHIN $PROCESS_UP_TIMEOUT 'N' check_entry_point_exists ".snaps" $M0/dir1 +EXPECT_WITHIN $PROCESS_UP_TIMEOUT 'N' check_entry_point_exists ".snaps" $N0/dir1 + +TEST $CLI volume set $V0 features.show-snapshot-directory disable; + +EXPECT_WITHIN $PROCESS_UP_TIMEOUT "0" STAT $M0/dir +EXPECT_WITHIN $PROCESS_UP_TIMEOUT "0" STAT $N0/dir +EXPECT_WITHIN $PROCESS_UP_TIMEOUT "0" STAT $M0/dir1 +EXPECT_WITHIN $PROCESS_UP_TIMEOUT "0" STAT $N0/dir1 + +EXPECT_WITHIN $PROCESS_UP_TIMEOUT 'N' check_entry_point_exists ".snaps" $M0/dir +EXPECT_WITHIN $PROCESS_UP_TIMEOUT 'N' check_entry_point_exists ".snaps" $N0/dir + +EXPECT_WITHIN $PROCESS_UP_TIMEOUT 'N' check_entry_point_exists ".snaps" $M0/dir1 +EXPECT_WITHIN $PROCESS_UP_TIMEOUT 'N' check_entry_point_exists ".snaps" $N0/dir1 + +cleanup; diff --git a/xlators/features/snapview-client/src/snapview-client-mem-types.h b/xlators/features/snapview-client/src/snapview-client-mem-types.h index b6fa0ef7cfb..1a0158d950e 100644 --- a/xlators/features/snapview-client/src/snapview-client-mem-types.h +++ b/xlators/features/snapview-client/src/snapview-client-mem-types.h @@ -17,6 +17,7 @@ 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_svc_fd_t,          gf_svc_mt_end  }; diff --git a/xlators/features/snapview-client/src/snapview-client.c b/xlators/features/snapview-client/src/snapview-client.c index 849cab390fa..2fa3660d38e 100644 --- a/xlators/features/snapview-client/src/snapview-client.c +++ b/xlators/features/snapview-client/src/snapview-client.c @@ -1,4 +1,4 @@ -/* + /*     Copyright (c) 2014 Red Hat, Inc. <http://www.redhat.com>     This file is part of GlusterFS. @@ -23,6 +23,10 @@ svc_local_free (svc_local_t *local)  {          if (local) {                  loc_wipe (&local->loc); +                if (local->fd) +                        fd_unref (local->fd); +                if (local->xdata) +                        dict_unref (local->xdata);                  mem_put (local);          }  } @@ -115,6 +119,150 @@ out:          return ret;  } +svc_fd_t * +svc_fd_new (void) +{ +        svc_fd_t    *svc_fd = NULL; + +        svc_fd = GF_CALLOC (1, sizeof (*svc_fd), gf_svc_mt_svc_fd_t); + +        return svc_fd; +} + +svc_fd_t * +__svc_fd_ctx_get (xlator_t *this, fd_t *fd) +{ +        svc_fd_t *svc_fd = NULL; +        uint64_t  value  = 0; +        int       ret    = -1; + +        GF_VALIDATE_OR_GOTO ("snapview-client", this, out); +        GF_VALIDATE_OR_GOTO (this->name, fd, out); + +        ret = __fd_ctx_get (fd, this, &value); +        if (ret) +                return NULL; + +        svc_fd = (svc_fd_t *) ((long) value); + +out: +        return svc_fd; +} + +svc_fd_t * +svc_fd_ctx_get (xlator_t *this, fd_t *fd) +{ +        svc_fd_t *svc_fd = NULL; + +        GF_VALIDATE_OR_GOTO ("snapview-client", this, out); +        GF_VALIDATE_OR_GOTO (this->name, fd, out); + +        LOCK (&fd->lock); +        { +                svc_fd = __svc_fd_ctx_get (this, fd); +        } +        UNLOCK (&fd->lock); + +out: +        return svc_fd; +} + +int +__svc_fd_ctx_set (xlator_t *this, fd_t *fd, svc_fd_t *svc_fd) +{ +        uint64_t    value = 0; +        int         ret   = -1; + +        GF_VALIDATE_OR_GOTO ("snapview-client", this, out); +        GF_VALIDATE_OR_GOTO (this->name, fd, out); +        GF_VALIDATE_OR_GOTO (this->name, svc_fd, out); + +        value = (uint64_t)(long) svc_fd; + +        ret = __fd_ctx_set (fd, this, value); + +out: +        return ret; +} + +int32_t +svc_fd_ctx_set (xlator_t *this, fd_t *fd, svc_fd_t *svc_fd) +{ +        int32_t    ret = -1; + +        GF_VALIDATE_OR_GOTO ("snapview-client", this, out); +        GF_VALIDATE_OR_GOTO (this->name, fd, out); +        GF_VALIDATE_OR_GOTO (this->name, svc_fd, out); + +        LOCK (&fd->lock); +        { +                ret = __svc_fd_ctx_set (this, fd, svc_fd); +        } +        UNLOCK (&fd->lock); + +out: +        return ret; +} + +svc_fd_t * +__svc_fd_ctx_get_or_new (xlator_t *this, fd_t *fd) +{ +        svc_fd_t        *svc_fd    = NULL; +        int              ret       = -1; +        inode_t         *inode     = NULL; + +        GF_VALIDATE_OR_GOTO ("snapview-client", this, out); +        GF_VALIDATE_OR_GOTO (this->name, fd, out); + +        inode = fd->inode; +        svc_fd = __svc_fd_ctx_get (this, fd); +        if (svc_fd) { +                ret = 0; +                goto out; +        } + +        svc_fd = svc_fd_new (); +        if (!svc_fd) { +                gf_log (this->name, GF_LOG_ERROR, "failed to allocate new fd " +                        "context for gfid %s", uuid_utoa (inode->gfid)); +                goto out; +        } + +        ret = __svc_fd_ctx_set (this, fd, svc_fd); +        if (ret) { +                gf_log (this->name, GF_LOG_ERROR, "failed to set fd context " +                        "for gfid %s", uuid_utoa (inode->gfid)); +                ret = -1; +        } + +out: +        if (ret) { +                GF_FREE (svc_fd); +                svc_fd = NULL; +        } + +        return svc_fd; +} + +svc_fd_t * +svc_fd_ctx_get_or_new (xlator_t *this, fd_t *fd) +{ +        svc_fd_t  *svc_fd = NULL; + +        GF_VALIDATE_OR_GOTO ("snapview-client", this, out); +        GF_VALIDATE_OR_GOTO (this->name, fd, out); + +        LOCK (&fd->lock); +        { +                svc_fd = __svc_fd_ctx_get_or_new (this, fd); +        } +        UNLOCK (&fd->lock); + +out: +        return svc_fd; +} + +  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, @@ -337,7 +485,7 @@ svc_lookup (call_frame_t *frame, xlator_t *this, loc_t *loc,  out:          if (wind)                  STACK_WIND (frame, svc_lookup_cbk, -                             subvolume, subvolume->fops->lookup, loc, xdata); +                            subvolume, subvolume->fops->lookup, loc, xdata);          else                  SVC_STACK_UNWIND (lookup, frame, op_ret, op_errno, NULL,                                    NULL, NULL, NULL); @@ -413,6 +561,63 @@ out:          return ret;  } +int32_t +svc_opendir_cbk (call_frame_t *frame, void *cookie, xlator_t *this, +                 int32_t op_ret, int32_t op_errno, fd_t *fd, dict_t *xdata) +{ +        svc_fd_t        *svc_fd          = NULL; +        svc_local_t     *local           = NULL; +        svc_private_t   *priv            = NULL; +        gf_boolean_t     special_dir     = _gf_false; +        char             path[PATH_MAX]  = {0, }; + +        GF_VALIDATE_OR_GOTO ("snapview-client", this, out); +        GF_VALIDATE_OR_GOTO (this->name, this->private, out); + +        if (op_ret) +                goto out; + +        priv = this->private; +        local = frame->local; + +        if (local->subvolume == FIRST_CHILD (this) && priv->special_dir +            && strcmp (priv->special_dir, "")) { +                if (!__is_root_gfid (fd->inode->gfid)) +                        snprintf (path, sizeof (path), "%s/.", +                                  priv->special_dir); +                else +                        snprintf (path, sizeof (path), "/."); + +                if (!strcmp (local->loc.path, priv->special_dir) || +                    !strcmp (local->loc.path, path)) { +                        gf_log_callingfn (this->name, GF_LOG_DEBUG, +                                          "got opendir on special " +                                          "directory %s (%s)", path, +                                          uuid_utoa (fd->inode->gfid)); +                        special_dir = _gf_true; +                } +        } + +        if (special_dir) { +                svc_fd = svc_fd_ctx_get_or_new (this, fd); +                if (!svc_fd) { +                        gf_log (this->name, GF_LOG_ERROR, +                                "fd context not found for %s", +                                uuid_utoa (fd->inode->gfid)); +                        goto out; +                } + +                svc_fd->last_offset = -1; +                svc_fd->special_dir = special_dir; +        } + +out: +        STACK_UNWIND_STRICT (opendir, frame, op_ret, op_errno, fd, xdata); + +        return 0; +} + +  /* 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 @@ -433,6 +638,7 @@ svc_opendir (call_frame_t *frame, xlator_t *this, loc_t *loc, fd_t *fd,          int            op_ret     = -1;          int            op_errno   = EINVAL;          gf_boolean_t   wind       = _gf_false; +        svc_local_t   *local      = NULL;          GF_VALIDATE_OR_GOTO ("svc", this, out);          GF_VALIDATE_OR_GOTO (this->name, frame, out); @@ -440,11 +646,24 @@ svc_opendir (call_frame_t *frame, xlator_t *this, loc_t *loc, fd_t *fd,          GF_VALIDATE_OR_GOTO (this->name, loc->inode, out);          GF_VALIDATE_OR_GOTO (this->name, fd, out); +        local = mem_get0 (this->local_pool); +        if (!local) { +                gf_log (this->name, GF_LOG_ERROR, "failed to allocate memory " +                        "for local (path: %s, gfid: %s)", loc->path, +                        uuid_utoa (fd->inode->gfid)); +                op_errno = ENOMEM; +                goto 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); +        loc_copy (&local->loc, loc); +        local->subvolume = subvolume; +        frame->local = local; + +        STACK_WIND (frame, svc_opendir_cbk, subvolume, subvolume->fops->opendir, +                    loc, fd, xdata);          wind = _gf_true; @@ -586,6 +805,7 @@ svc_getxattr (call_frame_t *frame, xlator_t *this, loc_t *loc, const char *name,                          ret = dict_set_dynstr_with_alloc (dict,                                          (char *)name,                                          priv->path); +                          if (ret) {                                  op_errno = ENOMEM;                                  dict_unref (dict); @@ -612,6 +832,9 @@ out:                  SVC_STACK_UNWIND (getxattr, frame, op_ret, op_errno,                                    dict, NULL); +        if (dict) +                dict_unref (dict); +          return 0;  } @@ -1264,12 +1487,29 @@ svc_readdir (call_frame_t *frame, xlator_t *this, fd_t *fd,          int           op_ret     = -1;          int           op_errno   = EINVAL;          gf_boolean_t  wind       = _gf_false; +        svc_fd_t     *svc_fd     = NULL; +        gf_dirent_t   entries; + +        INIT_LIST_HEAD (&entries);          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_fd = svc_fd_ctx_get_or_new (this,  fd); +        if (!svc_fd) +                gf_log (this->name, GF_LOG_ERROR, "failed to get the fd " +                        "context for the inode %s", +                        uuid_utoa (fd->inode->gfid)); +        else { +                if (svc_fd->entry_point_handled && off == svc_fd->last_offset) { +                        op_ret = 0; +                        op_errno = ENOENT; +                        goto out; +                } +        } +          SVC_GET_SUBVOL_FROM_CTX (this, op_ret, op_errno, inode_type, ret,                                   fd->inode, subvolume, out); @@ -1280,11 +1520,211 @@ svc_readdir (call_frame_t *frame, xlator_t *this, fd_t *fd,  out:          if (!wind) -                SVC_STACK_UNWIND (readdir, frame, op_ret, op_errno, NULL, +                SVC_STACK_UNWIND (readdir, frame, op_ret, op_errno, &entries,                                    NULL); + +        gf_dirent_free (&entries); +          return 0;  } +/* + * This lookup if mainly for supporting USS for windows. + * Since the dentry for the entry-point directory is not sent in + * the readdir response, from windows explorer, there is no way + * to access the snapshots. If the explicit path of the entry-point + * directory is mentioned in the address bar, then windows sends + * readdir on the parent directory and compares if the entry point + * directory's name is there in readdir response. If it is not there + * then access to snapshot world is denied. And windows users cannot + * access snapshots via samba. + * So, to handle this a new option called special-directory is created, + * which if set, snapview-client will send the entry-point's dentry + * in readdirp o/p for the special directory, so that it will be + * visible from windows explorer. + * But to send that virtual entry, the following mechanism is used. + * 1) Check if readdir from posix is over. + * 2) If so, then send a lookup on entry point directory to snap daemon + * (this is needed because in readdirp inodes are linked, so we need to + * maintain 1:1 mapping between inodes (gfids) from snapview server to + * snapview client). + * 3) Once successful lookup response received, send a new entry to + * windows. + */ + +int32_t +svc_readdirp_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) +{ +        gf_dirent_t    entries; +        gf_dirent_t   *entry      = NULL; +        svc_private_t *private    = NULL; +        svc_fd_t      *svc_fd     = NULL; +        svc_local_t   *local      = NULL; +        int            inode_type = -1; +        int            ret        = -1; + +        GF_VALIDATE_OR_GOTO ("snapview-client", this, out); +        GF_VALIDATE_OR_GOTO (this->name, this->private, out); + +        private = this->private; +        INIT_LIST_HEAD (&entries.list); + +        local = frame->local; + +        if (op_ret) { +                op_ret = 0; +                op_errno = ENOENT; +                goto out; +        } + +        svc_fd = svc_fd_ctx_get (this, local->fd); +        if (!svc_fd) { +                gf_log (this->name, GF_LOG_ERROR, "failed to get the fd " +                        "context for the inode %s", +                        uuid_utoa (local->fd->inode->gfid)); +                op_ret = 0; +                op_errno = ENOENT; +                goto out; +        } + +        entry = gf_dirent_for_name (private->path); +        if (!entry) { +                gf_log (this->name, GF_LOG_ERROR, "failed to allocate memory " +                        "for the entry %s", private->path); +                op_ret = 0; +                op_errno = ENOMEM; +                goto out; +        } + +        entry->inode = inode_ref (inode); +        entry->d_off = svc_fd->last_offset + 22; +        entry->d_ino = buf->ia_ino; +        entry->d_type = DT_DIR; +        entry->d_stat = *buf; +        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 the inode " +                        "context"); + +        list_add_tail (&entry->list, &entries.list); +        op_ret = 1; +        svc_fd->last_offset = entry->d_off; +        svc_fd->entry_point_handled = _gf_true; + +out: +        SVC_STACK_UNWIND (readdirp, frame, op_ret, op_errno, &entries, +                          local->xdata); + +        gf_dirent_free (&entries); + +        return 0; +} + +gf_boolean_t +svc_readdir_on_special_dir (call_frame_t *frame, void *cookie, xlator_t *this, +                            int32_t op_ret, int32_t op_errno, +                            gf_dirent_t *entries, dict_t *xdata) +{ +        svc_local_t   *local      = NULL; +        svc_private_t *private    = NULL; +        inode_t       *inode      = NULL; +        fd_t          *fd         = NULL; +        char          *path       = NULL; +        loc_t         *loc        = NULL; +        dict_t        *tmp_xdata  = NULL; +        int            ret        = -1; +        gf_boolean_t   unwind     = _gf_true; +        svc_fd_t      *svc_fd     = NULL; + +        GF_VALIDATE_OR_GOTO ("snapview-client", this, out); +        GF_VALIDATE_OR_GOTO (this->name, this->private, out); + +        private = this->private; +        local = frame->local; + +        loc = &local->loc; +        fd = local->fd; +        svc_fd = svc_fd_ctx_get (this, fd); +        if (!svc_fd) { +                gf_log (this->name, GF_LOG_ERROR, "failed to get the fd " +                        "context for the inode %s", +                        uuid_utoa (fd->inode->gfid)); +                goto out; +        } + +        /* +         * check if its end of readdir operation from posix, if special_dir +         * option is set, if readdir is done on special directory and if +         * readdirp is from normal regular graph. +         */ + +        if (!private->show_entry_point) +                goto out; + +        if (op_ret == 0 && op_errno == ENOENT && private->special_dir && +            strcmp (private->special_dir, "") && svc_fd->special_dir && +            local->subvolume == FIRST_CHILD (this)) { +                inode = inode_grep (fd->inode->table, fd->inode, +                                    private->path); +                if (!inode) { +                        inode = inode_new (fd->inode->table); +                        if (!inode) { +                                gf_log (this->name, GF_LOG_ERROR, "failed to " +                                        "allocate new inode"); +                                goto out; +                        } +                } + +                uuid_copy (local->loc.pargfid, fd->inode->gfid); +                uuid_copy (local->loc.gfid, inode->gfid); +                if (uuid_is_null (inode->gfid)) +                        ret = inode_path (fd->inode, private->path, &path); +                else +                        ret = inode_path (inode, NULL, &path); + +                if (ret < 0) +                        goto out; +                loc->path = gf_strdup (path); +                if (loc->path) { +                        if (!loc->name || +                            (loc->name && !strcmp (loc->name, ""))) { +                                loc->name = strrchr (loc->path, '/'); +                                if (loc->name) +                                        loc->name++; +                        } +                } + +                loc->inode = inode; +                loc->parent = inode_ref (fd->inode); +                tmp_xdata = dict_new (); +                if (!tmp_xdata) +                        goto out; +                ret = dict_set_str (tmp_xdata, "entry-point", "true"); +                if (ret) { +                        gf_log (this->name, GF_LOG_ERROR, "failed to set dict"); +                        goto out; +                } + +                local->cookie = cookie; +                local->xdata = dict_ref (xdata); +                STACK_WIND (frame, svc_readdirp_lookup_cbk, +                            SECOND_CHILD (this), +                            SECOND_CHILD (this)->fops->lookup, loc, tmp_xdata); +                unwind = _gf_false; +        } + +out: +        if (tmp_xdata) +                dict_unref (tmp_xdata); + +        GF_FREE (path); +        return unwind; +} +  int32_t  svc_readdirp_cbk (call_frame_t *frame, void *cookie, xlator_t *this,                    int32_t op_ret, int32_t op_errno, @@ -1295,12 +1735,22 @@ svc_readdirp_cbk (call_frame_t *frame, void *cookie, xlator_t *this,          gf_boolean_t   real       = _gf_true;          int            inode_type = -1;          int            ret        = -1; +        svc_fd_t      *svc_fd     = NULL; +        gf_boolean_t   unwind     = _gf_true; + +        GF_VALIDATE_OR_GOTO ("snapview-client", this, out);          if (op_ret < 0)                  goto out;          local = frame->local; -        frame->local = NULL; + +        svc_fd = svc_fd_ctx_get (this, local->fd); +        if (!svc_fd) { +                gf_log (this->name, GF_LOG_WARNING, "failed to get the fd " +                        "context for the gfid %s", +                        uuid_utoa (local->fd->inode->gfid)); +        }          if (local->subvolume == FIRST_CHILD (this))                  real = _gf_true; @@ -1320,11 +1770,16 @@ svc_readdirp_cbk (call_frame_t *frame, void *cookie, xlator_t *this,                  if (ret)                          gf_log (this->name, GF_LOG_ERROR, "failed to set inode "                                  "context"); +                svc_fd->last_offset = entry->d_off;          } +        unwind = svc_readdir_on_special_dir (frame, cookie, this, op_ret, +                                             op_errno, entries, xdata);  out: -        SVC_STACK_UNWIND (readdirp, frame, op_ret, op_errno, entries, xdata); +        if (unwind) +                SVC_STACK_UNWIND (readdirp, frame, op_ret, op_errno, entries, +                                  xdata);          return 0;  } @@ -1341,6 +1796,10 @@ svc_readdirp (call_frame_t *frame, xlator_t *this, fd_t *fd,          int            op_ret     = -1;          int            op_errno   = EINVAL;          gf_boolean_t   wind       = _gf_false; +        svc_fd_t      *svc_fd     = NULL; +        gf_dirent_t    entries; + +        INIT_LIST_HEAD (&entries.list);          GF_VALIDATE_OR_GOTO ("svc", this, out);          GF_VALIDATE_OR_GOTO (this->name, frame, out); @@ -1350,13 +1809,37 @@ svc_readdirp (call_frame_t *frame, xlator_t *this, fd_t *fd,          local = mem_get0 (this->local_pool);          if (!local) {                  gf_log (this->name, GF_LOG_ERROR, "failed to allocate local"); +                op_errno = ENOMEM;                  goto out;          } +        /* +         * This is mainly for samba shares (or windows clients). As part of +         * readdirp on the directory used as samba share, the entry point +         * directory would have been added at the end. So when a new readdirp +         * request comes, we have to check if the entry point has been handled +         * or not in readdirp. That information and the offset used for it +         * is remembered in fd context. If it has been handled, then simply +         * unwind indication end of readdir operation. +         */ +        svc_fd = svc_fd_ctx_get_or_new (this,  fd); +        if (!svc_fd) +                gf_log (this->name, GF_LOG_ERROR, "failed to get the fd " +                        "context for the inode %s", +                        uuid_utoa (fd->inode->gfid)); +        else { +                if (svc_fd->entry_point_handled && off == svc_fd->last_offset) { +                        op_ret = 0; +                        op_errno = ENOENT; +                        goto out; +                } +        } +          SVC_GET_SUBVOL_FROM_CTX (this, op_ret, op_errno, inode_type, ret,                                   fd->inode, subvolume, out);          local->subvolume = subvolume; +        local->fd = fd_ref (fd);          frame->local = local;          STACK_WIND (frame, svc_readdirp_cbk, subvolume, @@ -1366,7 +1849,10 @@ svc_readdirp (call_frame_t *frame, xlator_t *this, fd_t *fd,  out:          if (!wind) -                SVC_STACK_UNWIND (readdirp, frame, op_ret, op_errno, NULL, NULL); +                SVC_STACK_UNWIND (readdirp, frame, op_ret, op_errno, &entries, +                                  NULL); + +        gf_dirent_free (&entries);          return 0;  } @@ -1576,6 +2062,29 @@ out:  }  int32_t +svc_releasedir (xlator_t *this, fd_t *fd) +{ +        svc_fd_t *sfd      = NULL; +        uint64_t          tmp_pfd  = 0; +        int               ret      = 0; + +        GF_VALIDATE_OR_GOTO ("snapview-client", 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; +        } + +        GF_FREE (sfd); + +out: +        return 0; +} + +int32_t  svc_forget (xlator_t *this, inode_t *inode)  {          int            ret      = -1; @@ -1603,6 +2112,8 @@ reconfigure (xlator_t *this, dict_t *options)          priv = this->private;          GF_OPTION_RECONF ("snapshot-directory", priv->path, options, str, out); +        GF_OPTION_RECONF ("show-snapshot-directory", priv->show_entry_point, +                          options, bool, out);  out:          return 0; @@ -1665,6 +2176,18 @@ init (xlator_t *this)                  goto out;          GF_OPTION_INIT ("snapshot-directory", private->path, str, out); +        GF_OPTION_INIT ("snapdir-entry-path", private->special_dir, str, +                        out); +        GF_OPTION_INIT ("show-snapshot-directory", private->show_entry_point, +                        bool, out); + +        if (strstr (private->special_dir, private->path)) { +                gf_log (this->name, GF_LOG_ERROR, "entry point directory " +                        "cannot be part of the special directory"); +                GF_FREE (private->special_dir); +                private->special_dir = NULL; +                goto out; +        }          this->private = private;          this->local_pool = mem_pool_new (svc_local_t, 128); @@ -1698,6 +2221,7 @@ fini (xlator_t *this)          this->private = NULL;          GF_FREE (priv->path); +        GF_FREE (priv->special_dir);          GF_FREE (priv);          return; @@ -1757,6 +2281,7 @@ struct xlator_fops fops = {  struct xlator_cbks cbks = {          .forget = svc_forget, +        .releasedir = svc_releasedir,  };  struct volume_options options[] = { @@ -1764,5 +2289,20 @@ struct volume_options options[] = {            .type = GF_OPTION_TYPE_STR,            .default_value = ".snaps",          }, +        { .key = {"snapdir-entry-path"}, +          .type = GF_OPTION_TYPE_STR, +          .description = "An option to set the path of a directory on which " +                         "when readdir comes, dentry for the snapshot-directory" +                         " should be created and added in the readdir response", +          .default_value = "", +        }, +        { .key = {"show-snapshot-directory"}, +          .type = GF_OPTION_TYPE_BOOL, +          .description = "If this option is set, and the option " +                         "\"snapdir-entry-path\" is set (which is set by samba " +                         "vfs plugin for glusterfs, then send the entry point " +                         "when readdir comes on the snapdir-entry-path", +          .default_value = "off", +        },          { .key  = {NULL} },  }; diff --git a/xlators/features/snapview-client/src/snapview-client.h b/xlators/features/snapview-client/src/snapview-client.h index 000490393c1..9973458884b 100644 --- a/xlators/features/snapview-client/src/snapview-client.h +++ b/xlators/features/snapview-client/src/snapview-client.h @@ -25,6 +25,9 @@  struct __svc_local {          loc_t loc;          xlator_t *subvolume; +        fd_t     *fd; +        void *cookie; +        dict_t *xdata;  };  typedef struct __svc_local svc_local_t; @@ -81,10 +84,19 @@ svc_local_free (svc_local_t *local);          }  while (0);  struct svc_private { -        char *path; //might be helpful for samba +        char *path; +        char *special_dir; /* needed for samba */ +        gf_boolean_t show_entry_point;  };  typedef struct svc_private svc_private_t; +struct svc_fd { +        off_t last_offset; +        gf_boolean_t entry_point_handled; +        gf_boolean_t special_dir; +}; +typedef struct svc_fd svc_fd_t; +  typedef enum {          NORMAL_INODE = 1,          VIRTUAL_INODE @@ -107,4 +119,8 @@ svc_inode_ctx_set (xlator_t *this, inode_t *inode, int inode_type);  void  svc_local_free (svc_local_t *local); +gf_boolean_t +svc_readdir_on_special_dir (call_frame_t *frame, void *cookie, xlator_t *this, +                            int32_t op_ret, int32_t op_errno, +                            gf_dirent_t *entries, dict_t *xdata);  #endif /* __SNAP_VIEW_CLIENT_H__ */ diff --git a/xlators/features/snapview-server/src/snapview-server.c b/xlators/features/snapview-server/src/snapview-server.c index 55b51a1e367..5f42a996825 100644 --- a/xlators/features/snapview-server/src/snapview-server.c +++ b/xlators/features/snapview-server/src/snapview-server.c @@ -1062,6 +1062,8 @@ svs_releasedir (xlator_t *this, fd_t *fd)                                  uuid_utoa (fd->inode->gfid));          } +        GF_FREE (sfd); +  out:          return 0;  } @@ -1132,6 +1134,7 @@ svs_release (xlator_t *this, fd_t *fd)                  }          } +        GF_FREE (sfd);  out:          return 0;  } diff --git a/xlators/mgmt/glusterd/src/glusterd-volume-set.c b/xlators/mgmt/glusterd/src/glusterd-volume-set.c index 27e0028e13a..116cb3349cf 100644 --- a/xlators/mgmt/glusterd/src/glusterd-volume-set.c +++ b/xlators/mgmt/glusterd/src/glusterd-volume-set.c @@ -1152,6 +1152,15 @@ struct volopt_map_entry glusterd_volopt_map[] = {            .description = "Entry point directory for entering snapshot world"          }, +        { .key         = "features.show-snapshot-directory", +          .voltype     = "features/snapview-client", +          .op_version  = GD_OP_VERSION_3_6_0, +          .value       = "off", +          .flags       = OPT_FLAG_CLIENT_OPT | OPT_FLAG_XLATOR_OPT, +          .description = "show entry point in readdir output of " +                         "snapdir-entry-path which is set by samba" +        }, +  #ifdef HAVE_LIB_Z          /* Compressor-decompressor xlator options           * defaults used from xlator/features/compress/src/cdc.h  | 
