X-Git-Url: https://mattmccutchen.net/rsync/rsync.git/blobdiff_plain/7b558d7f8be904010cf69c8e69b6a4a8f64f3f33..4e7d07c8d4c75f0fc4207f0d6058db99b61c2f17:/flist.c diff --git a/flist.c b/flist.c index fcfde840..87a2ba79 100644 --- a/flist.c +++ b/flist.c @@ -62,16 +62,18 @@ extern struct file_list *the_file_list; extern char curr_dir[MAXPATHLEN]; +extern struct chmod_mode_struct *chmod_modes; + extern struct filter_list_struct filter_list; extern struct filter_list_struct server_filter_list; int io_error; +int checksum_len; dev_t filesystem_dev; /* used to implement -x */ +unsigned int file_struct_len; 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); @@ -82,9 +84,9 @@ void init_flist(void) /* Figure out how big the file_struct is without trailing padding */ file_struct_len = offsetof(struct file_struct, flags) + sizeof f.flags; + checksum_len = protocol_version < 21 ? 2 : MD4_SUM_LENGTH; } - static int show_filelist_p(void) { return verbose && xfer_dirs && !am_server; @@ -98,20 +100,17 @@ static void start_filelist_progress(char *kind) rflush(FINFO); } - static void emit_filelist_progress(int count) { rprintf(FINFO, " %d files...\r", count); } - static void maybe_emit_filelist_progress(int count) { if (do_progress && show_filelist_p() && (count % 100) == 0) emit_filelist_progress(count); } - static void finish_filelist_progress(const struct file_list *flist) { if (do_progress) { @@ -127,7 +126,6 @@ void show_flist_stats(void) /* Nothing yet */ } - static void list_file_entry(struct file_struct *f) { char perms[11]; @@ -144,18 +142,17 @@ static void list_file_entry(struct file_struct *f) rprintf(FINFO, "%s %11.0f %s %s -> %s\n", perms, (double)f->length, timestring(f->modtime), - safe_fname(f_name(f)), safe_fname(f->u.link)); + f_name(f, NULL), f->u.link); } else #endif { rprintf(FINFO, "%s %11.0f %s %s\n", perms, (double)f->length, timestring(f->modtime), - safe_fname(f_name(f))); + f_name(f, NULL)); } } - /** * Stat either a symlink or its referent, depending on the settings of * copy_links, copy_unsafe_links, etc. @@ -185,7 +182,7 @@ static int readlink_stat(const char *path, STRUCT_STAT *buffer, char *linkbuf) if (copy_unsafe_links && unsafe_symlink(linkbuf, path)) { if (verbose > 1) { rprintf(FINFO,"copying unsafe symlink \"%s\" -> \"%s\"\n", - safe_fname(path), safe_fname(linkbuf)); + path, linkbuf); } return do_stat(path, buffer); } @@ -261,7 +258,6 @@ static mode_t from_wire_mode(int mode) return (mode_t)mode; } - static void send_directory(int f, struct file_list *flist, char *fbuf, int len); @@ -310,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; @@ -339,9 +335,9 @@ void send_file_entry(struct file_struct *file, int f, unsigned short base_flags) io_write_phase = "send_file_entry"; - f_name_to(file, fname); + f_name(file, fname); - flags = base_flags; + flags = file->flags & XMIT_TOP_DIR; if (file->mode == mode) flags |= XMIT_SAME_MODE; @@ -414,10 +410,8 @@ void send_file_entry(struct file_struct *file, int f, unsigned short base_flags) } else write_byte(f, flags); } else { - if (!(flags & 0xFF) && !S_ISDIR(mode)) - flags |= XMIT_TOP_DIR; if (!(flags & 0xFF)) - flags |= XMIT_LONG_NAME; + flags |= S_ISDIR(mode) ? XMIT_LONG_NAME : XMIT_TOP_DIR; write_byte(f, flags); } if (flags & XMIT_SAME_NAME) @@ -480,19 +474,15 @@ void send_file_entry(struct file_struct *file, int f, unsigned short base_flags) } #endif - if (always_checksum) { + if (always_checksum && (S_ISREG(mode) || protocol_version < 28)) { char *sum; if (S_ISREG(mode)) sum = file->u.sum; - else if (protocol_version < 28) { + else { /* Prior to 28, we sent a useless set of nulls. */ sum = empty_sum; - } else - sum = NULL; - if (sum) { - write_buf(f, sum, - protocol_version < 21 ? 2 : MD4_SUM_LENGTH); } + write_buf(f, sum, checksum_len); } strlcpy(lastname, fname, MAXPATHLEN); @@ -500,8 +490,6 @@ void send_file_entry(struct file_struct *file, int f, unsigned short base_flags) io_write_phase = "unknown"; } - - static struct file_struct *receive_file_entry(struct file_list *flist, unsigned short flags, int f) { @@ -545,8 +533,8 @@ static struct file_struct *receive_file_entry(struct file_list *flist, if (l2 >= MAXPATHLEN - l1) { rprintf(FERROR, "overflow: flags=0x%x l1=%d l2=%d lastname=%s\n", - flags, l1, l2, safe_fname(lastname)); - overflow("receive_file_entry"); + flags, l1, l2, lastname); + overflow_exit("receive_file_entry"); } strlcpy(thisname, lastname, l1 + 1); @@ -581,6 +569,9 @@ static struct file_struct *receive_file_entry(struct file_list *flist, if (!(flags & XMIT_SAME_MODE)) mode = from_wire_mode(read_int(f)); + if (chmod_modes && !S_ISLNK(mode)) + mode = tweak_mode(mode, chmod_modes); + if (preserve_uid && !(flags & XMIT_SAME_UID)) uid = (uid_t)read_int(f); if (preserve_gid && !(flags & XMIT_SAME_GID)) @@ -611,7 +602,7 @@ static struct file_struct *receive_file_entry(struct file_list *flist, if (linkname_len <= 0 || linkname_len > MAXPATHLEN) { rprintf(FERROR, "overflow: linkname_len=%d\n", linkname_len - 1); - overflow("receive_file_entry"); + overflow_exit("receive_file_entry"); } } else @@ -628,7 +619,6 @@ static struct file_struct *receive_file_entry(struct file_list *flist, memset(bp, 0, file_struct_len); bp += file_struct_len; - file->flags = 0; file->modtime = modtime; file->length = file_length; file->mode = mode; @@ -653,13 +643,17 @@ static struct file_struct *receive_file_entry(struct file_list *flist, if (basename_len == 1+1 && *basename == '.') /* +1 for '\0' */ file->dir.depth--; if (flags & XMIT_TOP_DIR) { - in_del_hier = 1; + in_del_hier = recurse; del_hier_name_len = file->dir.depth == 0 ? 0 : l1 + l2; + if (relative_paths && del_hier_name_len > 2 + && lastname[del_hier_name_len-1] == '.' + && lastname[del_hier_name_len-2] == '/') + del_hier_name_len -= 2; file->flags |= FLAG_TOP_DIR | FLAG_DEL_HERE; } else if (in_del_hier) { if (!relative_paths || !del_hier_name_len || (l1 >= del_hier_name_len - && thisname[del_hier_name_len] == '/')) + && lastname[del_hier_name_len] == '/')) file->flags |= FLAG_DEL_HERE; else in_del_hier = 0; @@ -705,20 +699,16 @@ static struct file_struct *receive_file_entry(struct file_list *flist, } #endif - if (always_checksum) { + if (always_checksum && (sum_len || protocol_version < 28)) { char *sum; if (sum_len) { file->u.sum = sum = bp; /*bp += sum_len;*/ - } else if (protocol_version < 28) { + } else { /* Prior to 28, we get a useless set of nulls. */ sum = empty_sum; - } else - sum = NULL; - if (sum) { - read_buf(f, sum, - protocol_version < 21 ? 2 : MD4_SUM_LENGTH); } + read_buf(f, sum, checksum_len); } if (!preserve_perms) { @@ -730,7 +720,6 @@ static struct file_struct *receive_file_entry(struct file_list *flist, return file; } - /** * Create a file_struct for a named file by reading its stat() * information and performing extensive checks against global @@ -747,6 +736,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; @@ -758,15 +748,13 @@ 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; if (strlcpy(thisname, fname, sizeof thisname) >= sizeof thisname - flist_dir_len) { - rprintf(FINFO, "skipping overly long name: %s\n", - safe_fname(fname)); + rprintf(FINFO, "skipping overly long name: %s\n", fname); return NULL; } clean_fname(thisname, 0); @@ -775,7 +763,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 @@ -811,7 +801,7 @@ struct file_struct *make_file(char *fname, struct file_list *flist, goto skip_filters; if (S_ISDIR(st.st_mode) && !xfer_dirs) { - rprintf(FINFO, "skipping directory %s\n", safe_fname(thisname)); + rprintf(FINFO, "skipping directory %s\n", thisname); return NULL; } @@ -819,8 +809,16 @@ 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) { + if (verbose > 2) { + rprintf(FINFO, "skipping mount-point dir %s\n", + thisname); + } + return NULL; + } flags |= FLAG_MOUNT_POINT; + } if (is_excluded(thisname, S_ISDIR(st.st_mode) != 0, filter_level)) return NULL; @@ -833,11 +831,11 @@ struct file_struct *make_file(char *fname, struct file_list *flist, return NULL; } -skip_filters: + skip_filters: if (verbose > 2) { rprintf(FINFO, "[%s] make_file(%s,*,%d)\n", - who_am_i(), safe_fname(thisname), filter_level); + who_am_i(), thisname, filter_level); } if ((basename = strrchr(thisname, '/')) != NULL) { @@ -861,16 +859,16 @@ skip_filters: linkname_len = 0; #endif - sum_len = always_checksum && S_ISREG(st.st_mode) ? MD4_SUM_LENGTH : 0; + sum_len = always_checksum && am_sender && S_ISREG(st.st_mode) + ? MD4_SUM_LENGTH : 0; alloc_len = file_struct_len + dirname_len + basename_len + linkname_len + sum_len; - if (flist) { - bp = pool_alloc(flist->file_pool, alloc_len, - "receive_file_entry"); - } else { + if (flist) + bp = pool_alloc(flist->file_pool, alloc_len, "make_file"); + else { if (!(bp = new_array(char, alloc_len))) - out_of_memory("receive_file_entry"); + out_of_memory("make_file"); } file = (struct file_struct *)bp; @@ -943,7 +941,7 @@ skip_filters: if (keep_dirlinks && linkname_len && flist) { STRUCT_STAT st2; int save_mode = file->mode; - file->mode = S_IFDIR; /* find a directory w/our name */ + file->mode = S_IFDIR; /* Find a directory with our name. */ if (flist_find(the_file_list, file) >= 0 && do_stat(thisname, &st2) == 0 && S_ISDIR(st2.st_mode)) { file->modtime = st2.st_mtime; @@ -962,34 +960,39 @@ skip_filters: return file; } - 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; + if (chmod_modes && !S_ISLNK(file->mode)) + file->mode = tweak_mode(file->mode, chmod_modes); + maybe_emit_filelist_progress(flist->count + flist_count_offset); flist_expand(flist); if (file->basename[0]) { flist->files[flist->count++] = file; - send_file_entry(file, f, base_flags); + send_file_entry(file, f); } return file; } static void send_if_directory(int f, struct file_list *flist, - struct file_struct *file) + struct file_struct *file, + char *fbuf, unsigned int ol) { - char fbuf[MAXPATHLEN]; + char is_dot_dir = fbuf[ol-1] == '.' && (ol == 1 || fbuf[ol-2] == '/'); if (S_ISDIR(file->mode) - && !(file->flags & FLAG_MOUNT_POINT) && f_name_to(file, fbuf)) { + && !(file->flags & FLAG_MOUNT_POINT) && f_name(file, fbuf)) { void *save_filters; unsigned int len = strlen(fbuf); if (len > 1 && fbuf[len-1] == '/') @@ -1003,10 +1006,12 @@ static void send_if_directory(int f, struct file_list *flist, save_filters = push_local_filters(fbuf, len); send_directory(f, flist, fbuf, len); pop_local_filters(save_filters); + fbuf[ol] = '\0'; + if (is_dot_dir) + fbuf[ol-1] = '.'; } } - /* This function is normally called by the sender, but the receiving side also * calls it from get_dirlist() with f set to -1 so that we just construct the * file list in memory without sending it over the wire. Also, get_dirlist() @@ -1038,14 +1043,15 @@ static void send_directory(int f, struct file_list *flist, if (dname[0] == '.' && (dname[1] == '\0' || (dname[1] == '.' && dname[2] == '\0'))) continue; - if (strlcpy(p, dname, remainder) < remainder) - send_file_name(f, flist, fbuf, 0); - else { + if (strlcpy(p, dname, remainder) >= remainder) { io_error |= IOERR_GENERAL; rprintf(FINFO, "cannot send long-named file %s\n", full_fname(fbuf)); + continue; } + + send_file_name(f, flist, fbuf, NULL, 0); } fbuf[len] = '\0'; @@ -1060,14 +1066,13 @@ static void send_directory(int f, struct file_list *flist, if (recurse) { int i, end = flist->count - 1; for (i = start; i <= end; i++) - send_if_directory(f, flist, flist->files[i]); + send_if_directory(f, flist, flist->files[i], fbuf, len); } } - struct file_list *send_file_list(int f, int argc, char *argv[]) { - int l; + int len; STRUCT_STAT st; char *p, *dir, olddir[sizeof curr_dir]; char lastpath[MAXPATHLEN] = ""; @@ -1095,48 +1100,59 @@ struct file_list *send_file_list(int f, int argc, char *argv[]) } while (1) { - struct file_struct *file; - char fname2[MAXPATHLEN]; - char *fname = fname2; + char fbuf[MAXPATHLEN]; + char *fn; int is_dot_dir; if (use_ff_fd) { - if (read_filesfrom_line(filesfrom_fd, fname) == 0) + if (read_filesfrom_line(filesfrom_fd, fbuf) == 0) break; - sanitize_path(fname, fname, "", 0); + sanitize_path(fbuf, fbuf, "", 0); } else { if (argc-- == 0) break; - strlcpy(fname, *argv++, MAXPATHLEN); + strlcpy(fbuf, *argv++, MAXPATHLEN); if (sanitize_paths) - sanitize_path(fname, fname, "", 0); + sanitize_path(fbuf, fbuf, "", 0); } - l = strlen(fname); - if (!l || fname[l - 1] == '/') { - if (l == 2 && fname[0] == '.') { + len = strlen(fbuf); + 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 "./." */ - fname[1] = '\0'; - } else if (l < MAXPATHLEN) { - fname[l++] = '.'; - fname[l] = '\0'; + fbuf[1] = '\0'; + } else { + if (len + 1 >= MAXPATHLEN) + overflow_exit("send_file_list"); + fbuf[len++] = '.'; + fbuf[len] = '\0'; } is_dot_dir = 1; + } else if (len > 1 && fbuf[len-1] == '.' && fbuf[len-2] == '.' + && (len == 2 || fbuf[len-3] == '/')) { + if (len + 2 >= MAXPATHLEN) + overflow_exit("send_file_list"); + fbuf[len++] = '/'; + fbuf[len++] = '.'; + fbuf[len] = '\0'; + is_dot_dir = 1; } else { - is_dot_dir = fname[l-1] == '.' - && (l == 1 || fname[l-2] == '/'); + is_dot_dir = fbuf[len-1] == '.' + && (len == 1 || fbuf[len-2] == '/'); } - if (link_stat(fname, &st, keep_dirlinks) != 0) { + if (link_stat(fbuf, &st, keep_dirlinks) != 0) { io_error |= IOERR_GENERAL; rsyserr(FERROR, errno, "link_stat %s failed", - full_fname(fname)); + full_fname(fbuf)); continue; } if (S_ISDIR(st.st_mode) && !xfer_dirs) { - rprintf(FINFO, "skipping directory %s\n", - safe_fname(fname)); + rprintf(FINFO, "skipping directory %s\n", fbuf); continue; } @@ -1144,54 +1160,70 @@ struct file_list *send_file_list(int f, int argc, char *argv[]) olddir[0] = '\0'; if (!relative_paths) { - p = strrchr(fname, '/'); + p = strrchr(fbuf, '/'); if (p) { - *p = 0; - if (p == fname) + *p = '\0'; + if (p == fbuf) dir = "/"; else - dir = fname; - fname = p + 1; - } - } else if (implied_dirs && (p=strrchr(fname,'/')) && p != fname) { - /* this ensures we send the intermediate directories, - thus getting their permissions right */ - char *lp = lastpath, *fn = fname, *slash = fname; - *p = 0; - /* Skip any initial directories in our path that we - * have in common with lastpath. */ - while (*fn && *lp == *fn) { - if (*fn == '/') - slash = fn; - lp++, fn++; + dir = fbuf; + len -= p - fbuf + 1; + 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; + /* 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; } - *p = '/'; - if (fn != p || (*lp && *lp != '/')) { - int save_copy_links = copy_links; - int save_xfer_dirs = xfer_dirs; - copy_links = copy_unsafe_links; - xfer_dirs = 1; - while ((slash = strchr(slash+1, '/')) != 0) { - *slash = 0; - send_file_name(f, flist, fname, 0); - *slash = '/'; + 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); } - copy_links = save_copy_links; - xfer_dirs = save_xfer_dirs; - *p = 0; - strlcpy(lastpath, fname, sizeof lastpath); - *p = '/'; } } - if (!*fname) - fname = "."; + if (!*fn) { + len = 1; + fn = "."; + } if (dir && *dir) { static char *lastdir; static int lastdir_len; - strcpy(olddir, curr_dir); /* can't overflow */ + strlcpy(olddir, curr_dir, sizeof olddir); if (!push_dir(dir)) { io_error |= IOERR_GENERAL; @@ -1209,20 +1241,56 @@ struct file_list *send_file_list(int f, int argc, char *argv[]) } } + if (fn != fbuf) + memmove(fbuf, fn, len + 1); + + if (implied_dirs && (p=strrchr(fbuf,'/')) && p != fbuf) { + /* Send the implied directories at the start of the + * source spec, so we get their permissions right. */ + char *lp = lastpath, *slash = fbuf; + *p = '\0'; + /* Skip any initial directories in our path that we + * have in common with lastpath. */ + for (fn = fbuf; *fn && *lp == *fn; lp++, fn++) { + if (*fn == '/') + slash = fn; + } + *p = '/'; + if (fn != p || (*lp && *lp != '/')) { + int save_copy_links = copy_links; + int save_xfer_dirs = xfer_dirs; + copy_links = copy_unsafe_links; + xfer_dirs = 1; + while ((slash = strchr(slash+1, '/')) != 0) { + *slash = '\0'; + send_file_name(f, flist, fbuf, NULL, 0); + *slash = '/'; + } + copy_links = save_copy_links; + xfer_dirs = save_xfer_dirs; + *p = '\0'; + strlcpy(lastpath, fbuf, sizeof lastpath); + *p = '/'; + } + } + if (one_file_system) filesystem_dev = st.st_dev; - if ((file = send_file_name(f, flist, fname, XMIT_TOP_DIR))) { - if (recurse || (xfer_dirs && is_dot_dir)) - send_if_directory(f, flist, file); - } + if (recurse || (xfer_dirs && is_dot_dir)) { + struct file_struct *file; + 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, &st, 0); if (olddir[0]) { flist_dir = NULL; flist_dir_len = 0; if (!pop_dir(olddir)) { rsyserr(FERROR, errno, "pop_dir %s failed", - full_fname(dir)); + full_fname(olddir)); exit_cleanup(RERR_FILESELECT); } } @@ -1235,7 +1303,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); @@ -1255,8 +1323,6 @@ struct file_list *send_file_list(int f, int argc, char *argv[]) * without causing a compatibility problem with older versions. */ clean_flist(flist, 0, 0); - /* Now send the uid/gid list. This was introduced in - * protocol version 15 */ send_uid_list(f); /* send the io_error flag */ @@ -1275,7 +1341,6 @@ struct file_list *send_file_list(int f, int argc, char *argv[]) return flist; } - struct file_list *recv_file_list(int f) { struct file_list *flist; @@ -1295,7 +1360,6 @@ struct file_list *recv_file_list(int f) if (!flist->files) goto oom; - while ((flags = read_byte(f)) != 0) { struct file_struct *file; @@ -1314,7 +1378,7 @@ struct file_list *recv_file_list(int f) if (verbose > 2) { rprintf(FINFO, "recv_file_name(%s)\n", - safe_fname(f_name(file))); + f_name(file, NULL)); } } receive_file_entry(NULL, 0, 0); /* Signal that we're done. */ @@ -1328,8 +1392,6 @@ struct file_list *recv_file_list(int f) clean_flist(flist, relative_paths, 1); if (f >= 0) { - /* Now send the uid/gid list. This was introduced in - * protocol version 15 */ recv_uid_list(f, flist); /* Recv the io_error flag */ @@ -1356,18 +1418,16 @@ struct file_list *recv_file_list(int f) return flist; -oom: + oom: out_of_memory("recv_file_list"); return NULL; /* not reached */ } - static int file_compare(struct file_struct **file1, struct file_struct **file2) { return f_name_cmp(*file1, *file2); } - /* Search for an identically-named item in the file list. Note that the * items must agree in their directory-ness, or no match is returned. */ int flist_find(struct file_list *flist, struct file_struct *f) @@ -1397,7 +1457,6 @@ int flist_find(struct file_list *flist, struct file_struct *f) return -1; } - /* * Free up any resources a file_struct has allocated * and clear the file. @@ -1409,7 +1468,6 @@ void clear_file(int i, struct file_list *flist) memset(flist->files[i], 0, file_struct_len); } - /* * allocate a new file list */ @@ -1449,7 +1507,6 @@ void flist_free(struct file_list *flist) free(flist); } - /* * This routine ensures we don't have any duplicate names in our file list. * duplicate names can cause corruption because of the pipelining @@ -1458,13 +1515,15 @@ static void clean_flist(struct file_list *flist, int strip_root, int no_dups) { int i, prev_i = 0; - if (!flist || flist->count == 0) + if (!flist) + return; + if (flist->count == 0) { + flist->high = -1; 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) { @@ -1507,12 +1566,12 @@ static void clean_flist(struct file_list *flist, int strip_root, int no_dups) if (verbose > 1 && !am_server) { rprintf(FINFO, "removing duplicate name %s from file list (%d)\n", - safe_fname(f_name(file)), drop); + f_name(file, NULL), drop); } /* Make sure that if we unduplicate '.', that we don't * lose track of a user-specified top directory. */ - if (flist->files[drop]->flags & FLAG_TOP_DIR) - flist->files[keep]->flags |= FLAG_TOP_DIR; + flist->files[keep]->flags |= flist->files[drop]->flags + & (FLAG_TOP_DIR|FLAG_DEL_HERE); clear_file(drop, flist); @@ -1550,7 +1609,6 @@ static void clean_flist(struct file_list *flist, int strip_root, int no_dups) } } - static void output_flist(struct file_list *flist) { char uidbuf[16], gidbuf[16], depthbuf[16]; @@ -1572,14 +1630,13 @@ static void output_flist(struct file_list *flist) sprintf(depthbuf, "%d", file->dir.depth); rprintf(FINFO, "[%s] i=%d %s %s%s%s%s mode=0%o len=%.0f%s%s flags=%x\n", who, i, am_sender ? NS(file->dir.root) : depthbuf, - file->dirname ? safe_fname(file->dirname) : "", + file->dirname ? file->dirname : "", file->dirname ? "/" : "", NS(file->basename), S_ISDIR(file->mode) ? "/" : "", (int)file->mode, (double)file->length, uidbuf, gidbuf, file->flags); } } - enum fnc_state { s_DIR, s_SLASH, s_BASE, s_TRAILING }; enum fnc_type { t_PATH, t_ITEM }; @@ -1667,8 +1724,13 @@ int f_name_cmp(struct file_struct *f1, struct file_struct *f2) break; case s_SLASH: type1 = S_ISDIR(f1->mode) ? t_path : t_ITEM; - state1 = s_BASE; c1 = (uchar*)f1->basename; + if (type1 == t_PATH && *c1 == '.' && !c1[1]) { + type1 = t_ITEM; + state1 = s_TRAILING; + c1 = (uchar*)""; + } else + state1 = s_BASE; break; case s_BASE: state1 = s_TRAILING; @@ -1692,8 +1754,13 @@ int f_name_cmp(struct file_struct *f1, struct file_struct *f2) break; case s_SLASH: type2 = S_ISDIR(f2->mode) ? t_path : t_ITEM; - state2 = s_BASE; c2 = (uchar*)f2->basename; + if (type2 == t_PATH && *c2 == '.' && !c2[1]) { + type2 = t_ITEM; + state2 = s_TRAILING; + c2 = (uchar*)""; + } else + state2 = s_BASE; break; case s_BASE: state2 = s_TRAILING; @@ -1716,16 +1783,24 @@ int f_name_cmp(struct file_struct *f1, struct file_struct *f2) return dif; } - /* Return a copy of the full filename of a flist entry, using the indicated - * buffer. No size-checking is done because we checked the size when creating - * the file_struct entry. + * buffer or one of 5 static buffers if fbuf is NULL. No size-checking is + * done because we checked the size when creating the file_struct entry. */ -char *f_name_to(struct file_struct *f, char *fbuf) +char *f_name(struct file_struct *f, char *fbuf) { if (!f || !f->basename) return NULL; + if (!fbuf) { + static char names[5][MAXPATHLEN]; + static unsigned int n; + + n = (n + 1) % (sizeof names / sizeof names[0]); + + fbuf = names[n]; + } + if (f->dirname) { int len = strlen(f->dirname); memcpy(fbuf, f->dirname, len); @@ -1733,22 +1808,10 @@ char *f_name_to(struct file_struct *f, char *fbuf) strcpy(fbuf + len + 1, f->basename); } else strcpy(fbuf, f->basename); - return fbuf; -} - -/* Like f_name_to(), but we rotate through 5 static buffers of our own. */ -char *f_name(struct file_struct *f) -{ - static char names[5][MAXPATHLEN]; - static unsigned int n; - - n = (n + 1) % (sizeof names / sizeof names[0]); - - return f_name_to(f, names[n]); + return fbuf; } - /* Do a non-recursive scan of the named directory, possibly ignoring all * exclude rules except for the daemon's. If "dlen" is >=0, it is the length * of the dirname string, and also indicates that "dirname" is a MAXPATHLEN