X-Git-Url: https://mattmccutchen.net/rsync/rsync.git/blobdiff_plain/9411292489496984c8d5d9a446bf071afac3866d..b4d30300b9cf6cdb77a95bd2018c7152f06ec94b:/util.c diff --git a/util.c b/util.c index 373ce333..0cafed69 100644 --- a/util.c +++ b/util.c @@ -4,7 +4,7 @@ * Copyright (C) 1996-2000 Andrew Tridgell * Copyright (C) 1996 Paul Mackerras * Copyright (C) 2001, 2002 Martin Pool - * Copyright (C) 2003-2008 Wayne Davison + * Copyright (C) 2003-2009 Wayne Davison * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -806,7 +806,8 @@ int count_dir_elements(const char *p) return cnt; } -/* Turns multiple adjacent slashes into a single slash, drops all leading or +/* Turns multiple adjacent slashes into a single slash (possible exception: + * the preserving of two leading slashes at the start), drops all leading or * interior "." elements unless CFN_KEEP_DOT_DIRS is flagged. Will also drop * a trailing '.' after a '/' if CFN_DROP_TRAILING_DOT_DIR is flagged, removes * a trailing slash (perhaps after removing the aforementioned dot) unless @@ -821,9 +822,16 @@ unsigned int clean_fname(char *name, int flags) if (!name) return 0; - if ((anchored = *f == '/') != 0) + if ((anchored = *f == '/') != 0) { *t++ = *f++; - else if (flags & CFN_KEEP_DOT_DIRS && *f == '.' && f[1] == '/') { +#ifdef __CYGWIN__ + /* If there are exactly 2 slashes at the start, preserve + * them. Would break daemon excludes unless the paths are + * really treated differently, so used this sparingly. */ + if (*f == '/' && f[1] != '/') + *t++ = *f++; +#endif + } else if (flags & CFN_KEEP_DOT_DIRS && *f == '.' && f[1] == '/') { *t++ = *f++; *t++ = *f++; } @@ -1160,12 +1168,13 @@ int handle_partial_dir(const char *fname, int create) return 1; } -/** - * Determine if a symlink points outside the current directory tree. +/* Determine if a symlink points outside the current directory tree. * This is considered "unsafe" because e.g. when mirroring somebody * else's machine it might allow them to establish a symlink to * /etc/passwd, and then read it through a web server. * + * Returns 1 if unsafe, 0 if safe. + * * Null symlinks and absolute symlinks are always unsafe. * * Basically here we are concerned with symlinks whose target contains @@ -1173,17 +1182,11 @@ int handle_partial_dir(const char *fname, int create) * transferred directory. We are not allowed to go back up and * reenter. * - * @param dest Target of the symlink in question. + * "dest" is the target of the symlink in question. * - * @param src Top source directory currently applicable. Basically this - * is the first parameter to rsync in a simple invocation, but it's - * modified by flist.c in slightly complex ways. - * - * @retval True if unsafe - * @retval False is unsafe - * - * @sa t_unsafe.c - **/ + * "src" is the top source directory currently applicable at the level + * of the referenced symlink. This is usually the symlink's full path + * (including its name), as referenced from the root of the transfer. */ int unsafe_symlink(const char *dest, const char *src) { const char *name, *slash; @@ -1195,33 +1198,33 @@ int unsafe_symlink(const char *dest, const char *src) /* find out what our safety margin is */ for (name = src; (slash = strchr(name, '/')) != 0; name = slash+1) { - if (strncmp(name, "../", 3) == 0) { - depth = 0; - } else if (strncmp(name, "./", 2) == 0) { - /* nothing */ - } else { + /* ".." segment starts the count over. "." segment is ignored. */ + if (*name == '.' && (name[1] == '/' || (name[1] == '.' && name[2] == '/'))) { + if (name[1] == '.') + depth = 0; + } else depth++; - } + while (slash[1] == '/') slash++; /* just in case src isn't clean */ } - if (strcmp(name, "..") == 0) + if (*name == '.' && name[1] == '.' && name[2] == '\0') depth = 0; for (name = dest; (slash = strchr(name, '/')) != 0; name = slash+1) { - if (strncmp(name, "../", 3) == 0) { - /* if at any point we go outside the current directory - then stop - it is unsafe */ - if (--depth < 0) - return 1; - } else if (strncmp(name, "./", 2) == 0) { - /* nothing */ - } else { + if (*name == '.' && (name[1] == '/' || (name[1] == '.' && name[2] == '/'))) { + if (name[1] == '.') { + /* if at any point we go outside the current directory + then stop - it is unsafe */ + if (--depth < 0) + return 1; + } + } else depth++; - } + while (slash[1] == '/') slash++; } - if (strcmp(name, "..") == 0) + if (*name == '.' && name[1] == '.' && name[2] == '\0') depth--; - return (depth < 0); + return depth < 0; } /* Return the date and time as a string. Some callers tweak returned buf. */