X-Git-Url: https://mattmccutchen.net/rsync/rsync.git/blobdiff_plain/0a5f12720e49bc6f64eec54e252d9ca3b1b3169e..82471e68a8bb3da8ca95f2b6564c330c52ce891e:/flist.c diff --git a/flist.c b/flist.c index 31fcf2f7..21eef260 100644 --- a/flist.c +++ b/flist.c @@ -34,6 +34,7 @@ extern int do_progress; extern int am_root; extern int am_server; extern int am_daemon; +extern int am_sender; extern int always_checksum; extern int module_id; extern int ignore_errors; @@ -42,8 +43,8 @@ extern int numeric_ids; extern int cvs_exclude; extern int recurse; +extern int xfer_dirs; extern char curr_dir[MAXPATHLEN]; -extern char *files_from; extern int filesfrom_fd; extern int one_file_system; @@ -72,9 +73,10 @@ int io_error; static char empty_sum[MD4_SUM_LENGTH]; static unsigned int file_struct_len; +static struct file_list *received_flist; static void clean_flist(struct file_list *flist, int strip_root, int no_dups); -static void output_flist(struct file_list *flist); +static void output_flist(struct file_list *flist, const char *whose_list); void init_flist(void) { @@ -87,7 +89,7 @@ void init_flist(void) static int show_filelist_p(void) { - return verbose && (recurse || files_from) && !am_server; + return verbose && xfer_dirs && !am_server; } static void start_filelist_progress(char *kind) @@ -170,12 +172,12 @@ static void list_file_entry(struct file_struct *f) * @post @p buffer contains information about the link or the * referrent as appropriate, if they exist. **/ -int readlink_stat(const char *path, STRUCT_STAT *buffer, char *linkbuf) +static int readlink_stat(const char *path, STRUCT_STAT *buffer, char *linkbuf) { #if SUPPORT_LINKS if (copy_links) return do_stat(path, buffer); - if (link_stat(path, buffer, keep_dirlinks) < 0) + if (link_stat(path, buffer, 0) < 0) return -1; if (S_ISLNK(buffer->st_mode)) { int l = readlink((char *)path, linkbuf, MAXPATHLEN - 1); @@ -315,7 +317,7 @@ void flist_expand(struct file_list *flist) new_ptr = realloc_array(flist->files, struct file_struct *, flist->malloced); - if (verbose >= 2) { + if (verbose >= 2 && flist->malloced != FLIST_START) { rprintf(FINFO, "[%s] expand file_list to %.0f bytes, did%s move\n", who_am_i(), (double)sizeof flist->files[0] * flist->malloced, @@ -333,7 +335,7 @@ void send_file_entry(struct file_struct *file, int f, unsigned short base_flags) unsigned short flags; static time_t modtime; static mode_t mode; - static uint64 dev; + static int64 dev; static dev_t rdev; static uint32 rdev_major; static uid_t uid; @@ -420,11 +422,11 @@ void send_file_entry(struct file_struct *file, int f, unsigned short base_flags) /* We must make sure we don't send a zero flag byte or the * other end will terminate the flist transfer. Note that - * the use of XMIT_TOP_DIR on a non-dir has no meaning, so + * the use of XMIT_DEL_START on a non-dir has no meaning, so * it's harmless way to add a bit to the first flag byte. */ if (protocol_version >= 28) { if (!flags && !S_ISDIR(mode)) - flags |= XMIT_TOP_DIR; + flags |= XMIT_DEL_START; if ((flags & 0xFF00) || !flags) { flags |= XMIT_EXTENDED_FLAGS; write_byte(f, flags); @@ -433,7 +435,7 @@ void send_file_entry(struct file_struct *file, int f, unsigned short base_flags) write_byte(f, flags); } else { if (!(flags & 0xFF) && !S_ISDIR(mode)) - flags |= XMIT_TOP_DIR; + flags |= XMIT_DEL_START; if (!(flags & 0xFF)) flags |= XMIT_LONG_NAME; write_byte(f, flags); @@ -525,13 +527,13 @@ void receive_file_entry(struct file_struct **fptr, unsigned short flags, { static time_t modtime; static mode_t mode; - static uint64 dev; + static int64 dev; static dev_t rdev; static uint32 rdev_major; static uid_t uid; static gid_t gid; static char lastname[MAXPATHLEN], *lastdir; - static int lastdir_len = -1; + static int lastdir_depth, lastdir_len = -1; char thisname[MAXPATHLEN]; unsigned int l1 = 0, l2 = 0; int alloc_len, basename_len, dirname_len, linkname_len, sum_len; @@ -570,10 +572,10 @@ void receive_file_entry(struct file_struct **fptr, unsigned short flags, strlcpy(lastname, thisname, MAXPATHLEN); - clean_fname(thisname); + clean_fname(thisname, 0); if (sanitize_paths) - sanitize_path(thisname, thisname, NULL); + sanitize_path(thisname, thisname, "", 0); if ((basename = strrchr(thisname, '/')) != NULL) { dirname_len = ++basename - thisname; /* counts future '\0' */ @@ -643,7 +645,7 @@ void receive_file_entry(struct file_struct **fptr, unsigned short flags, memset(bp, 0, file_struct_len); bp += file_struct_len; - file->flags = flags & XMIT_TOP_DIR ? FLAG_TOP_DIR : 0; + file->flags = flags & XMIT_DEL_START ? FLAG_DEL_START : 0; file->modtime = modtime; file->length = file_length; file->mode = mode; @@ -656,6 +658,8 @@ void receive_file_entry(struct file_struct **fptr, unsigned short flags, memcpy(bp, dirname, dirname_len - 1); bp += dirname_len; bp[-1] = '\0'; + if (sanitize_paths) + lastdir_depth = count_dir_elements(lastdir); } else if (dirname) file->dirname = dirname; @@ -671,7 +675,7 @@ void receive_file_entry(struct file_struct **fptr, unsigned short flags, file->u.link = bp; read_sbuf(f, bp, linkname_len - 1); if (sanitize_paths) - sanitize_path(bp, bp, lastdir); + sanitize_path(bp, bp, "", lastdir_depth); bp += linkname_len; } #endif @@ -680,7 +684,7 @@ void receive_file_entry(struct file_struct **fptr, unsigned short flags, if (preserve_hard_links && protocol_version < 28 && S_ISREG(mode)) flags |= XMIT_HAS_IDEV_DATA; if (flags & XMIT_HAS_IDEV_DATA) { - uint64 inode; + int64 inode; if (protocol_version < 26) { dev = read_int(f); inode = read_int(f); @@ -759,9 +763,9 @@ struct file_struct *make_file(char *fname, struct file_list *flist, rprintf(FINFO, "skipping overly long name: %s\n", fname); return NULL; } - clean_fname(thisname); + clean_fname(thisname, 0); if (sanitize_paths) - sanitize_path(thisname, thisname, NULL); + sanitize_path(thisname, thisname, "", 0); memset(sum, 0, SUM_LENGTH); @@ -800,7 +804,7 @@ struct file_struct *make_file(char *fname, struct file_list *flist, if (exclude_level == NO_EXCLUDES) goto skip_excludes; - if (S_ISDIR(st.st_mode) && !recurse && !files_from) { + if (S_ISDIR(st.st_mode) && !xfer_dirs) { rprintf(FINFO, "skipping directory %s\n", thisname); return NULL; } @@ -815,8 +819,13 @@ struct file_struct *make_file(char *fname, struct file_list *flist, if (check_exclude_file(thisname, S_ISDIR(st.st_mode) != 0, exclude_level)) return NULL; - if (lp_ignore_nonreadable(module_id) && access(thisname, R_OK) != 0) - return NULL; + if (lp_ignore_nonreadable(module_id)) { +#if SUPPORT_LINKS + if (!S_ISLNK(st.st_mode)) +#endif + if (access(thisname, R_OK) != 0) + return NULL; + } skip_excludes: @@ -923,6 +932,26 @@ skip_excludes: file->basedir = flist_dir; + /* This code is only used by the receiver when it is building + * a list of files for a delete pass. */ + if (keep_dirlinks && linkname_len && flist) { + STRUCT_STAT st2; + int i = flist_find(received_flist, file); + if (i >= 0 && S_ISDIR(received_flist->files[i]->mode) + && do_stat(thisname, &st2) == 0 && S_ISDIR(st2.st_mode)) { + file->modtime = st2.st_mtime; + file->length = st2.st_size; + file->mode = st2.st_mode; + file->uid = st2.st_uid; + file->gid = st2.st_gid; + file->u.link = NULL; + if (file->link_u.idev) { + pool_free(flist->hlink_pool, 0, file->link_u.idev); + file->link_u.idev = NULL; + } + } + } + if (!S_ISDIR(st.st_mode)) stats.total_size += st.st_size; @@ -967,6 +996,8 @@ void send_file_name(int f, struct file_list *flist, char *fname, } +/* Note that the "recurse" value either contains -1, for infinite recursion, + * or a number >= 0 indicating how many levels of recursion we will allow. */ static void send_directory(int f, struct file_list *flist, char *dir) { DIR *d; @@ -1014,9 +1045,10 @@ static void send_directory(int f, struct file_list *flist, char *dir) if (dname[0] == '.' && (dname[1] == '\0' || (dname[1] == '.' && dname[2] == '\0'))) continue; - if (strlcpy(p, dname, MAXPATHLEN - offset) < MAXPATHLEN - offset) - send_file_name(f, flist, fname, recurse, 0); - else { + if (strlcpy(p, dname, MAXPATHLEN - offset) < MAXPATHLEN - offset) { + int do_subdirs = recurse >= 1 ? recurse-- : recurse; + send_file_name(f, flist, fname, do_subdirs, 0); + } else { io_error |= IOERR_GENERAL; rprintf(FINFO, "cannot send long-named file %s\n", @@ -1056,7 +1088,7 @@ struct file_list *send_file_list(int f, int argc, char *argv[]) start_write = stats.total_written; flist = flist_new(f == -1 ? WITHOUT_HLINK : WITH_HLINK, - "send_file_list"); + "send_file_list"); if (f != -1) { io_start_buffering_out(); @@ -1073,21 +1105,22 @@ struct file_list *send_file_list(int f, int argc, char *argv[]) while (1) { char fname2[MAXPATHLEN]; char *fname = fname2; + int do_subdirs; if (use_ff_fd) { if (read_filesfrom_line(filesfrom_fd, fname) == 0) break; - sanitize_path(fname, fname, NULL); + sanitize_path(fname, fname, "", 0); } else { if (argc-- == 0) break; strlcpy(fname, *argv++, MAXPATHLEN); if (sanitize_paths) - sanitize_path(fname, fname, NULL); + sanitize_path(fname, fname, "", 0); } l = strlen(fname); - if (fname[l - 1] == '/') { + if (!l || fname[l - 1] == '/') { if (l == 2 && fname[0] == '.') { /* Turn "./" into just "." rather than "./." */ fname[1] = '\0'; @@ -1096,6 +1129,11 @@ struct file_list *send_file_list(int f, int argc, char *argv[]) fname[l] = '\0'; } } + if (fname[l-1] == '.' && (l == 1 || fname[l-2] == '/')) { + if (!recurse && xfer_dirs) + recurse = 1; /* allow one level */ + } else if (recurse > 0) + recurse = 0; if (link_stat(fname, &st, keep_dirlinks) != 0) { if (f != -1) { @@ -1106,7 +1144,7 @@ struct file_list *send_file_list(int f, int argc, char *argv[]) continue; } - if (S_ISDIR(st.st_mode) && !recurse && !files_from) { + if (S_ISDIR(st.st_mode) && !xfer_dirs) { rprintf(FINFO, "skipping directory %s\n", fname); continue; } @@ -1138,21 +1176,17 @@ struct file_list *send_file_list(int f, int argc, char *argv[]) } *p = '/'; if (fn != p || (*lp && *lp != '/')) { - int copy_links_saved = copy_links; - int recurse_saved = recurse; + int save_copy_links = copy_links; + int save_xfer_dirs = xfer_dirs; copy_links = copy_unsafe_links; - /* set recurse to 1 to prevent make_file - * from ignoring directory, but still - * turn off the recursive parameter to - * send_file_name */ - recurse = 1; + xfer_dirs = 1; while ((slash = strchr(slash+1, '/')) != 0) { *slash = 0; send_file_name(f, flist, fname, 0, 0); *slash = '/'; } - copy_links = copy_links_saved; - recurse = recurse_saved; + copy_links = save_copy_links; + xfer_dirs = save_xfer_dirs; *p = 0; strlcpy(lastpath, fname, sizeof lastpath); *p = '/'; @@ -1187,7 +1221,8 @@ struct file_list *send_file_list(int f, int argc, char *argv[]) if (one_file_system) set_filesystem(fname); - send_file_name(f, flist, fname, recurse, XMIT_TOP_DIR); + do_subdirs = recurse >= 1 ? recurse-- : recurse; + send_file_name(f, flist, fname, do_subdirs, XMIT_DEL_START); if (olddir[0]) { flist_dir = NULL; @@ -1228,7 +1263,7 @@ struct file_list *send_file_list(int f, int argc, char *argv[]) } if (verbose > 3) - output_flist(flist); + output_flist(flist, f < 0 ? "delete" : who_am_i()); if (verbose > 2) rprintf(FINFO, "send_file_list done\n"); @@ -1249,6 +1284,7 @@ struct file_list *recv_file_list(int f) start_read = stats.total_read; flist = flist_new(WITH_HLINK, "recv_file_list"); + received_flist = flist; flist->count = 0; flist->malloced = 1000; @@ -1301,7 +1337,7 @@ struct file_list *recv_file_list(int f) } if (verbose > 3) - output_flist(flist); + output_flist(flist, who_am_i()); if (list_only) { int i; @@ -1449,8 +1485,8 @@ static void clean_flist(struct file_list *flist, int strip_root, int no_dups) /* Make sure that if we unduplicate '.', that we don't * lose track of a user-specified starting point (or * else deletions will mysteriously fail with -R). */ - if (flist->files[i]->flags & FLAG_TOP_DIR) - flist->files[prev_i]->flags |= FLAG_TOP_DIR; + if (flist->files[i]->flags & FLAG_DEL_START) + flist->files[prev_i]->flags |= FLAG_DEL_START; clear_file(i, flist); } else @@ -1477,7 +1513,7 @@ static void clean_flist(struct file_list *flist, int strip_root, int no_dups) } } -static void output_flist(struct file_list *flist) +static void output_flist(struct file_list *flist, const char *whose_list) { char uidbuf[16], gidbuf[16]; struct file_struct *file; @@ -1485,7 +1521,7 @@ static void output_flist(struct file_list *flist) for (i = 0; i < flist->count; i++) { file = flist->files[i]; - if (am_root && preserve_uid) + if ((am_root || am_sender) && preserve_uid) sprintf(uidbuf, " uid=%ld", (long)file->uid); else *uidbuf = '\0'; @@ -1494,7 +1530,7 @@ static void output_flist(struct file_list *flist) else *gidbuf = '\0'; rprintf(FINFO, "[%s] i=%d %s %s %s mode=0%o len=%.0f%s%s\n", - who_am_i(), i, NS(file->basedir), NS(file->dirname), + whose_list, i, NS(file->basedir), NS(file->dirname), NS(file->basename), (int)file->mode, (double)file->length, uidbuf, gidbuf); }