X-Git-Url: https://mattmccutchen.net/rsync/rsync.git/blobdiff_plain/5459e693a31e67bd68f6ef58d89ada67ddee9670..2f188c8d96c8a0c887fb73bf3288168825af0401:/flist.c diff --git a/flist.c b/flist.c index 7f165a9c..c7607f03 100644 --- a/flist.c +++ b/flist.c @@ -7,8 +7,9 @@ * Copyright (C) 2002-2007 Wayne Davison * * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 3 as - * published by the Free Software Foundation. + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -295,7 +296,7 @@ static void flist_expand(struct file_list *flist, int extra) flist->malloced); if (verbose >= 2 && flist->malloced != FLIST_START) { - rprintf(FCLIENT, "[%s] expand file_list to %.0f bytes, did%s move\n", + rprintf(FCLIENT, "[%s] expand file_list pointer array to %.0f bytes, did%s move\n", who_am_i(), (double)sizeof flist->files[0] * flist->malloced, (new_ptr == flist->files) ? " not" : ""); @@ -357,7 +358,7 @@ static void send_file_entry(int f, struct file_struct *file, int ndx) char fname[MAXPATHLEN]; int first_hlink_ndx = -1; int l1, l2; - int flags; + int xflags; #ifdef ICONV_OPTION if (ic_send != (iconv_t)-1) { @@ -389,56 +390,60 @@ static void send_file_entry(int f, struct file_struct *file, int ndx) #endif f_name(file, fname); - flags = file->flags & FLAG_TOP_DIR; /* FLAG_TOP_DIR == XMIT_TOP_DIR */ + xflags = file->flags & FLAG_TOP_DIR; /* FLAG_TOP_DIR == XMIT_TOP_DIR */ if (file->mode == mode) - flags |= XMIT_SAME_MODE; + xflags |= XMIT_SAME_MODE; else mode = file->mode; + + if (protocol_version >= 30 && S_ISDIR(mode) && !(file->flags & FLAG_XFER_DIR)) + xflags |= XMIT_NON_XFER_DIR; + if ((preserve_devices && IS_DEVICE(mode)) || (preserve_specials && IS_SPECIAL(mode))) { if (protocol_version < 28) { if (tmp_rdev == rdev) - flags |= XMIT_SAME_RDEV_pre28; + xflags |= XMIT_SAME_RDEV_pre28; else rdev = tmp_rdev; } else { rdev = tmp_rdev; if ((uint32)major(rdev) == rdev_major) - flags |= XMIT_SAME_RDEV_MAJOR; + xflags |= XMIT_SAME_RDEV_MAJOR; else rdev_major = major(rdev); if (protocol_version < 30 && (uint32)minor(rdev) <= 0xFFu) - flags |= XMIT_RDEV_MINOR_8_pre30; + xflags |= XMIT_RDEV_MINOR_8_pre30; } } else if (protocol_version < 28) rdev = MAKEDEV(0, 0); if (uid_ndx) { if ((uid_t)F_OWNER(file) == uid && *lastname) - flags |= XMIT_SAME_UID; + xflags |= XMIT_SAME_UID; else { uid = F_OWNER(file); if (uid_ndx && !numeric_ids) { user_name = add_uid(uid); if (inc_recurse && user_name) - flags |= XMIT_USER_NAME_FOLLOWS; + xflags |= XMIT_USER_NAME_FOLLOWS; } } } if (gid_ndx) { if ((gid_t)F_GROUP(file) == gid && *lastname) - flags |= XMIT_SAME_GID; + xflags |= XMIT_SAME_GID; else { gid = F_GROUP(file); if (gid_ndx && !numeric_ids) { group_name = add_gid(gid); if (inc_recurse && group_name) - flags |= XMIT_GROUP_NAME_FOLLOWS; + xflags |= XMIT_GROUP_NAME_FOLLOWS; } } } if (file->modtime == modtime) - flags |= XMIT_SAME_TIME; + xflags |= XMIT_SAME_TIME; else modtime = file->modtime; @@ -449,16 +454,16 @@ static void send_file_entry(int f, struct file_struct *file, int ndx) first_hlink_ndx = (int32)(long)np->data - 1; if (first_hlink_ndx < 0) { np->data = (void*)(long)(ndx + 1); - flags |= XMIT_HLINK_FIRST; + xflags |= XMIT_HLINK_FIRST; } - flags |= XMIT_HLINKED; + xflags |= XMIT_HLINKED; } else { if (tmp_dev == dev) { if (protocol_version >= 28) - flags |= XMIT_SAME_DEV_pre30; + xflags |= XMIT_SAME_DEV_pre30; } else dev = tmp_dev; - flags |= XMIT_HLINKED; + xflags |= XMIT_HLINKED; } } #endif @@ -469,30 +474,30 @@ static void send_file_entry(int f, struct file_struct *file, int ndx) l2 = strlen(fname+l1); if (l1 > 0) - flags |= XMIT_SAME_NAME; + xflags |= XMIT_SAME_NAME; if (l2 > 255) - flags |= XMIT_LONG_NAME; + xflags |= XMIT_LONG_NAME; /* 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 * 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; - if ((flags & 0xFF00) || !flags) { - flags |= XMIT_EXTENDED_FLAGS; - write_shortint(f, flags); + if (!xflags && !S_ISDIR(mode)) + xflags |= XMIT_TOP_DIR; + if ((xflags & 0xFF00) || !xflags) { + xflags |= XMIT_EXTENDED_FLAGS; + write_shortint(f, xflags); } else - write_byte(f, flags); + write_byte(f, xflags); } else { - if (!(flags & 0xFF)) - flags |= S_ISDIR(mode) ? XMIT_LONG_NAME : XMIT_TOP_DIR; - write_byte(f, flags); + if (!(xflags & 0xFF)) + xflags |= S_ISDIR(mode) ? XMIT_LONG_NAME : XMIT_TOP_DIR; + write_byte(f, xflags); } - if (flags & XMIT_SAME_NAME) + if (xflags & XMIT_SAME_NAME) write_byte(f, l1); - if (flags & XMIT_LONG_NAME) + if (xflags & XMIT_LONG_NAME) write_varint30(f, l2); else write_byte(f, l2); @@ -504,32 +509,32 @@ static void send_file_entry(int f, struct file_struct *file, int ndx) } write_varlong30(f, F_LENGTH(file), 3); - if (!(flags & XMIT_SAME_TIME)) { + if (!(xflags & XMIT_SAME_TIME)) { if (protocol_version >= 30) write_varlong(f, modtime, 4); else write_int(f, modtime); } - if (!(flags & XMIT_SAME_MODE)) + if (!(xflags & XMIT_SAME_MODE)) write_int(f, to_wire_mode(mode)); - if (uid_ndx && !(flags & XMIT_SAME_UID)) { + if (uid_ndx && !(xflags & XMIT_SAME_UID)) { if (protocol_version < 30) write_int(f, uid); else { write_varint(f, uid); - if (flags & XMIT_USER_NAME_FOLLOWS) { + if (xflags & XMIT_USER_NAME_FOLLOWS) { int len = strlen(user_name); write_byte(f, len); write_buf(f, user_name, len); } } } - if (gid_ndx && !(flags & XMIT_SAME_GID)) { + if (gid_ndx && !(xflags & XMIT_SAME_GID)) { if (protocol_version < 30) write_int(f, gid); else { write_varint(f, gid); - if (flags & XMIT_GROUP_NAME_FOLLOWS) { + if (xflags & XMIT_GROUP_NAME_FOLLOWS) { int len = strlen(group_name); write_byte(f, len); write_buf(f, group_name, len); @@ -539,14 +544,14 @@ static void send_file_entry(int f, struct file_struct *file, int ndx) if ((preserve_devices && IS_DEVICE(mode)) || (preserve_specials && IS_SPECIAL(mode))) { if (protocol_version < 28) { - if (!(flags & XMIT_SAME_RDEV_pre28)) + if (!(xflags & XMIT_SAME_RDEV_pre28)) write_int(f, (int)rdev); } else { - if (!(flags & XMIT_SAME_RDEV_MAJOR)) + if (!(xflags & XMIT_SAME_RDEV_MAJOR)) write_varint30(f, major(rdev)); if (protocol_version >= 30) write_varint(f, minor(rdev)); - else if (flags & XMIT_RDEV_MINOR_8_pre30) + else if (xflags & XMIT_RDEV_MINOR_8_pre30) write_byte(f, minor(rdev)); else write_int(f, minor(rdev)); @@ -570,7 +575,7 @@ static void send_file_entry(int f, struct file_struct *file, int ndx) write_int(f, (int32)tmp_ino); } else { /* 64-bit dev_t and ino_t */ - if (!(flags & XMIT_SAME_DEV_pre30)) + if (!(xflags & XMIT_SAME_DEV_pre30)) write_longint(f, dev); write_longint(f, tmp_ino); } @@ -708,7 +713,7 @@ static struct file_struct *recv_file_entry(struct file_list *flist, || (preserve_specials && IS_SPECIAL(mode))) { uint32 *devp = F_RDEV_P(first); rdev = MAKEDEV(DEV_MAJOR(devp), DEV_MINOR(devp)); - extra_len += 2 * EXTRA_LEN; + extra_len += DEV_EXTRA_CNT * EXTRA_LEN; } if (preserve_links && S_ISLNK(mode)) linkname_len = strlen(F_SYMLINK(first)) + 1; @@ -779,7 +784,7 @@ static struct file_struct *recv_file_entry(struct file_list *flist, rdev_minor = read_int(f); rdev = MAKEDEV(rdev_major, rdev_minor); } - extra_len += 2 * EXTRA_LEN; + extra_len += DEV_EXTRA_CNT * EXTRA_LEN; file_length = 0; } else if (protocol_version < 28) rdev = MAKEDEV(0, 0); @@ -827,7 +832,7 @@ static struct file_struct *recv_file_entry(struct file_list *flist, if (inc_recurse && S_ISDIR(mode)) { if (one_file_system) { /* Room to save the dir's device for -x */ - extra_len += 2 * EXTRA_LEN; + extra_len += DEV_EXTRA_CNT * EXTRA_LEN; } pool = dir_flist->file_pool; } else @@ -884,6 +889,9 @@ static struct file_struct *recv_file_entry(struct file_list *flist, && lastname[del_hier_name_len-2] == '/') del_hier_name_len -= 2; file->flags |= FLAG_TOP_DIR | FLAG_XFER_DIR; + } else if (protocol_version >= 30) { + if (!(xflags & XMIT_NON_XFER_DIR)) + file->flags |= FLAG_XFER_DIR; } else if (in_del_hier) { if (!relative_paths || !del_hier_name_len || (l1 >= del_hier_name_len @@ -945,7 +953,7 @@ static struct file_struct *recv_file_entry(struct file_list *flist, if (always_checksum && (S_ISREG(mode) || protocol_version < 28)) { if (S_ISREG(mode)) - bp = (char*)F_SUM(file); + bp = F_SUM(file); else { /* Prior to 28, we get a useless set of nulls. */ bp = tmp_sum; @@ -972,34 +980,24 @@ static struct file_struct *recv_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 - * options. +/* Create a file_struct for a named file by reading its stat() information + * and performing extensive checks against global options. * - * @return the new file, or NULL if there was an error or this file - * should be excluded. - * - * @todo There is a small optimization opportunity here to avoid - * stat()ing the file in some circumstances, which has a certain cost. - * We are called immediately after doing readdir(), and so we may - * already know the d_type of the file. We could for example avoid - * statting directories if we're not recursing, but this is not a very - * important case. Some systems may not have d_type. - **/ + * Returns a pointer to the new file struct, or NULL if there was an error + * or this file should be excluded. */ struct file_struct *make_file(const char *fname, struct file_list *flist, STRUCT_STAT *stp, int flags, int filter_level) { static char *lastdir; static int lastdir_len = -1; struct file_struct *file; - STRUCT_STAT st; char thisname[MAXPATHLEN]; char linkname[MAXPATHLEN]; int alloc_len, basename_len, linkname_len; int extra_len = file_extra_cnt * EXTRA_LEN; const char *basename; alloc_pool_t *pool; + STRUCT_STAT st; char *bp; if (strlcpy(thisname, fname, sizeof thisname) >= sizeof thisname) { @@ -1052,10 +1050,13 @@ struct file_struct *make_file(const char *fname, struct file_list *flist, if (filter_level == NO_FILTERS) goto skip_filters; - if (S_ISDIR(st.st_mode) && !xfer_dirs) { - rprintf(FINFO, "skipping directory %s\n", thisname); - return NULL; - } + if (S_ISDIR(st.st_mode)) { + if (!xfer_dirs) { + rprintf(FINFO, "skipping directory %s\n", thisname); + return NULL; + } + } else + flags &= ~FLAG_XFER_DIR; /* -x only affects directories because we need to avoid recursing * into a mount-point directory, not to avoid copying a symlinked @@ -1093,7 +1094,7 @@ struct file_struct *make_file(const char *fname, struct file_list *flist, if (flist->prev && S_ISDIR(st.st_mode) && flags & FLAG_DIVERT_DIRS) { /* Room for parent/sibling/next-child info. */ - extra_len += 3 * EXTRA_LEN; + extra_len += DIRNODE_EXTRA_CNT * EXTRA_LEN; dir_count++; pool = dir_flist->file_pool; } else @@ -1239,14 +1240,14 @@ void unmake_file(struct file_struct *file) static struct file_struct *send_file_name(int f, struct file_list *flist, char *fname, STRUCT_STAT *stp, - int flags, int filter_flags) + int flags, int filter_level) { struct file_struct *file; #if defined SUPPORT_ACLS || defined SUPPORT_XATTRS statx sx; #endif - file = make_file(fname, flist, stp, flags, filter_flags); + file = make_file(fname, flist, stp, flags, filter_level); if (!file) return NULL; @@ -1393,7 +1394,7 @@ static void add_dirs_to_tree(int parent_ndx, struct file_list *from_flist, int i; int32 *dp = NULL; int32 *parent_dp = parent_ndx < 0 ? NULL - : F_DIRNODE_P(dir_flist->sorted[parent_ndx]); + : F_DIR_NODE_P(dir_flist->sorted[parent_ndx]); flist_expand(dir_flist, dir_cnt); dir_flist->sorted = dir_flist->files; @@ -1418,7 +1419,7 @@ static void add_dirs_to_tree(int parent_ndx, struct file_list *from_flist, else send_dir_ndx = dir_flist->used - 1; - dp = F_DIRNODE_P(file); + dp = F_DIR_NODE_P(file); DIR_PARENT(dp) = parent_ndx; DIR_FIRST_CHILD(dp) = -1; } @@ -1440,7 +1441,7 @@ static void send_directory(int f, struct file_list *flist, char *fbuf, int len, DIR *d; int divert_dirs = (flags & FLAG_DIVERT_DIRS) != 0; int start = flist->used; - int filter_flags = f == -2 ? SERVER_FILTERS : ALL_FILTERS; + int filter_level = f == -2 ? SERVER_FILTERS : ALL_FILTERS; assert(flist != NULL); @@ -1468,8 +1469,15 @@ static void send_directory(int f, struct file_list *flist, char *fbuf, int len, full_fname(fbuf)); continue; } + if (dname[0] == '\0') { + io_error |= IOERR_GENERAL; + rprintf(FINFO, + "cannot send file with empty name in %s\n", + full_fname(fbuf)); + continue; + } - send_file_name(f, flist, fbuf, NULL, flags, filter_flags); + send_file_name(f, flist, fbuf, NULL, flags, filter_level); } fbuf[len] = '\0'; @@ -1541,7 +1549,7 @@ void send_extra_file_list(int f, int at_least) flist->parent_ndx = dir_ndx; send1extra(f, file, flist); - dp = F_DIRNODE_P(file); + dp = F_DIR_NODE_P(file); /* If there are any duplicate directory names that follow, we * send all the dirs together in one file-list. The dir_flist @@ -1551,7 +1559,7 @@ void send_extra_file_list(int f, int at_least) send_dir_ndx = dir_ndx; file = dir_flist->sorted[dir_ndx]; send1extra(f, file, flist); - dp = F_DIRNODE_P(file); + dp = F_DIR_NODE_P(file); } write_byte(f, 0); @@ -1594,7 +1602,7 @@ void send_extra_file_list(int f, int at_least) } send_dir_depth--; file = dir_flist->sorted[send_dir_ndx]; - dp = F_DIRNODE_P(file); + dp = F_DIR_NODE_P(file); } send_dir_ndx = DIR_NEXT_SIBLING(dp); } @@ -1636,10 +1644,10 @@ struct file_list *send_file_list(int f, int argc, char *argv[]) flist = cur_flist = flist_new(0, "send_file_list"); if (inc_recurse) { dir_flist = flist_new(FLIST_TEMP, "send_file_list"); - flags = FLAG_DIVERT_DIRS; + flags = FLAG_DIVERT_DIRS | FLAG_XFER_DIR; } else { dir_flist = cur_flist; - flags = 0; + flags = FLAG_XFER_DIR; } disable_buffering = io_start_buffering_out(f); @@ -1805,7 +1813,7 @@ struct file_list *send_file_list(int f, int argc, char *argv[]) if (fn != p || (*lp && *lp != '/')) { int save_copy_links = copy_links; int save_xfer_dirs = xfer_dirs; - int dir_flags = inc_recurse ? FLAG_DIVERT_DIRS : 0; + int dir_flags = flags & ~FLAG_XFER_DIR; copy_links |= copy_unsafe_links; xfer_dirs = 1; while ((slash = strchr(slash+1, '/')) != 0) { @@ -1827,7 +1835,7 @@ struct file_list *send_file_list(int f, int argc, char *argv[]) if (recurse || (xfer_dirs && is_dot_dir)) { struct file_struct *file; - int top_flags = FLAG_TOP_DIR | FLAG_XFER_DIR | flags; + int top_flags = FLAG_TOP_DIR | flags; file = send_file_name(f, flist, fbuf, &st, top_flags, ALL_FILTERS); if (file && !inc_recurse) @@ -2448,6 +2456,8 @@ static void output_flist(struct file_list *flist) enum fnc_state { s_DIR, s_SLASH, s_BASE, s_TRAILING }; enum fnc_type { t_PATH, t_ITEM }; +static int found_prefix; + /* Compare the names of two file_struct entities, similar to how strcmp() * would do if it were operating on the joined strings. * @@ -2463,7 +2473,7 @@ enum fnc_type { t_PATH, t_ITEM }; * cannot (and never is in the current codebase). The basename component * may be NULL (for a removed item), in which case it is considered to be * after any existing item. */ -int f_name_cmp(struct file_struct *f1, struct file_struct *f2) +int f_name_cmp(const struct file_struct *f1, const struct file_struct *f2) { int dif; const uchar *c1, *c2; @@ -2568,6 +2578,7 @@ int f_name_cmp(struct file_struct *f1, struct file_struct *f2) } /* FALL THROUGH */ case s_TRAILING: + found_prefix = 1; if (!*c1) return 0; type2 = t_ITEM; @@ -2581,6 +2592,16 @@ int f_name_cmp(struct file_struct *f1, struct file_struct *f2) return dif; } +/* Returns 1 if f1's filename has all of f2's filename as a prefix. This does + * not match if f2's basename is not an exact match of a path element in f1. + * E.g. /path/foo is not a prefix of /path/foobar/baz, but /path/foobar is. */ +int f_name_has_prefix(const struct file_struct *f1, const struct file_struct *f2) +{ + found_prefix = 0; + f_name_cmp(f1, f2); + return found_prefix; +} + char *f_name_buf(void) { static char names[5][MAXPATHLEN]; @@ -2595,7 +2616,7 @@ char *f_name_buf(void) * 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(struct file_struct *f, char *fbuf) +char *f_name(const struct file_struct *f, char *fbuf) { if (!f || !F_IS_ACTIVE(f)) return NULL;