summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWayne Davison <wayned@samba.org>2008-03-20 11:59:54 -0700
committerWayne Davison <wayned@samba.org>2008-03-20 22:39:29 -0700
commitd48810ba5b0b8d09b272092a70da1255c2346ab8 (patch)
tree46b7a8cc372f917bafe0879f3db54e1b681d13f9
parent819bfe459919769c9ddae14affeaa79596c640bb (diff)
downloadrsync-d48810ba5b0b8d09b272092a70da1255c2346ab8.tar.gz
Some improvements to the file-name cleaning code:
- Removed the CFN_KEEP_LEADING_DOT_DIR flag for clean_fname(). - Explicitly add an implied dot-dir to the transfer rather than keeping a leading a "./" prefix as a part of a relative pathname. - Added the CFN_KEEP_DOT_DIRS flag for clean_fname(). - Added the SP_KEEP_DOT_DIRS flag for sanitize_path(). - Call clean_fname() a couple more times.
-rw-r--r--exclude.c2
-rw-r--r--flist.c32
-rw-r--r--main.c4
-rw-r--r--options.c10
-rw-r--r--rsync.h5
-rw-r--r--util.c50
6 files changed, 62 insertions, 41 deletions
diff --git a/exclude.c b/exclude.c
index d192b551..422a824d 100644
--- a/exclude.c
+++ b/exclude.c
@@ -295,7 +295,7 @@ static char *parse_merge_name(const char *merge_file, unsigned int *len_ptr,
strlcpy(to, merge_file, *len_ptr + 1);
merge_file = to;
}
- if (!sanitize_path(fn, merge_file, r, dirbuf_depth)) {
+ if (!sanitize_path(fn, merge_file, r, dirbuf_depth, SP_DEFAULT)) {
rprintf(FERROR, "merge-file name overflows: %s\n",
merge_file);
return NULL;
diff --git a/flist.c b/flist.c
index f6385ca6..dce55c7b 100644
--- a/flist.c
+++ b/flist.c
@@ -687,7 +687,7 @@ static struct file_struct *recv_file_entry(struct file_list *flist,
clean_fname(thisname, 0);
if (sanitize_paths)
- sanitize_path(thisname, thisname, "", 0);
+ sanitize_path(thisname, thisname, "", 0, SP_DEFAULT);
if ((basename = strrchr(thisname, '/')) != NULL) {
int len = basename++ - thisname;
@@ -939,7 +939,7 @@ static struct file_struct *recv_file_entry(struct file_list *flist,
} else {
read_sbuf(f, bp, linkname_len - 1);
if (sanitize_paths)
- sanitize_path(bp, bp, "", lastdir_depth);
+ sanitize_path(bp, bp, "", lastdir_depth, SP_DEFAULT);
}
}
#endif
@@ -1030,7 +1030,7 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
}
clean_fname(thisname, 0);
if (sanitize_paths)
- sanitize_path(thisname, thisname, "", 0);
+ sanitize_path(thisname, thisname, "", 0, SP_DEFAULT);
if (stp && S_ISDIR(stp->st_mode)) {
st = *stp; /* Needed for "symlink/." with --relative. */
@@ -1795,6 +1795,7 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
| (filesfrom_convert ? RL_CONVERT : 0)
#endif
| (eol_nulls || reading_remotely ? RL_EOL_NULLS : 0);
+ int implied_dot_dir = 0;
rprintf(FLOG, "building file list\n");
if (show_filelist_p())
@@ -1836,13 +1837,13 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
if (use_ff_fd) {
if (read_line(filesfrom_fd, fbuf, sizeof fbuf, rl_flags) == 0)
break;
- sanitize_path(fbuf, fbuf, "", 0);
+ sanitize_path(fbuf, fbuf, "", 0, SP_KEEP_DOT_DIRS);
} else {
if (argc-- == 0)
break;
strlcpy(fbuf, *argv++, MAXPATHLEN);
if (sanitize_paths)
- sanitize_path(fbuf, fbuf, "", 0);
+ sanitize_path(fbuf, fbuf, "", 0, SP_KEEP_DOT_DIRS);
}
len = strlen(fbuf);
@@ -1892,8 +1893,10 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
*p = '\0';
if (p == fbuf)
dir = "/";
- else
+ else {
dir = fbuf;
+ clean_fname(dir, 0);
+ }
fn = p + 3;
while (*fn == '/')
fn++;
@@ -1901,8 +1904,15 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
*--fn = '\0'; /* ensure room for '.' */
} else
fn = fbuf;
- len = clean_fname(fn, CFN_KEEP_LEADING_DOT_DIR
- | CFN_KEEP_TRAILING_SLASH
+ /* A leading ./ can be used in relative mode to affect
+ * the dest dir without its name being in the path. */
+ if (*fn == '.' && fn[1] == '/' && !implied_dot_dir) {
+ send_file_name(f, flist, ".", NULL,
+ (flags | FLAG_IMPLIED_DIR) & ~FLAG_CONTENT_DIR,
+ ALL_FILTERS);
+ implied_dot_dir = 1;
+ }
+ len = clean_fname(fn, CFN_KEEP_TRAILING_SLASH
| CFN_DROP_TRAILING_DOT_DIR);
if (len == 1) {
if (fn[0] == '/') {
@@ -1996,15 +2006,17 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
int top_flags = FLAG_TOP_DIR | FLAG_CONTENT_DIR | flags;
file = send_file_name(f, flist, fbuf, &st,
top_flags, ALL_FILTERS);
+ if (!file)
+ continue;
if (inc_recurse) {
- if (name_type == DOT_NAME && file) {
+ if (name_type == DOT_NAME) {
if (send_dir_depth < 0) {
send_dir_depth = 0;
change_local_filter_dir(fbuf, len, send_dir_depth);
}
send_directory(f, flist, fbuf, len, flags);
}
- } else if (file)
+ } else
send_if_directory(f, flist, file, fbuf, len, flags);
} else
send_file_name(f, flist, fbuf, &st, flags, ALL_FILTERS);
diff --git a/main.c b/main.c
index 629f6d20..5985f808 100644
--- a/main.c
+++ b/main.c
@@ -902,9 +902,9 @@ static void do_server_recv(int f_in, int f_out, int argc, char *argv[])
if (sanitize_paths) {
char **dir_p;
for (dir_p = basis_dir; *dir_p; dir_p++)
- *dir_p = sanitize_path(NULL, *dir_p, NULL, curr_dir_depth);
+ *dir_p = sanitize_path(NULL, *dir_p, NULL, curr_dir_depth, SP_DEFAULT);
if (partial_dir)
- partial_dir = sanitize_path(NULL, partial_dir, NULL, curr_dir_depth);
+ partial_dir = sanitize_path(NULL, partial_dir, NULL, curr_dir_depth, SP_DEFAULT);
}
check_alt_basis_dirs();
diff --git a/options.c b/options.c
index b63956f2..f2d23f62 100644
--- a/options.c
+++ b/options.c
@@ -1032,7 +1032,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
case OPT_INCLUDE_FROM:
arg = poptGetOptArg(pc);
if (sanitize_paths)
- arg = sanitize_path(NULL, arg, NULL, 0);
+ arg = sanitize_path(NULL, arg, NULL, 0, SP_DEFAULT);
if (daemon_filter_list.head) {
int rej;
char *dir, *cp = strdup(arg);
@@ -1438,11 +1438,11 @@ int parse_arguments(int *argc_p, const char ***argv_p)
if (sanitize_paths) {
int i;
for (i = argc; i-- > 0; )
- argv[i] = sanitize_path(NULL, argv[i], "", 0);
+ argv[i] = sanitize_path(NULL, argv[i], "", 0, SP_KEEP_DOT_DIRS);
if (tmpdir)
- tmpdir = sanitize_path(NULL, tmpdir, NULL, 0);
+ tmpdir = sanitize_path(NULL, tmpdir, NULL, 0, SP_DEFAULT);
if (backup_dir)
- backup_dir = sanitize_path(NULL, backup_dir, NULL, 0);
+ backup_dir = sanitize_path(NULL, backup_dir, NULL, 0, SP_DEFAULT);
}
if (daemon_filter_list.head && !am_sender) {
struct filter_list_struct *elp = &daemon_filter_list;
@@ -1650,7 +1650,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
}
} else {
if (sanitize_paths)
- files_from = sanitize_path(NULL, files_from, NULL, 0);
+ files_from = sanitize_path(NULL, files_from, NULL, 0, SP_DEFAULT);
if (daemon_filter_list.head) {
char *dir;
if (!*files_from)
diff --git a/rsync.h b/rsync.h
index d7dcb08a..7f7e3531 100644
--- a/rsync.h
+++ b/rsync.h
@@ -189,11 +189,14 @@
#define SIGNIFICANT_ITEM_FLAGS (~(\
ITEM_BASIS_TYPE_FOLLOWS | ITEM_XNAME_FOLLOWS | ITEM_LOCAL_CHANGE))
-#define CFN_KEEP_LEADING_DOT_DIR (1<<0)
+#define CFN_KEEP_DOT_DIRS (1<<0)
#define CFN_KEEP_TRAILING_SLASH (1<<1)
#define CFN_DROP_TRAILING_DOT_DIR (1<<2)
#define CFN_COLLAPSE_DOT_DOT_DIRS (1<<3)
+#define SP_DEFAULT 0
+#define SP_KEEP_DOT_DIRS (1<<0)
+
/* Log-message categories. FLOG only goes to the log file, not the client;
* FCLIENT is the opposite. */
enum logcode {
diff --git a/util.c b/util.c
index 5a8333e8..a8b17d53 100644
--- a/util.c
+++ b/util.c
@@ -547,11 +547,15 @@ void glob_expand(char *s, char ***argv_ptr, int *argc_ptr, int *maxargs_ptr)
s = ".";
if (sanitize_paths)
- s = sanitize_path(NULL, s, "", 0);
- else
+ s = sanitize_path(NULL, s, "", 0, SP_KEEP_DOT_DIRS);
+ else {
s = strdup(s);
- if (!s)
- out_of_memory("glob_expand");
+ if (!s)
+ out_of_memory("glob_expand");
+ clean_fname(s, CFN_KEEP_DOT_DIRS
+ | CFN_KEEP_TRAILING_SLASH
+ | CFN_COLLAPSE_DOT_DOT_DIRS);
+ }
memset(&globbuf, 0, sizeof globbuf);
glob(s, 0, NULL, &globbuf);
@@ -715,13 +719,13 @@ int count_dir_elements(const char *p)
return cnt;
}
-/* Turns multiple adjacent slashes into a single slash, drops interior "."
- * elements, drops an intial "./" unless CFN_KEEP_LEADING_DOT_DIR is flagged,
- * will even drop a trailing '.' after a '/' if CFN_DROP_TRAILING_DOT_DIR is
- * flagged, removes a trailing slash (perhaps after removing the aforementioned
- * dot) unless CFN_KEEP_TRAILING_SLASH is flagged, will even collapse ".."
- * elements (except at the start of the string) if CFN_COLLAPSE_DOT_DOT_DIRS
- * is flagged. If the resulting name would be empty, we return ".". */
+/* Turns multiple adjacent slashes into a single slash, 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
+ * CFN_KEEP_TRAILING_SLASH is flagged, and will also collapse ".." elements
+ * (except at the start) if CFN_COLLAPSE_DOT_DOT_DIRS is flagged. If the
+ * resulting name would be empty, returns ".". */
unsigned int clean_fname(char *name, int flags)
{
char *limit = name - 1, *t = name, *f = name;
@@ -732,7 +736,7 @@ unsigned int clean_fname(char *name, int flags)
if ((anchored = *f == '/') != 0)
*t++ = *f++;
- else if (flags & CFN_KEEP_LEADING_DOT_DIR && *f == '.' && f[1] == '/') {
+ else if (flags & CFN_KEEP_DOT_DIRS && *f == '.' && f[1] == '/') {
*t++ = *f++;
*t++ = *f++;
}
@@ -744,7 +748,7 @@ unsigned int clean_fname(char *name, int flags)
}
if (*f == '.') {
/* discard interior "." dirs */
- if (f[1] == '/') {
+ if (f[1] == '/' && !(flags & CFN_KEEP_DOT_DIRS)) {
f += 2;
continue;
}
@@ -801,10 +805,11 @@ unsigned int clean_fname(char *name, int flags)
* ALWAYS collapses ".." elements (except for those at the start of the
* string up to "depth" deep). If the resulting name would be empty,
* change it into a ".". */
-char *sanitize_path(char *dest, const char *p, const char *rootdir, int depth)
+char *sanitize_path(char *dest, const char *p, const char *rootdir, int depth,
+ int flags)
{
char *start, *sanp;
- int rlen = 0, leave_one_dotdir = relative_paths;
+ int rlen = 0, drop_dot_dirs = !relative_paths || !(flags & SP_KEEP_DOT_DIRS);
if (dest != p) {
int plen = strlen(p);
@@ -827,6 +832,11 @@ char *sanitize_path(char *dest, const char *p, const char *rootdir, int depth)
}
}
+ if (drop_dot_dirs) {
+ while (*p == '.' && p[1] == '/')
+ p += 2;
+ }
+
start = sanp = dest + rlen;
/* This loop iterates once per filename component in p, pointing at
* the start of the name (past any prior slash) for each iteration. */
@@ -836,10 +846,8 @@ char *sanitize_path(char *dest, const char *p, const char *rootdir, int depth)
p++;
continue;
}
- if (*p == '.' && (p[1] == '/' || p[1] == '\0')) {
- if (leave_one_dotdir && p[1])
- leave_one_dotdir = 0;
- else {
+ if (drop_dot_dirs) {
+ if (*p == '.' && (p[1] == '/' || p[1] == '\0')) {
/* skip "." component */
p++;
continue;
@@ -852,10 +860,8 @@ char *sanitize_path(char *dest, const char *p, const char *rootdir, int depth)
if (sanp != start) {
/* back up sanp one level */
--sanp; /* now pointing at slash */
- while (sanp > start && sanp[-1] != '/') {
- /* skip back up to slash */
+ while (sanp > start && sanp[-1] != '/')
sanp--;
- }
}
continue;
}