diff options
| -rw-r--r-- | libglusterfs/src/common-utils.c | 127 | ||||
| -rw-r--r-- | libglusterfs/src/common-utils.h | 4 | ||||
| -rw-r--r-- | tests/bugs/bug-1157223-symlink-mounting.t | 124 | ||||
| -rw-r--r-- | xlators/nfs/server/src/mount3.c | 144 | 
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);  | 
