summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjiffin <jthottan@redhat.com>2014-11-04 20:33:01 +0530
committerNiels de Vos <ndevos@redhat.com>2014-11-14 06:40:30 -0800
commitfdef42e82d66011a3a92c9c96db4ada2fa8d4814 (patch)
tree47deaab1135b7acc2fd1d9973d582074f27817dd
parentc3dc90b53558316904985932490584402aaef48d (diff)
gNFS : make it possible to mount a subdir that actually is a symlink
We are using the function to export all sub-directories in a gluster volume via nfs. For real directories it works fine but if we have a symbolic link which points to the directory, it is not possible to mount that directory via nfs and the nameof the link. Kernel nfs resolves symlink handle to directoryhandle , similar gluster nfs should resolve the symbolic link handle into directory handle. Change-Id: I8bd07534ba9474f0b863f2335b2fd222ab625dba BUG: 1157223 Signed-off-by: jiffin tony thottan <jthottan@redhat.com> Reviewed-on: http://review.gluster.org/9052 Reviewed-by: soumya k <skoduri@redhat.com> Tested-by: Gluster Build System <jenkins@build.gluster.com> Reviewed-by: Jeff Darcy <jdarcy@redhat.com> Reviewed-by: Niels de Vos <ndevos@redhat.com>
-rw-r--r--libglusterfs/src/common-utils.c127
-rw-r--r--libglusterfs/src/common-utils.h4
-rw-r--r--tests/bugs/bug-1157223-symlink-mounting.t124
-rw-r--r--xlators/nfs/server/src/mount3.c144
4 files changed, 398 insertions, 1 deletions
diff --git a/libglusterfs/src/common-utils.c b/libglusterfs/src/common-utils.c
index 0f1aceec39b..1318c4d49a6 100644
--- a/libglusterfs/src/common-utils.c
+++ b/libglusterfs/src/common-utils.c
@@ -3429,3 +3429,130 @@ fop_log_level (glusterfs_fop_t fop, int op_errno)
return GF_LOG_ERROR;
}
+
+/* This function will build absolute path of file/directory from the
+ * current location and relative path given from the current location
+ * For example consider our current path is /a/b/c/ and relative path
+ * from current location is ./../x/y/z .After parsing through this
+ * function the absolute path becomes /a/b/x/y/z/.
+ *
+ * The function gives a pointer to absolute path if it is successful
+ * and also returns zero.
+ * Otherwise function gives NULL pointer with returning an err value.
+ *
+ * So the user need to free memory allocated for path.
+ *
+ */
+
+int32_t
+gf_build_absolute_path (char *current_path, char *relative_path, char **path)
+{
+ char *absolute_path = NULL;
+ char *token = NULL;
+ char *component = NULL;
+ char *saveptr = NULL;
+ char *end = NULL;
+ int ret = 0;
+ size_t relativepath_len = 0;
+ size_t currentpath_len = 0;
+ size_t max_absolutepath_len = 0;
+
+ GF_ASSERT (current_path);
+ GF_ASSERT (relative_path);
+ GF_ASSERT (path);
+
+ if (!path || !current_path || !relative_path) {
+ ret = -EFAULT;
+ goto err;
+ }
+ /* Check for current and relative path
+ * current path should be absolute one and start from '/'
+ * relative path should not start from '/'
+ */
+ currentpath_len = strlen (current_path);
+ if (current_path[0] != '/' || (currentpath_len > PATH_MAX)) {
+ gf_log (THIS->name, GF_LOG_ERROR, "Wrong value for"
+ " current path %s", current_path);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ relativepath_len = strlen (relative_path);
+ if (relative_path[0] == '/' || (relativepath_len > PATH_MAX)) {
+ gf_log (THIS->name, GF_LOG_ERROR, "Wrong value for"
+ " relative path %s", relative_path);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* It is maximum possible value for absolute path */
+ max_absolutepath_len = currentpath_len + relativepath_len + 2;
+
+ absolute_path = GF_CALLOC (1, max_absolutepath_len, gf_common_mt_char);
+ if (!absolute_path) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ absolute_path[0] = '\0';
+
+ /* If current path is root i.e contains only "/", we do not
+ * need to copy it
+ */
+ if (strcmp (current_path, "/") != 0) {
+ strcpy (absolute_path, current_path);
+
+ /* We trim '/' at the end for easier string manipulation */
+ gf_path_strip_trailing_slashes (absolute_path);
+ }
+
+ /* Used to spilt relative path based on '/' */
+ component = gf_strdup (relative_path);
+ if (!component) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ /* In the relative path, we want to consider ".." and "."
+ * if token is ".." , we just need to reduce one level hierarchy
+ * if token is "." , we just ignore it
+ * if token is NULL , end of relative path
+ * if absolute path becomes '\0' and still "..", then it is a bad
+ * relative path, it points to out of boundary area and stop
+ * building the absolute path
+ * All other cases we just concatenate token to the absolute path
+ */
+ for (token = strtok_r (component, "/", &saveptr),
+ end = strchr (absolute_path, '\0'); token;
+ token = strtok_r (NULL, "/", &saveptr)) {
+ if (strcmp (token, ".") == 0)
+ continue;
+
+ else if (strcmp (token, "..") == 0) {
+
+ if (absolute_path[0] == '\0') {
+ ret = -EACCES;
+ goto err;
+ }
+
+ end = strrchr (absolute_path, '/');
+ *end = '\0';
+ } else {
+ ret = snprintf (end, max_absolutepath_len -
+ strlen (absolute_path), "/%s", token);
+ end = strchr (absolute_path , '\0');
+ }
+ }
+
+ if (strlen (absolute_path) > PATH_MAX) {
+ ret = -EINVAL;
+ goto err;
+ }
+ *path = gf_strdup (absolute_path);
+
+err:
+ if (component)
+ GF_FREE (component);
+ if (absolute_path)
+ GF_FREE (absolute_path);
+ return ret;
+}
diff --git a/libglusterfs/src/common-utils.h b/libglusterfs/src/common-utils.h
index 0d5abb42ec2..d2bc42b9662 100644
--- a/libglusterfs/src/common-utils.h
+++ b/libglusterfs/src/common-utils.h
@@ -653,4 +653,8 @@ gf_backtrace_done (char *buf);
gf_loglevel_t
fop_log_level (glusterfs_fop_t fop, int op_errno);
+
+int32_t
+gf_build_absolute_path (char *current_path, char *relative_path, char **path);
+
#endif /* _COMMON_UTILS_H */
diff --git a/tests/bugs/bug-1157223-symlink-mounting.t b/tests/bugs/bug-1157223-symlink-mounting.t
new file mode 100644
index 00000000000..4ebc3453889
--- /dev/null
+++ b/tests/bugs/bug-1157223-symlink-mounting.t
@@ -0,0 +1,124 @@
+#!/bin/bash
+
+. $(dirname $0)/../include.rc
+. $(dirname $0)/../nfs.rc
+
+cleanup;
+
+## Start and create a volume
+TEST glusterd
+TEST pidof glusterd
+
+TEST $CLI volume info;
+TEST $CLI volume create $V0 $H0:$B0/$V0
+
+TEST $CLI volume start $V0;
+
+## Wait for volume to register with rpc.mountd
+EXPECT_WITHIN $NFS_EXPORT_TIMEOUT "1" is_nfs_export_available;
+
+## Mount NFS
+TEST mount_nfs $H0:/$V0 $N0 nolock;
+
+mkdir $N0/dir1;
+mkdir $N0/dir2;
+pushd $N0/ ;
+
+##link created using relative path
+ln -s dir1 symlink1;
+
+##relative path contains ".."
+ln -s ../dir1 dir2/symlink2;
+
+##link created using absolute path
+ln -s $N0/dir1 symlink3;
+
+##link pointing to another symlinks
+ln -s symlink1 symlink4
+ln -s symlink3 symlink5
+
+##dead links
+ln -s does/not/exist symlink6
+
+##link which contains ".." points out of glusterfs
+ln -s ../../ symlink7
+
+##links pointing to unauthorized area
+ln -s .glusterfs symlink8
+
+popd ;
+
+##Umount the volume
+EXPECT_WITHIN $UMOUNT_TIMEOUT "Y" umount_nfs $N0
+
+## Mount and umount NFS via directory
+TEST mount_nfs $H0:/$V0/dir1 $N0 nolock;
+EXPECT_WITHIN $UMOUNT_TIMEOUT "Y" umount_nfs $N0
+
+## Mount and umount NFS via symlink1
+TEST mount_nfs $H0:/$V0/symlink1 $N0 nolock;
+EXPECT_WITHIN $UMOUNT_TIMEOUT "Y" umount_nfs $N0
+
+## Mount and umount NFS via symlink2
+TEST mount_nfs $H0:/$V0/dir2/symlink2 $N0 nolock;
+EXPECT_WITHIN $UMOUNT_TIMEOUT "Y" umount_nfs $N0
+
+## Mount NFS via symlink3 should fail
+TEST ! mount_nfs $H0:/$V0/symlink3 $N0 nolock;
+
+## Mount and umount NFS via symlink4
+TEST mount_nfs $H0:/$V0/symlink4 $N0 nolock;
+EXPECT_WITHIN $UMOUNT_TIMEOUT "Y" umount_nfs $N0
+
+## Mount NFS via symlink5 should fail
+TEST ! mount_nfs $H0:/$V0/symlink5 $N0 nolock;
+
+## Mount NFS via symlink6 should fail
+TEST ! mount_nfs $H0:/$V0/symlink6 $N0 nolock;
+
+## Mount NFS via symlink7 should fail
+TEST ! mount_nfs $H0:/$V0/symlink7 $N0 nolock;
+
+## Mount NFS via symlink8 should fail
+TEST ! mount_nfs $H0:/$V0/symlink8 $N0 nolock;
+
+##Similar check for udp mount
+$CLI volume stop $V0
+TEST $CLI volume set $V0 nfs.mount-udp on
+$CLI volume start $V0
+
+## Wait for volume to register with rpc.mountd
+EXPECT_WITHIN $NFS_EXPORT_TIMEOUT "1" is_nfs_export_available;
+
+## Mount and umount NFS via directory
+TEST mount_nfs $H0:/$V0/dir1 $N0 nolock,mountproto=udp,proto=tcp;
+EXPECT_WITHIN $UMOUNT_TIMEOUT "Y" umount_nfs $N0
+
+## Mount and umount NFS via symlink1
+TEST mount_nfs $H0:/$V0/symlink1 $N0 nolock,mountproto=udp,proto=tcp;
+EXPECT_WITHIN $UMOUNT_TIMEOUT "Y" umount_nfs $N0
+
+## Mount and umount NFS via symlink2
+TEST mount_nfs $H0:/$V0/dir2/symlink2 $N0 nolock,mountproto=udp,proto=tcp;
+EXPECT_WITHIN $UMOUNT_TIMEOUT "Y" umount_nfs $N0
+
+## Mount NFS via symlink3 should fail
+TEST ! mount_nfs $H0:/$V0/symlink3 $N0 nolock,mountproto=udp,proto=tcp;
+
+## Mount and umount NFS via symlink4
+TEST mount_nfs $H0:/$V0/symlink4 $N0 nolock,mountproto=udp,proto=tcp;
+EXPECT_WITHIN $UMOUNT_TIMEOUT "Y" umount_nfs $N0
+
+## Mount NFS via symlink5 should fail
+TEST ! mount_nfs $H0:/$V0/symlink5 $N0 nolock,mountproto=udp,proto=tcp;
+
+## Mount NFS via symlink6 should fail
+TEST ! mount_nfs $H0:/$V0/symlink6 $N0 nolock,mountproto=udp,proto=tcp;
+
+##symlink7 is not check here, because in udp mount ../../ resolves into root '/'
+
+## Mount NFS via symlink8 should fail
+TEST ! mount_nfs $H0:/$V0/symlink8 $N0 nolock,mountproto=udp,proto=tcp;
+
+rm -rf $H0:$B0/
+cleanup;
diff --git a/xlators/nfs/server/src/mount3.c b/xlators/nfs/server/src/mount3.c
index 6be856ba92a..f76c1c41ff2 100644
--- a/xlators/nfs/server/src/mount3.c
+++ b/xlators/nfs/server/src/mount3.c
@@ -845,6 +845,15 @@ mnt3_resolve_subdir_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
struct iatt *buf, dict_t *xattr,
struct iatt *postparent);
+int
+mnt3_parse_dir_exports (rpcsvc_request_t *req, struct mount3_state *ms,
+ char *subdir);
+
+int32_t
+mnt3_readlink_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int32_t op_ret, int32_t op_errno, const char *path,
+ struct iatt *buf, dict_t *xdata);
+
/* There are multiple components in the directory export path and each one
* needs to be looked up one after the other.
*/
@@ -880,6 +889,13 @@ __mnt3_resolve_export_subdir_comp (mnt3_resolve_t *mres)
}
nfs_request_user_init (&nfu, mres->req);
+ if (IA_ISLNK (mres->resolveloc.inode->ia_type)) {
+ ret = nfs_readlink (mres->mstate->nfsx, mres->exp->vol, &nfu,
+ &mres->resolveloc, mnt3_readlink_cbk, mres);
+ gf_log (GF_MNT, GF_LOG_DEBUG, "Symlink found , need to resolve"
+ " into directory handle");
+ goto err;
+ }
ret = nfs_lookup (mres->mstate->nfsx, mres->exp->vol, &nfu,
&mres->resolveloc, mnt3_resolve_subdir_cbk, mres);
@@ -954,7 +970,124 @@ err:
return 0;
}
+/* This function resolves symbolic link into directory path from
+ * the mount and restart the parsing process from the begining
+ *
+ * Note : Path specified in the symlink should be relative to the
+ * symlink, because that is the one which is consistent throught
+ * out the file system.
+ * If the symlink resolves into another symlink ,then same process
+ * will be repeated.
+ * If symbolic links points outside the file system are not considered
+ * here.
+ *
+ * TODO : 1.) This function cannot handle symlinks points to path which
+ * goes out of the filesystem and comes backs again to same.
+ * For example, consider vol is exported volume.It contains
+ * dir,
+ * symlink1 which points to ../vol/dir,
+ * symlink2 which points to ../mnt/../vol/dir,
+ * symlink1 and symlink2 are not handled right now.
+ *
+ * 2.) udp mount routine is much simpler from tcp routine and resolves
+ * symlink directly.May be ,its better we change this routine
+ * similar to udp
+ */
+int32_t
+mnt3_readlink_cbk (call_frame_t *frame, void *cookie, xlator_t *this,
+ int32_t op_ret, int32_t op_errno, const char *path,
+ struct iatt *buf, dict_t *xdata)
+{
+ mnt3_resolve_t *mres = NULL;
+ int ret = -EFAULT;
+ char *real_loc = NULL;
+ size_t path_len = 0;
+ size_t parent_path_len = 0;
+ char *parent_path = NULL;
+ char *absolute_path = NULL;
+ char *relative_path = NULL;
+ int mntstat = 0;
+
+ GF_ASSERT (frame);
+
+ mres = frame->local;
+ if (!mres || !path || (path[0] == '/') || (op_ret < 0))
+ goto mnterr;
+ /* Finding current location of symlink */
+ parent_path_len = strlen (mres->resolveloc.path) - strlen (mres->resolveloc.name);
+ parent_path = gf_strndup (mres->resolveloc.path, parent_path_len);
+ if (!parent_path) {
+ ret = -ENOMEM;
+ goto mnterr;
+ }
+
+ relative_path = gf_strdup (path);
+ if (!relative_path) {
+ ret = -ENOMEM;
+ goto mnterr;
+ }
+ /* Resolving into absolute path */
+ ret = gf_build_absolute_path (parent_path, relative_path, &absolute_path);
+ if (ret < 0) {
+ gf_log (GF_MNT, GF_LOG_ERROR, "Cannot resolve symlink, path"
+ "is out of boundary from current location %s"
+ "and with relative path %s pointed by symlink",
+ parent_path, relative_path);
+
+ goto mnterr;
+ }
+
+ /* Building the actual mount path to be mounted */
+ path_len = strlen (mres->exp->vol->name) + strlen (absolute_path)
+ + strlen (mres->remainingdir) + 1;
+ real_loc = GF_CALLOC (1, path_len, gf_nfs_mt_char);
+ if (!real_loc) {
+ ret = -ENOMEM;
+ goto mnterr;
+ }
+ sprintf (real_loc , "%s%s", mres->exp->vol->name, absolute_path);
+ gf_path_strip_trailing_slashes (real_loc);
+
+ /* There may entries after symlink in the mount path,
+ * we should include remaining entries too */
+ if (strlen (mres->remainingdir) > 0)
+ strcat (real_loc, mres->remainingdir);
+
+ gf_log (GF_MNT, GF_LOG_DEBUG, "Resolved path is : %s%s "
+ "and actual mount path is %s",
+ absolute_path, mres->remainingdir, real_loc);
+
+ /* After the resolving the symlink , parsing should be done
+ * for the populated mount path
+ */
+ ret = mnt3_parse_dir_exports (mres->req, mres->mstate, real_loc);
+
+ if (ret) {
+ gf_log (GF_MNT, GF_LOG_ERROR,
+ "Resolved into an unknown path %s%s "
+ "from the current location of symlink %s",
+ absolute_path, mres->remainingdir, parent_path);
+ }
+
+ GF_FREE (real_loc);
+ GF_FREE (absolute_path);
+ GF_FREE (parent_path);
+ GF_FREE (relative_path);
+
+ return ret;
+
+mnterr:
+ mntstat = mnt3svc_errno_to_mnterr (-ret);
+ mnt3svc_mnt_error_reply (mres->req, mntstat);
+ if (absolute_path)
+ GF_FREE (absolute_path);
+ if (parent_path)
+ GF_FREE (parent_path);
+ if (relative_path)
+ GF_FREE (relative_path);
+ return ret;
+}
/* We will always have to perform a hard lookup on all the components of a
* directory export for a mount request because in the mount reply we need the
@@ -996,6 +1129,13 @@ __mnt3_resolve_subdir (mnt3_resolve_t *mres)
}
nfs_request_user_init (&nfu, mres->req);
+ if (IA_ISLNK (mres->resolveloc.inode->ia_type)) {
+ ret = nfs_readlink (mres->mstate->nfsx, mres->exp->vol, &nfu,
+ &mres->resolveloc, mnt3_readlink_cbk, mres);
+ gf_log (GF_MNT, GF_LOG_DEBUG, "Symlink found , need to resolve "
+ "into directory handle");
+ goto err;
+ }
ret = nfs_lookup (mres->mstate->nfsx, mres->exp->vol, &nfu,
&mres->resolveloc, mnt3_resolve_subdir_cbk, mres);
@@ -2054,13 +2194,15 @@ __mnt3udp_get_export_subdir_inode (struct svc_req *req, char *subdir,
* TODO: Instead of linking against libgfapi.so, just for one API
* i.e. glfs_resolve_at(), It would be cleaner if PATH name to
* inode resolution code can be moved to libglusterfs.so or so.
+ * refer bugzilla for more details :
+ * https://bugzilla.redhat.com/show_bug.cgi?id=1161573
*/
fs = glfs_new_from_ctx (exp->vol->ctx);
if (!fs)
return NULL;
ret = glfs_resolve_at (fs, exp->vol, NULL, subdir,
- &loc, &buf, 0 /* Follow link */,
+ &loc, &buf, 1 /* Follow link */,
0 /* Hard lookup */);
glfs_free_from_ctx (fs);