Ticket #1828: Improved symlink handling in ftpfs

Originally from: http://mail.gnome.org/archives/mc-devel/2005-April/msg00035.html

The routines `vfs_s_find_entry_linear()' and `vfs_s_find_entry_tree()'
call `canonicalize_pathname()' on entry. This routine eats `..' path
components in certain cases. In case of ftpfs this is not desired -
the path should be kept as is since the code in direntry.c doesn't have
enough knowledge of the directory structure on the remote end.

Assume that
there is a path like this on the remote server

    /path1/path2/path3

The `path2' component is a symlink to some directory and `path3' is a
symlink stored in `path2' which is relative to `path2' i.e.

path2

    path3 -> ../some/other/path

Now, the code in direntry.c will determine that `path3' is a symlink and
will try to resolve (vfs_s_resolve_symlink) it by passing the following path

    /path1/path2/../some/other/path

to `vfs_s_find_entry_linear' . As I've said above this routine calls
`canonicalize_pathname' on entry which will modify the path like this:

    /path1/some/other/path

Now this is clearly wrong since `path2' is a symlink and it should be
resolved first. In the case of ftpfs the code in direntry.c doesn't have
enough knowledge about physycal directory layout on the remote filesystem
so it shouldn't try to canonicalize the path. The path should be left as
is and passed to the remote end for processing.

Fix issue:
Changed function canonicalize_pathname (as fact, renamed to custom_canonicalize_pathname)
In this function added ability to partial canonicalize of pathname.
And some functions from vfs/direntry.c calls custom_canonicalize_pathname without
removal of '..' stuff.

Signed-off-by: Slava Zanko <slavazanko@gmail.com>
This commit is contained in:
Slava Zanko 2009-11-23 10:45:46 +02:00
parent 00b94eec5d
commit 4f36622725
3 changed files with 114 additions and 87 deletions

View File

@ -146,6 +146,17 @@ extern struct sigaction startup_handler;
char *tilde_expand (const char *);
/* Pathname canonicalization */
typedef enum {
CANON_PATH_JOINSLASHES = 1L<<0, /* Multiple `/'s are collapsed to a single `/'. */
CANON_PATH_REMSLASHDOTS = 1L<<1, /* Leading `./'s, `/'s and trailing `/.'s are removed. */
CANON_PATH_REMDOUBLEDOTS = 1L<<3, /* Non-leading `../'s and trailing `..'s are handled by removing */
CANON_PATH_GUARDUNC = 1L<<4, /* Detect and preserve UNC paths: //server/... */
CANON_PATH_ALL = CANON_PATH_JOINSLASHES
| CANON_PATH_REMSLASHDOTS
| CANON_PATH_REMDOUBLEDOTS
| CANON_PATH_GUARDUNC
} CANON_PATH_FLAGS;
void custom_canonicalize_pathname (char *, CANON_PATH_FLAGS);
void canonicalize_pathname (char *);
/* Misc Unix functions */

View File

@ -457,14 +457,14 @@ close_error_pipe (int error, const char *text)
* Well formed UNC paths are modified only in the local part.
*/
void
canonicalize_pathname (char *path)
custom_canonicalize_pathname (char *path, CANON_PATH_FLAGS flags)
{
char *p, *s;
int len;
char *lpath = path; /* path without leading UNC part */
/* Detect and preserve UNC paths: //server/... */
if (path[0] == PATH_SEP && path[1] == PATH_SEP) {
if ( ( flags & CANON_PATH_GUARDUNC ) && path[0] == PATH_SEP && path[1] == PATH_SEP) {
p = path + 2;
while (p[0] && p[0] != '/')
p++;
@ -475,6 +475,7 @@ canonicalize_pathname (char *path)
if (!lpath[0] || !lpath[1])
return;
if ( flags & CANON_PATH_JOINSLASHES ) {
/* Collapse multiple slashes */
p = lpath;
while (*p) {
@ -485,7 +486,9 @@ canonicalize_pathname (char *path)
}
p++;
}
}
if ( flags & CANON_PATH_JOINSLASHES ) {
/* Collapse "/./" -> "/" */
p = lpath;
while (*p) {
@ -494,7 +497,9 @@ canonicalize_pathname (char *path)
else
p++;
}
}
if ( flags & CANON_PATH_REMSLASHDOTS ) {
/* Remove trailing slashes */
p = lpath + strlen (lpath) - 1;
while (p > lpath && *p == PATH_SEP)
@ -526,7 +531,9 @@ canonicalize_pathname (char *path)
}
}
}
}
if ( flags & CANON_PATH_REMDOUBLEDOTS ) {
/* Collapse "/.." with the previous part of path */
p = lpath;
while (p[0] && p[1] && p[2]) {
@ -580,6 +587,13 @@ canonicalize_pathname (char *path)
break;
}
}
}
void
canonicalize_pathname (char *path)
{
custom_canonicalize_pathname (path, CANON_PATH_ALL);
}
#ifdef HAVE_GET_PROCESS_STATS
# include <sys/procstats.h>

View File

@ -263,7 +263,8 @@ vfs_s_find_entry_tree (struct vfs_class *me, struct vfs_s_inode *root,
char * const pathref = g_strdup (a_path);
char *path = pathref;
canonicalize_pathname (path);
/* canonicalize as well, but don't remove '../' from path */
custom_canonicalize_pathname (path, CANON_PATH_ALL & (~CANON_PATH_RMDBLDT));
while (root) {
while (*path == PATH_SEP) /* Strip leading '/' */
@ -336,7 +337,8 @@ vfs_s_find_entry_linear (struct vfs_class *me, struct vfs_s_inode *root,
if (root->super->root != root)
vfs_die ("We have to use _real_ root. Always. Sorry.");
canonicalize_pathname (path);
/* canonicalize as well, but don't remove '../' from path */
custom_canonicalize_pathname (path, CANON_PATH_ALL & (~CANON_PATH_RMDBLDT));
if (!(flags & FL_DIR)) {
char *dirname, *name, *save;