Some improvements to the file-name cleaning code:
authorWayne Davison <wayned@samba.org>
Thu, 20 Mar 2008 18:59:54 +0000 (11:59 -0700)
committerWayne Davison <wayned@samba.org>
Fri, 21 Mar 2008 05:39:29 +0000 (22:39 -0700)
- 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.

exclude.c
flist.c
main.c
options.c
rsync.h
util.c

index d192b55..422a824 100644 (file)
--- 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;
                }
                        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;
                        rprintf(FERROR, "merge-file name overflows: %s\n",
                                merge_file);
                        return NULL;
diff --git a/flist.c b/flist.c
index f6385ca..dce55c7 100644 (file)
--- 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)
        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;
 
        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)
                } 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
                }
        }
 #endif
@@ -1030,7 +1030,7 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
        }
        clean_fname(thisname, 0);
        if (sanitize_paths)
        }
        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. */
 
        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);
                     | (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())
 
        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;
                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)
                } 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);
                }
 
                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 = "/";
                                *p = '\0';
                                if (p == fbuf)
                                        dir = "/";
-                               else
+                               else {
                                        dir = fbuf;
                                        dir = fbuf;
+                                       clean_fname(dir, 0);
+                               }
                                fn = p + 3;
                                while (*fn == '/')
                                        fn++;
                                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;
                                        *--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] == '/') {
                                            | 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);
                        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 (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);
                                }
                                        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);
                                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 629f6d2..5985f80 100644 (file)
--- 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++)
        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)
                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();
 
        }
        check_alt_basis_dirs();
 
index b63956f..f2d23f6 100644 (file)
--- 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)
                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);
                        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; )
        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)
                if (tmpdir)
-                       tmpdir = sanitize_path(NULL, tmpdir, NULL, 0);
+                       tmpdir = sanitize_path(NULL, tmpdir, NULL, 0, SP_DEFAULT);
                if (backup_dir)
                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;
        }
        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)
                        }
                } 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)
                        if (daemon_filter_list.head) {
                                char *dir;
                                if (!*files_from)
diff --git a/rsync.h b/rsync.h
index d7dcb08..7f7e353 100644 (file)
--- a/rsync.h
+++ b/rsync.h
 #define SIGNIFICANT_ITEM_FLAGS (~(\
        ITEM_BASIS_TYPE_FOLLOWS | ITEM_XNAME_FOLLOWS | ITEM_LOCAL_CHANGE))
 
 #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 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 {
 /* 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 5a8333e..a8b17d5 100644 (file)
--- 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 = ".";
 
        if (sanitize_paths)
-               s = sanitize_path(NULL, s, "", 0);
-       else
+               s = sanitize_path(NULL, s, "", 0, SP_KEEP_DOT_DIRS);
+       else {
                s = strdup(s);
                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);
 
        memset(&globbuf, 0, sizeof globbuf);
        glob(s, 0, NULL, &globbuf);
@@ -715,13 +719,13 @@ int count_dir_elements(const char *p)
        return cnt;
 }
 
        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;
 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++;
 
        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++;
        }
                *t++ = *f++;
                *t++ = *f++;
        }
@@ -744,7 +748,7 @@ unsigned int clean_fname(char *name, int flags)
                }
                if (*f == '.') {
                        /* discard interior "." dirs */
                }
                if (*f == '.') {
                        /* discard interior "." dirs */
-                       if (f[1] == '/') {
+                       if (f[1] == '/' && !(flags & CFN_KEEP_DOT_DIRS)) {
                                f += 2;
                                continue;
                        }
                                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 ".". */
  * 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;
 {
        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);
 
        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. */
        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;
                }
                        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;
                                /* 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 */
                                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--;
                                                sanp--;
-                                       }
                                }
                                continue;
                        }
                                }
                                continue;
                        }