summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/admin-guide/en-US/markdown/admin_managing_snapshots.md48
-rw-r--r--tests/bugs/bug-1168875.t96
-rw-r--r--xlators/features/snapview-client/src/snapview-client-mem-types.h1
-rw-r--r--xlators/features/snapview-client/src/snapview-client.c556
-rw-r--r--xlators/features/snapview-client/src/snapview-client.h18
-rw-r--r--xlators/features/snapview-server/src/snapview-server.c3
-rw-r--r--xlators/mgmt/glusterd/src/glusterd-volume-set.c9
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