X-Git-Url: https://mattmccutchen.net/rsync/rsync.git/blobdiff_plain/5e4ff5f9c58c2b84ef32d43e542a8fa12b64bc65..3b17384631b95be73f69dd3a712009a5792c0f3c:/flist.c diff --git a/flist.c b/flist.c index 2eafdccf..9654bd72 100644 --- a/flist.c +++ b/flist.c @@ -74,7 +74,6 @@ dev_t filesystem_dev; /* used to implement -x */ static char empty_sum[MD4_SUM_LENGTH]; static int flist_count_offset; static unsigned int file_struct_len; -static struct file_list *sorting_flist; static void clean_flist(struct file_list *flist, int strip_root, int no_dups); static void output_flist(struct file_list *flist); @@ -307,7 +306,7 @@ void flist_expand(struct file_list *flist) out_of_memory("flist_expand"); } -void send_file_entry(struct file_struct *file, int f, unsigned short base_flags) +static void send_file_entry(struct file_struct *file, int f) { unsigned short flags; static time_t modtime; @@ -338,7 +337,7 @@ void send_file_entry(struct file_struct *file, int f, unsigned short base_flags) f_name(file, fname); - flags = base_flags; + flags = file->flags & XMIT_TOP_DIR; if (file->mode == mode) flags |= XMIT_SAME_MODE; @@ -734,6 +733,7 @@ static struct file_struct *receive_file_entry(struct file_list *flist, * important case. Some systems may not have d_type. **/ struct file_struct *make_file(char *fname, struct file_list *flist, + STRUCT_STAT *stp, unsigned short flags, int filter_level) { static char *lastdir; @@ -745,7 +745,6 @@ struct file_struct *make_file(char *fname, struct file_list *flist, char linkname[MAXPATHLEN]; int alloc_len, basename_len, dirname_len, linkname_len, sum_len; char *basename, *dirname, *bp; - unsigned short flags = 0; if (!flist || !flist->count) /* Ignore lastdir when invalid. */ lastdir_len = -1; @@ -761,7 +760,9 @@ struct file_struct *make_file(char *fname, struct file_list *flist, memset(sum, 0, SUM_LENGTH); - if (readlink_stat(thisname, &st, linkname) != 0) { + if (stp && S_ISDIR(stp->st_mode)) + st = *stp; /* Needed for "symlink/." with --relative. */ + else if (readlink_stat(thisname, &st, linkname) != 0) { int save_errno = errno; /* See if file is excluded before reporting an error. */ if (filter_level != NO_FILTERS @@ -805,8 +806,11 @@ struct file_struct *make_file(char *fname, struct file_list *flist, * into a mount-point directory, not to avoid copying a symlinked * file if -L (or similar) was specified. */ if (one_file_system && st.st_dev != filesystem_dev - && S_ISDIR(st.st_mode)) + && S_ISDIR(st.st_mode)) { + if (one_file_system > 1) + return NULL; flags |= FLAG_MOUNT_POINT; + } if (is_excluded(thisname, S_ISDIR(st.st_mode) != 0, filter_level)) return NULL; @@ -952,11 +956,13 @@ struct file_struct *make_file(char *fname, struct file_list *flist, } static struct file_struct *send_file_name(int f, struct file_list *flist, - char *fname, unsigned short base_flags) + char *fname, STRUCT_STAT *stp, + unsigned short flags) { struct file_struct *file; - file = make_file(fname, flist, f == -2 ? SERVER_FILTERS : ALL_FILTERS); + file = make_file(fname, flist, stp, flags, + f == -2 ? SERVER_FILTERS : ALL_FILTERS); if (!file) return NULL; @@ -966,7 +972,7 @@ static struct file_struct *send_file_name(int f, struct file_list *flist, if (file->basename[0]) { flist->files[flist->count++] = file; - send_file_entry(file, f, base_flags); + send_file_entry(file, f); } return file; } @@ -1037,7 +1043,7 @@ static void send_directory(int f, struct file_list *flist, continue; } - send_file_name(f, flist, fbuf, 0); + send_file_name(f, flist, fbuf, NULL, 0); } fbuf[len] = '\0'; @@ -1103,7 +1109,10 @@ struct file_list *send_file_list(int f, int argc, char *argv[]) } len = strlen(fbuf); - if (!len || fbuf[len - 1] == '/') { + if (relative_paths) { + /* We clean up fbuf below. */ + is_dot_dir = 0; + } else if (!len || fbuf[len - 1] == '/') { if (len == 2 && fbuf[0] == '.') { /* Turn "./" into just "." rather than "./." */ fbuf[1] = '\0'; @@ -1154,16 +1163,48 @@ struct file_list *send_file_list(int f, int argc, char *argv[]) fn = p + 1; } else fn = fbuf; - } else if ((p = strstr(fbuf, "/./")) != NULL) { - *p = '\0'; - if (p == fbuf) - dir = "/"; - else - dir = fbuf; - len -= p - fbuf + 3; - fn = p + 3; - } else - fn = fbuf; + } else { + if ((p = strstr(fbuf, "/./")) != NULL) { + *p = '\0'; + if (p == fbuf) + dir = "/"; + else + dir = fbuf; + len -= p - fbuf + 3; + fn = p + 3; + } else + fn = fbuf; + /* Get rid of trailing "/" and "/.". */ + while (len) { + if (fn[len - 1] == '/') { + is_dot_dir = 1; + if (!--len && !dir) { + len++; + break; + } + } + else if (len >= 2 && fn[len - 1] == '.' + && fn[len - 2] == '/') { + is_dot_dir = 1; + if (!(len -= 2) && !dir) { + len++; + break; + } + } else + break; + } + fn[len] = '\0'; + /* Reject a ".." dir in the active part of the path. */ + for (p = fn; (p = strstr(p, "..")) != NULL; p += 2) { + if ((p[2] == '/' || p[2] == '\0') + && (p == fn || p[-1] == '/')) { + rprintf(FERROR, + "found \"..\" dir in relative path: %s\n", + fbuf); + exit_cleanup(RERR_SYNTAX); + } + } + } if (!*fn) { len = 1; @@ -1214,7 +1255,7 @@ struct file_list *send_file_list(int f, int argc, char *argv[]) xfer_dirs = 1; while ((slash = strchr(slash+1, '/')) != 0) { *slash = '\0'; - send_file_name(f, flist, fbuf, 0); + send_file_name(f, flist, fbuf, NULL, 0); *slash = '/'; } copy_links = save_copy_links; @@ -1230,10 +1271,11 @@ struct file_list *send_file_list(int f, int argc, char *argv[]) if (recurse || (xfer_dirs && is_dot_dir)) { struct file_struct *file; - if ((file = send_file_name(f, flist, fbuf, XMIT_TOP_DIR))) + file = send_file_name(f, flist, fbuf, &st, FLAG_TOP_DIR); + if (file) send_if_directory(f, flist, file, fbuf, len); } else - send_file_name(f, flist, fbuf, 0); + send_file_name(f, flist, fbuf, &st, 0); if (olddir[0]) { flist_dir = NULL; @@ -1253,7 +1295,7 @@ struct file_list *send_file_list(int f, int argc, char *argv[]) stats.flist_buildtime = 1; start_tv = end_tv; - send_file_entry(NULL, f, 0); + send_file_entry(NULL, f); if (show_filelist_p()) finish_filelist_progress(flist); @@ -1476,10 +1518,8 @@ static void clean_flist(struct file_list *flist, int strip_root, int no_dups) return; } - sorting_flist = flist; qsort(flist->files, flist->count, sizeof flist->files[0], (int (*)())file_compare); - sorting_flist = NULL; for (i = no_dups? 0 : flist->count; i < flist->count; i++) { if (flist->files[i]->basename) {