X-Git-Url: https://mattmccutchen.net/rsync/rsync.git/blobdiff_plain/ebec5eb689e9b01509b6507d23478548cbc117e8..9c000f5e7bec6ddd1f488e2e2290fbf946a8de09:/flist.c diff --git a/flist.c b/flist.c index 61a6360d..f66a4e20 100644 --- a/flist.c +++ b/flist.c @@ -48,10 +48,12 @@ extern int preserve_links; extern int preserve_hard_links; extern int preserve_perms; extern int preserve_devices; +extern int preserve_specials; extern int preserve_uid; extern int preserve_gid; extern int relative_paths; extern int implied_dirs; +extern int skip_empty_dirs; extern int copy_links; extern int copy_unsafe_links; extern int protocol_version; @@ -69,10 +71,12 @@ 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 int max_dir_depth = 0; static void clean_flist(struct file_list *flist, int strip_root, int no_dups); static void output_flist(struct file_list *flist); @@ -342,16 +346,14 @@ static void send_file_entry(struct file_struct *file, int f) flags |= XMIT_SAME_MODE; else mode = file->mode; - if (preserve_devices) { + if ((preserve_devices && IS_DEVICE(mode)) + || (preserve_specials && IS_SPECIAL(mode))) { if (protocol_version < 28) { - if (IS_DEVICE(mode)) { - if (file->u.rdev == rdev) - flags |= XMIT_SAME_RDEV_pre28; - else - rdev = file->u.rdev; - } else - rdev = makedev(0, 0); - } else if (IS_DEVICE(mode)) { + if (file->u.rdev == rdev) + flags |= XMIT_SAME_RDEV_pre28; + else + rdev = file->u.rdev; + } else { rdev = file->u.rdev; if ((uint32)major(rdev) == rdev_major) flags |= XMIT_SAME_RDEV_MAJOR; @@ -360,7 +362,8 @@ static void send_file_entry(struct file_struct *file, int f) if ((uint32)minor(rdev) <= 0xFFu) flags |= XMIT_RDEV_MINOR_IS_SMALL; } - } + } else if (protocol_version < 28) + rdev = makedev(0, 0); if (file->uid == uid) flags |= XMIT_SAME_UID; else @@ -436,7 +439,8 @@ static void send_file_entry(struct file_struct *file, int f) add_gid(gid); write_int(f, gid); } - if (preserve_devices && IS_DEVICE(mode)) { + if ((preserve_devices && IS_DEVICE(mode)) + || (preserve_specials && IS_SPECIAL(mode))) { if (protocol_version < 28) { if (!(flags & XMIT_SAME_RDEV_pre28)) write_int(f, (int)rdev); @@ -576,14 +580,12 @@ static struct file_struct *receive_file_entry(struct file_list *flist, if (preserve_gid && !(flags & XMIT_SAME_GID)) gid = (gid_t)read_int(f); - if (preserve_devices) { + if ((preserve_devices && IS_DEVICE(mode)) + || (preserve_specials && IS_SPECIAL(mode))) { if (protocol_version < 28) { - if (IS_DEVICE(mode)) { - if (!(flags & XMIT_SAME_RDEV_pre28)) - rdev = (dev_t)read_int(f); - } else - rdev = makedev(0, 0); - } else if (IS_DEVICE(mode)) { + if (!(flags & XMIT_SAME_RDEV_pre28)) + rdev = (dev_t)read_int(f); + } else { uint32 rdev_minor; if (!(flags & XMIT_SAME_RDEV_MAJOR)) rdev_major = read_int(f); @@ -593,7 +595,8 @@ static struct file_struct *receive_file_entry(struct file_list *flist, rdev_minor = read_int(f); rdev = makedev(rdev_major, rdev_minor); } - } + } else if (protocol_version < 28) + rdev = makedev(0, 0); #ifdef SUPPORT_LINKS if (preserve_links && S_ISLNK(mode)) { @@ -618,7 +621,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; @@ -633,6 +635,8 @@ static struct file_struct *receive_file_entry(struct file_list *flist, bp[-1] = '\0'; lastdir_depth = count_dir_elements(lastdir); file->dir.depth = lastdir_depth + 1; + if (lastdir_depth >= max_dir_depth) + max_dir_depth = lastdir_depth + 1; } else if (dirname) { file->dirname = dirname; /* we're reusing lastname */ file->dir.depth = lastdir_depth + 1; @@ -646,13 +650,14 @@ static struct file_struct *receive_file_entry(struct file_list *flist, in_del_hier = recurse; del_hier_name_len = file->dir.depth == 0 ? 0 : l1 + l2; if (relative_paths && del_hier_name_len > 2 - && basename_len == 1+1 && *basename == '.') + && 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; @@ -663,7 +668,8 @@ static struct file_struct *receive_file_entry(struct file_list *flist, memcpy(bp, basename, basename_len); bp += basename_len; - if (preserve_devices && IS_DEVICE(mode)) + if ((preserve_devices && IS_DEVICE(mode)) + || (preserve_specials && IS_SPECIAL(mode))) file->u.rdev = rdev; #ifdef SUPPORT_LINKS @@ -807,24 +813,16 @@ struct file_struct *make_file(char *fname, struct file_list *flist, /* We only care about directories 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 && S_ISDIR(st.st_mode) && !(flags & FLAG_TOP_DIR)) { - STRUCT_STAT st2; - unsigned int len = strlcat(thisname, "/..", sizeof thisname); - /* If the directory's .. dir is on a different filesystem, - * either mark this dir as a mount-point or skip it. */ - if (len < sizeof thisname && do_stat(thisname, &st2) == 0 - && (st.st_dev != st2.st_dev || st.st_ino != st2.st_ino)) { - if (one_file_system > 1) { - if (verbose > 2) { - rprintf(FINFO, - "skipping mount-point dir %s\n", - thisname); - } - return NULL; + if (one_file_system && st.st_dev != filesystem_dev + && S_ISDIR(st.st_mode)) { + if (one_file_system > 1) { + if (verbose > 2) { + rprintf(FINFO, "skipping mount-point dir %s\n", + thisname); } - flags |= FLAG_MOUNT_POINT; + return NULL; } - thisname[len-3] = '\0'; + flags |= FLAG_MOUNT_POINT; } if (is_excluded(thisname, S_ISDIR(st.st_mode) != 0, filter_level)) @@ -923,7 +921,8 @@ struct file_struct *make_file(char *fname, struct file_list *flist, bp += basename_len; #ifdef HAVE_STRUCT_STAT_ST_RDEV - if (preserve_devices && IS_DEVICE(st.st_mode)) + if ((preserve_devices && IS_DEVICE(st.st_mode)) + || (preserve_specials && IS_SPECIAL(st.st_mode))) file->u.rdev = st.st_rdev; #endif @@ -1281,6 +1280,9 @@ struct file_list *send_file_list(int f, int argc, char *argv[]) } } + if (one_file_system) + filesystem_dev = st.st_dev; + if (recurse || (xfer_dirs && is_dot_dir)) { struct file_struct *file; file = send_file_name(f, flist, fbuf, &st, FLAG_TOP_DIR); @@ -1327,8 +1329,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 */ @@ -1398,8 +1398,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 */ @@ -1546,6 +1544,20 @@ static void clean_flist(struct file_list *flist, int strip_root, int no_dups) if (!file->basename) continue; + + if (strip_root && file->dirname) { + /* We need to strip off the leading slashes for + * relative paths, but this must be done _after_ + * the sorting phase (above). */ + if (*file->dirname == '/') { + char *s = file->dirname + 1; + while (*s == '/') s++; + memmove(file->dirname, s, strlen(s) + 1); + } + if (!*file->dirname) + file->dirname = NULL; + } + if (f_name_cmp(file, flist->files[prev_i]) == 0) j = prev_i; else if (protocol_version >= 29 && S_ISDIR(file->mode)) { @@ -1558,6 +1570,7 @@ static void clean_flist(struct file_list *flist, int strip_root, int no_dups) file->mode = save_mode; } else j = -1; + if (j >= 0) { struct file_struct *fp = flist->files[j]; int keep, drop; @@ -1576,8 +1589,8 @@ static void clean_flist(struct file_list *flist, int strip_root, int no_dups) "removing duplicate name %s from file list (%d)\n", f_name(file, NULL), drop); } - /* Make sure that if we unduplicate '.', that we don't - * lose track of a user-specified top directory. */ + /* Make sure we don't lose track of a user-specified + * top directory. */ flist->files[keep]->flags |= flist->files[drop]->flags & (FLAG_TOP_DIR|FLAG_DEL_HERE); @@ -1597,23 +1610,45 @@ static void clean_flist(struct file_list *flist, int strip_root, int no_dups) } flist->high = no_dups ? prev_i : flist->count - 1; - if (strip_root) { - /* We need to strip off the leading slashes for relative - * paths, but this must be done _after_ the sorting phase. */ + if (skip_empty_dirs && no_dups && max_dir_depth) { + int j, cur_depth = 0; + int *maybe_dirs = new_array(int, max_dir_depth); + + maybe_dirs[0] = -1; + for (i = flist->low; i <= flist->high; i++) { struct file_struct *file = flist->files[i]; - if (!file->dirname) - continue; - if (*file->dirname == '/') { - char *s = file->dirname + 1; - while (*s == '/') s++; - memmove(file->dirname, s, strlen(s) + 1); + if (S_ISDIR(file->mode) && file->dir.depth) { + j = cur_depth; + cur_depth = file->dir.depth - 1; + for ( ; j >= cur_depth; j--) { + if (maybe_dirs[j] < 0) + continue; + clear_file(maybe_dirs[j], flist); + } + maybe_dirs[cur_depth] = i; + } else if (maybe_dirs[cur_depth] >= 0) { + for (j = 0; j <= cur_depth; j++) + maybe_dirs[j] = -1; } + } + for (j = cur_depth; j >= 0; j--) { + if (maybe_dirs[j] < 0) + continue; + clear_file(maybe_dirs[j], flist); + } - if (!*file->dirname) - file->dirname = NULL; + for (i = flist->low; i <= flist->high; i++) { + if (flist->files[i]->basename) + break; + } + flist->low = i; + for (i = flist->high; i >= flist->low; i--) { + if (flist->files[i]->basename) + break; } + flist->high = i; } }