From 89c2cc5ddb21f337fe66629fefe7230facbf9d7e Mon Sep 17 00:00:00 2001 From: Raghavendra Bhat Date: Fri, 28 Nov 2014 18:08:53 +0530 Subject: features/snapview-client: handle readdir requests differently for samba. * For samba export, the entry point is also added to the readdir response. Change-Id: I825c017e0f16db1f1890bb56e086f36e6558a1c2 BUG: 1175742 Signed-off-by: Raghavendra Bhat Reviewed-on: http://review.gluster.org/9218 Tested-by: Gluster Build System Reviewed-by: Vijay Bellur Signed-off-by: Sachin Pandit Reviewed-on: http://review.gluster.org/9344 --- .../en-US/markdown/admin_managing_snapshots.md | 48 ++ tests/bugs/bug-1168875.t | 96 ++++ .../src/snapview-client-mem-types.h | 1 + .../features/snapview-client/src/snapview-client.c | 556 ++++++++++++++++++++- .../features/snapview-client/src/snapview-client.h | 18 +- .../features/snapview-server/src/snapview-server.c | 3 + xlators/mgmt/glusterd/src/glusterd-volume-set.c | 9 + 7 files changed, 722 insertions(+), 9 deletions(-) create mode 100644 tests/bugs/bug-1168875.t 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 e36e3faeac5..def874d7b79 100644 --- a/doc/admin-guide/en-US/markdown/admin_managing_snapshots.md +++ b/doc/admin-guide/en-US/markdown/admin_managing_snapshots.md @@ -245,4 +245,52 @@ world) can be changed using the below command. *gluster volume set snapshot-directory * +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 +(\\\\\) 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 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. 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; } @@ -1575,6 +2061,29 @@ out: return 0; } +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) { @@ -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 5173df6059b..dc620067ec4 100644 --- a/xlators/mgmt/glusterd/src/glusterd-volume-set.c +++ b/xlators/mgmt/glusterd/src/glusterd-volume-set.c @@ -1140,6 +1140,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 -- cgit