X-Git-Url: https://mattmccutchen.net/rsync/rsync.git/blobdiff_plain/3f30927214317034727e11218aff0bd6d686bdf5..3f0211b63a6cdc4a2cecfd2a0dffeba172c86a47:/flist.c diff --git a/flist.c b/flist.c index 707edfd0..0b4c4e2f 100644 --- a/flist.c +++ b/flist.c @@ -63,6 +63,8 @@ extern int copy_links; extern int copy_unsafe_links; extern int protocol_version; extern int sanitize_paths; +extern int need_unsorted_flist; +extern int unsort_ndx; extern struct stats stats; extern char *filesfrom_host; @@ -74,9 +76,7 @@ extern struct filter_list_struct filter_list; extern struct filter_list_struct server_filter_list; #ifdef ICONV_OPTION -extern int ic_ndx; extern int filesfrom_convert; -extern int need_unsorted_flist; extern iconv_t ic_send, ic_recv; #endif @@ -87,11 +87,15 @@ int checksum_len; dev_t filesystem_dev; /* used to implement -x */ struct file_list *cur_flist, *first_flist, *dir_flist; -int send_dir_ndx = -1, send_dir_depth = 0; +int send_dir_ndx = -1, send_dir_depth = -1; int flist_cnt = 0; /* how many (non-tmp) file list objects exist */ int file_total = 0; /* total of all active items over all file-lists */ int flist_eof = 0; /* all the file-lists are now known */ +#define NORMAL_NAME 0 +#define SLASH_ENDING_NAME 1 +#define DOT_NAME 2 + /* Starting from protocol version 26, we always use 64-bit ino_t and dev_t * internally, even if this platform does not allow files to have 64-bit inums. * The only exception is if we're on a platform with no 64-bit type at all. @@ -335,7 +339,9 @@ static void send_file_entry(int f, struct file_struct *file, int ndx, int first_ { static time_t modtime; static mode_t mode; +#ifdef SUPPORT_HARD_LINKS static int64 dev; +#endif static dev_t rdev; static uint32 rdev_major; static uid_t uid; @@ -599,7 +605,9 @@ static struct file_struct *recv_file_entry(struct file_list *flist, { static int64 modtime; static mode_t mode; +#ifdef SUPPORT_HARD_LINKS static int64 dev; +#endif static dev_t rdev; static uint32 rdev_major; static uid_t uid; @@ -663,7 +671,7 @@ static struct file_struct *recv_file_entry(struct file_list *flist, clean_fname(thisname, 0); if (sanitize_paths) - sanitize_path(thisname, thisname, "", 0, NULL); + sanitize_path(thisname, thisname, "", 0); if ((basename = strrchr(thisname, '/')) != NULL) { int len = basename++ - thisname; @@ -718,7 +726,7 @@ static struct file_struct *recv_file_entry(struct file_list *flist, modtime = read_varlong(f, 4); #if SIZEOF_TIME_T < SIZEOF_INT64 if ((modtime > INT_MAX || modtime < INT_MIN) && !am_generator) { - rprintf(FERROR, + rprintf(FERROR_XFER, "Time value of %s truncated on receiver.\n", lastname); } @@ -856,10 +864,8 @@ static struct file_struct *recv_file_entry(struct file_list *flist, F_GROUP(file) = gid; file->flags |= gid_flags; } -#ifdef ICONV_OPTION - if (ic_ndx) + if (unsort_ndx) F_NDX(file) = flist->used + flist->ndx_start; -#endif if (basename != thisname) { file->dirname = lastdir; @@ -911,7 +917,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, NULL); + sanitize_path(bp, bp, "", lastdir_depth); } #endif @@ -1002,7 +1008,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, NULL); + sanitize_path(thisname, thisname, "", 0); if (stp && S_ISDIR(stp->st_mode)) { st = *stp; /* Needed for "symlink/." with --relative. */ @@ -1023,20 +1029,20 @@ struct file_struct *make_file(const char *fname, struct file_list *flist, if (copy_links && x_lstat(thisname, &st, NULL) == 0 && S_ISLNK(st.st_mode)) { io_error |= IOERR_GENERAL; - rprintf(FERROR, "symlink has no referent: %s\n", + rprintf(FERROR_XFER, "symlink has no referent: %s\n", full_fname(thisname)); } else #endif { enum logcode c = am_daemon && protocol_version < 28 - ? FERROR : FINFO; + ? FERROR : FWARNING; io_error |= IOERR_VANISHED; rprintf(c, "file has vanished: %s\n", full_fname(thisname)); } } else { io_error |= IOERR_GENERAL; - rsyserr(FERROR, save_errno, "readlink %s failed", + rsyserr(FERROR_XFER, save_errno, "readlink %s failed", full_fname(thisname)); } return NULL; @@ -1054,20 +1060,18 @@ struct file_struct *make_file(const char *fname, struct file_list *flist, /* -x only affects dirs because we need to avoid recursing * into a mount-point directory, not to avoid copying a * symlinked file if -L (or similar) was specified. */ - if (one_file_system && flags & FLAG_CONTENT_DIR) { - if (flags & FLAG_TOP_DIR) - filesystem_dev = st.st_dev; - else if (st.st_dev != filesystem_dev) { - if (one_file_system > 1) { - if (verbose > 1) { - rprintf(FINFO, - "[%s] skipping mount-point dir %s\n", - who_am_i(), thisname); - } - return NULL; + if (one_file_system && st.st_dev != filesystem_dev + && BITS_SETnUNSET(flags, FLAG_CONTENT_DIR, FLAG_TOP_DIR)) { + if (one_file_system > 1) { + if (verbose > 1) { + rprintf(FINFO, + "[%s] skipping mount-point dir %s\n", + who_am_i(), thisname); } - flags |= FLAG_MOUNT_DIR; + return NULL; } + flags |= FLAG_MOUNT_DIR; + flags &= ~FLAG_CONTENT_DIR; } } else flags &= ~FLAG_CONTENT_DIR; @@ -1220,10 +1224,8 @@ struct file_struct *make_file(const char *fname, struct file_list *flist, if (basename_len == 0+1) return NULL; -#ifdef ICONV_OPTION - if (ic_ndx) + if (unsort_ndx) F_NDX(file) = dir_count - 1; -#endif return file; } @@ -1308,7 +1310,7 @@ static void send_if_directory(int f, struct file_list *flist, fbuf[--len] = '\0'; if (len >= MAXPATHLEN - 1) { io_error |= IOERR_GENERAL; - rprintf(FERROR, "skipping long-named directory: %s\n", + rprintf(FERROR_XFER, "skipping long-named directory: %s\n", full_fname(fbuf)); return; } @@ -1409,6 +1411,9 @@ static void add_dirs_to_tree(int parent_ndx, struct file_list *from_flist, dir_flist->files[dir_flist->used++] = file; dir_cnt--; + if (file->basename[0] == '.' && file->basename[1] == '\0') + continue; + if (dp) DIR_NEXT_SIBLING(dp) = dir_flist->used - 1; else if (parent_dp) @@ -1444,7 +1449,7 @@ static void send_directory(int f, struct file_list *flist, char *fbuf, int len, if (!(d = opendir(fbuf))) { io_error |= IOERR_GENERAL; - rsyserr(FERROR, errno, "opendir %s failed", full_fname(fbuf)); + rsyserr(FERROR_XFER, errno, "opendir %s failed", full_fname(fbuf)); return; } @@ -1481,7 +1486,7 @@ static void send_directory(int f, struct file_list *flist, char *fbuf, int len, if (errno) { io_error |= IOERR_GENERAL; - rsyserr(FERROR, errno, "readdir(%s)", full_fname(fbuf)); + rsyserr(FERROR_XFER, errno, "readdir(%s)", full_fname(fbuf)); } closedir(d); @@ -1499,7 +1504,7 @@ static int lastpath_len = 0; static struct file_struct *lastpath_struct; static void send_implied_dirs(int f, struct file_list *flist, char *fname, - char *start, char *limit, int flags, int is_dot_dir) + char *start, char *limit, int flags, char name_type) { struct file_struct *file; item_list *relname_list; @@ -1565,7 +1570,7 @@ static void send_implied_dirs(int f, struct file_list *flist, char *fname, rnpp = EXPAND_ITEM_LIST(relname_list, relnamecache *, 32); if (!(*rnpp = (relnamecache*)new_array(char, sizeof (relnamecache) + len))) out_of_memory("send_implied_dirs"); - (*rnpp)->is_dot_dir = is_dot_dir; + (*rnpp)->name_type = name_type; strlcpy((*rnpp)->fname, limit+1, len + 1); } @@ -1586,8 +1591,19 @@ static void send1extra(int f, struct file_struct *file, struct file_list *flist) change_local_filter_dir(fbuf, dlen, send_dir_depth); - if (BITS_SETnUNSET(file->flags, FLAG_CONTENT_DIR, FLAG_MOUNT_DIR)) + if (file->flags & FLAG_CONTENT_DIR) { + if (one_file_system) { + STRUCT_STAT st; + if (link_stat(fbuf, &st, copy_dirlinks) != 0) { + io_error |= IOERR_GENERAL; + rsyserr(FERROR_XFER, errno, "link_stat %s failed", + full_fname(fbuf)); + return; + } + filesystem_dev = st.st_dev; + } send_directory(f, flist, fbuf, dlen, flags); + } if (!relative_paths) return; @@ -1599,7 +1615,7 @@ static void send1extra(int f, struct file_struct *file, struct file_list *flist) for (j = 0; j < relname_list->count; j++) { char *slash; relnamecache *rnp = ((relnamecache**)relname_list->items)[j]; - int is_dot_dir = rnp->is_dot_dir; + char name_type = rnp->name_type; fbuf[dlen] = '/'; len = strlcpy(fbuf + dlen + 1, rnp->fname, sizeof fbuf - dlen - 1); @@ -1609,15 +1625,15 @@ static void send1extra(int f, struct file_struct *file, struct file_list *flist) slash = strchr(fbuf+dlen+1, '/'); if (slash) { - send_implied_dirs(f, flist, fbuf, fbuf+dlen+1, slash, flags, is_dot_dir); + send_implied_dirs(f, flist, fbuf, fbuf+dlen+1, slash, flags, name_type); continue; } - if (is_dot_dir) { + if (name_type != NORMAL_NAME) { STRUCT_STAT st; if (link_stat(fbuf, &st, 1) != 0) { io_error |= IOERR_GENERAL; - rsyserr(FERROR, errno, "link_stat %s failed", + rsyserr(FERROR_XFER, errno, "link_stat %s failed", full_fname(fbuf)); continue; } @@ -1634,19 +1650,17 @@ void send_extra_file_list(int f, int at_least) struct file_list *flist; int64 start_write; uint16 prev_flags; - int future_cnt, save_io_error = io_error; + int old_cnt, save_io_error = io_error; if (flist_eof) return; /* Keep sending data until we have the requested number of * files in the upcoming file-lists. */ - if (cur_flist->next) { - flist = first_flist->prev; /* the newest flist */ - future_cnt = flist->ndx_end - cur_flist->next->ndx_start + 1; - } else - future_cnt = 0; - while (future_cnt < at_least) { + old_cnt = cur_flist->used; + for (flist = first_flist; flist != cur_flist; flist = flist->next) + old_cnt += flist->used; + while (file_total - old_cnt < at_least) { struct file_struct *file = dir_flist->sorted[send_dir_ndx]; int dir_ndx, dstart = dir_count; const char *pathname = F_PATHNAME(file); @@ -1655,11 +1669,9 @@ void send_extra_file_list(int f, int at_least) flist = flist_new(0, "send_extra_file_list"); start_write = stats.total_written; -#ifdef ICONV_OPTION - if (ic_ndx) + if (unsort_ndx) dir_ndx = F_NDX(file); else -#endif dir_ndx = send_dir_ndx; write_ndx(f, NDX_FLIST_OFFSET - dir_ndx); flist->parent_ndx = dir_ndx; @@ -1685,26 +1697,24 @@ void send_extra_file_list(int f, int at_least) write_byte(f, 0); -#ifdef ICONV_OPTION if (need_unsorted_flist) { if (!(flist->sorted = new_array(struct file_struct *, flist->used))) out_of_memory("send_extra_file_list"); memcpy(flist->sorted, flist->files, flist->used * sizeof (struct file_struct*)); } else -#endif flist->sorted = flist->files; clean_flist(flist, 0); - flist->ndx_end = flist->ndx_start + flist->used - 1 - - (dir_count - dstart); + flist->ndx_end = flist->ndx_start + flist->used - 1; + if (!need_unsorted_flist) + flist->ndx_end -= (dir_count - dstart); add_dirs_to_tree(send_dir_ndx, flist, dir_count - dstart); flist_done_allocating(flist); file_total += flist->used; - future_cnt += flist->used; stats.flist_size += stats.total_written - start_write; stats.num_files += flist->used; if (verbose > 3) @@ -1781,7 +1791,7 @@ struct file_list *send_file_list(int f, int argc, char *argv[]) disable_buffering = io_start_buffering_out(f); if (filesfrom_fd >= 0) { if (argv[0] && !push_dir(argv[0], 0)) { - rsyserr(FERROR, errno, "push_dir %s failed in %s", + rsyserr(FERROR_XFER, errno, "push_dir %s failed in %s", full_fname(argv[0]), curr_dir); exit_cleanup(RERR_FILESELECT); } @@ -1789,26 +1799,24 @@ struct file_list *send_file_list(int f, int argc, char *argv[]) } while (1) { - char fbuf[MAXPATHLEN]; - char *fn; - int is_dot_dir; + char fbuf[MAXPATHLEN], *fn, name_type; if (use_ff_fd) { if (read_line(filesfrom_fd, fbuf, sizeof fbuf, rl_flags) == 0) break; - sanitize_path(fbuf, fbuf, "", 0, NULL); + sanitize_path(fbuf, fbuf, "", 0); } else { if (argc-- == 0) break; strlcpy(fbuf, *argv++, MAXPATHLEN); if (sanitize_paths) - sanitize_path(fbuf, fbuf, "", 0, NULL); + sanitize_path(fbuf, fbuf, "", 0); } len = strlen(fbuf); if (relative_paths) { /* We clean up fbuf below. */ - is_dot_dir = 0; + name_type = NORMAL_NAME; } else if (!len || fbuf[len - 1] == '/') { if (len == 2 && fbuf[0] == '.') { /* Turn "./" into just "." rather than "./." */ @@ -1819,7 +1827,7 @@ struct file_list *send_file_list(int f, int argc, char *argv[]) fbuf[len++] = '.'; fbuf[len] = '\0'; } - is_dot_dir = 1; + name_type = DOT_NAME; } else if (len > 1 && fbuf[len-1] == '.' && fbuf[len-2] == '.' && (len == 2 || fbuf[len-3] == '/')) { if (len + 2 >= MAXPATHLEN) @@ -1827,11 +1835,11 @@ struct file_list *send_file_list(int f, int argc, char *argv[]) fbuf[len++] = '/'; fbuf[len++] = '.'; fbuf[len] = '\0'; - is_dot_dir = 1; - } else { - is_dot_dir = fbuf[len-1] == '.' - && (len == 1 || fbuf[len-2] == '/'); - } + name_type = DOT_NAME; + } else if (fbuf[len-1] == '.' && (len == 1 || fbuf[len-2] == '/')) + name_type = DOT_NAME; + else + name_type = NORMAL_NAME; dir = NULL; @@ -1854,36 +1862,29 @@ struct file_list *send_file_list(int f, int argc, char *argv[]) dir = "/"; else dir = fbuf; - len -= p - fbuf + 3; fn = p + 3; + while (*fn == '/') + fn++; + if (!*fn) + *--fn = '\0'; /* ensure room for '.' */ } 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'; + len = clean_fname(fn, CFN_KEEP_LEADING_DOT_DIR + | CFN_KEEP_TRAILING_SLASH + | CFN_DROP_TRAILING_DOT_DIR); if (len == 1) { if (fn[0] == '/') { fn = "/."; len = 2; + name_type = DOT_NAME; } else if (fn[0] == '.') - is_dot_dir = 1; + name_type = DOT_NAME; + } else if (fn[len-1] == '/') { + fn[--len] = '\0'; + if (len == 1 && *fn == '.') + name_type = DOT_NAME; + else + name_type = SLASH_ENDING_NAME; } /* Reject a ".." dir in the active part of the path. */ for (p = fn; (p = strstr(p, "..")) != NULL; p += 2) { @@ -1891,7 +1892,7 @@ struct file_list *send_file_list(int f, int argc, char *argv[]) && (p == fn || p[-1] == '/')) { rprintf(FERROR, "found \"..\" dir in relative path: %s\n", - fbuf); + fn); exit_cleanup(RERR_SYNTAX); } } @@ -1900,7 +1901,7 @@ struct file_list *send_file_list(int f, int argc, char *argv[]) if (!*fn) { len = 1; fn = "."; - is_dot_dir = 1; + name_type = DOT_NAME; } dirlen = dir ? strlen(dir) : 0; @@ -1915,9 +1916,9 @@ struct file_list *send_file_list(int f, int argc, char *argv[]) if (fn != fbuf) memmove(fbuf, fn, len + 1); - if (link_stat(fbuf, &st, copy_dirlinks || is_dot_dir) != 0) { + if (link_stat(fbuf, &st, copy_dirlinks || name_type != NORMAL_NAME) != 0) { io_error |= IOERR_GENERAL; - rsyserr(FERROR, errno, "link_stat %s failed", + rsyserr(FERROR_XFER, errno, "link_stat %s failed", full_fname(fbuf)); continue; } @@ -1929,8 +1930,14 @@ struct file_list *send_file_list(int f, int argc, char *argv[]) if (inc_recurse && relative_paths && *fbuf) { if ((p = strchr(fbuf+1, '/')) != NULL) { - send_implied_dirs(f, flist, fbuf, fbuf, p, flags, is_dot_dir); - continue; + if (p - fbuf == 1 && *fbuf == '.') { + if ((fn = strchr(p+1, '/')) != NULL) + p = fn; + } else + fn = p; + send_implied_dirs(f, flist, fbuf, fbuf, p, flags, name_type); + if (fn == p) + continue; } } else if (implied_dirs && (p=strrchr(fbuf,'/')) && p != fbuf) { /* Send the implied directories at the start of the @@ -1948,12 +1955,23 @@ struct file_list *send_file_list(int f, int argc, char *argv[]) send_implied_dirs(f, flist, fbuf, slash, p, flags, 0); } - if (recurse || (xfer_dirs && is_dot_dir)) { + if (one_file_system) + filesystem_dev = st.st_dev; + + if (recurse || (xfer_dirs && name_type != NORMAL_NAME)) { struct file_struct *file; int top_flags = FLAG_TOP_DIR | FLAG_CONTENT_DIR | flags; file = send_file_name(f, flist, fbuf, &st, top_flags, ALL_FILTERS); - if (file && !inc_recurse) + if (inc_recurse) { + 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) send_if_directory(f, flist, file, fbuf, len, flags); } else send_file_name(f, flist, fbuf, &st, flags, ALL_FILTERS); @@ -1988,7 +2006,6 @@ struct file_list *send_file_list(int f, int argc, char *argv[]) * receiving side to ask for whatever name it kept. For incremental * recursion mode, the sender marks duplicate dirs so that it can * send them together in a single file-list. */ -#ifdef ICONV_OPTION if (need_unsorted_flist) { if (inc_recurse) { if (!(flist->sorted = new_array(struct file_struct *, flist->used))) @@ -2001,9 +2018,7 @@ struct file_list *send_file_list(int f, int argc, char *argv[]) flist->low = 0; flist->high = flist->used - 1; } - } else -#endif - { + } else { flist->sorted = flist->files; clean_flist(flist, 0); } @@ -2035,7 +2050,10 @@ struct file_list *send_file_list(int f, int argc, char *argv[]) rprintf(FINFO, "send_file_list done\n"); if (inc_recurse) { + send_dir_depth = 1; add_dirs_to_tree(-1, flist, dir_count); + if (!file_total || strcmp(flist->sorted[0]->basename, ".") != 0) + flist->parent_ndx = -1; flist_done_allocating(flist); if (send_dir_ndx < 0) { write_ndx(f, NDX_FLIST_EOF); @@ -2075,7 +2093,7 @@ struct file_list *recv_file_list(int f) flist = flist_new(0, "recv_file_list"); if (inc_recurse) { - if (flist->ndx_start == 0) + if (flist->ndx_start == 1) dir_flist = flist_new(FLIST_TEMP, "recv_file_list"); dstart = dir_flist->used; } else { @@ -2109,7 +2127,7 @@ struct file_list *recv_file_list(int f) file_total += flist->used; flist->ndx_end = flist->ndx_start + flist->used - 1; - if (inc_recurse && flist->ndx_start) + if (inc_recurse && !need_unsorted_flist && flist->ndx_start > 1) flist->ndx_end -= dir_flist->used - dstart; if (verbose > 2) @@ -2118,7 +2136,6 @@ struct file_list *recv_file_list(int f) if (show_filelist_p()) finish_filelist_progress(flist); -#ifdef ICONV_OPTION if (need_unsorted_flist) { /* Create an extra array of index pointers that we can sort for * the generator's use (for wading through the files in sorted @@ -2137,9 +2154,7 @@ struct file_list *recv_file_list(int f) (dir_flist->used - dstart) * sizeof (struct file_struct*)); fsort(dir_flist->sorted + dstart, dir_flist->used - dstart); } - } else -#endif - { + } else { flist->sorted = flist->files; if (inc_recurse && dir_flist->used > dstart) { dir_flist->sorted = dir_flist->files; @@ -2160,6 +2175,9 @@ struct file_list *recv_file_list(int f) read_int(f); else io_error |= read_int(f); + } else if (inc_recurse && flist->ndx_start == 1) { + if (!file_total || strcmp(flist->sorted[0]->basename, ".") != 0) + flist->parent_ndx = -1; } if (verbose > 3) @@ -2289,6 +2307,8 @@ struct file_list *flist_new(int flags, char *msg) if (!flist->file_pool) out_of_memory(msg); + flist->ndx_start = inc_recurse ? 1 : 0; + first_flist = cur_flist = flist->prev = flist; } else { flist->file_pool = first_flist->file_pool; @@ -2527,7 +2547,7 @@ static void output_flist(struct file_list *flist) rprintf(FINFO, "[%s] flist start=%d, end=%d, used=%d, low=%d, high=%d\n", who, flist->ndx_start, flist->ndx_end, flist->used, flist->low, flist->high); for (i = 0; i < flist->used; i++) { - file = flist->sorted[i]; + file = flist->files[i]; if ((am_root || am_sender) && uid_ndx) { snprintf(uidbuf, sizeof uidbuf, " uid=%u", F_OWNER(file));