From 3932c423732eefb0ba9035c6467e757e8a484728 Mon Sep 17 00:00:00 2001 From: Wayne Davison Date: Sat, 22 Sep 2007 21:43:11 +0000 Subject: [PATCH] Using relative with incremental recursion now results in a normal sequence of directories in the hierarchy, and works properly when merging things from multiple sources. --- flist.c | 187 ++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 149 insertions(+), 38 deletions(-) diff --git a/flist.c b/flist.c index 42518d9a..32224294 100644 --- a/flist.c +++ b/flist.c @@ -1077,16 +1077,19 @@ struct file_struct *make_file(const char *fname, struct file_list *flist, /* -x only affects 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 && 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); + if (one_file_system && S_ISDIR(st.st_mode)) { + if (flags & FLAG_TOP_DIR) + filesystem_dev = st.st_dev; + else if (st.st_dev != filesystem_dev) { + if (one_file_system > 1) { + if (verbose > 2) { + rprintf(FINFO, "skipping mount-point dir %s\n", + thisname); + } + return NULL; } - return NULL; + flags |= FLAG_MOUNT_DIR; } - flags |= FLAG_MOUNT_DIR; } if (is_excluded(thisname, S_ISDIR(st.st_mode) != 0, filter_level)) { @@ -1111,6 +1114,8 @@ struct file_struct *make_file(const char *fname, struct file_list *flist, && flags & FLAG_DIVERT_DIRS) { /* Room for parent/sibling/next-child info. */ extra_len += DIRNODE_EXTRA_CNT * EXTRA_LEN; + if (relative_paths) + extra_len += PTR_EXTRA_CNT * EXTRA_LEN; dir_count++; pool = dir_flist->file_pool; } else @@ -1424,8 +1429,7 @@ 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->flags & FLAG_XFER_DIR) - || file->flags & FLAG_MOUNT_DIR) + if (file->flags & FLAG_MOUNT_DIR) continue; if (dp) @@ -1513,10 +1517,89 @@ static void send_directory(int f, struct file_list *flist, char *fbuf, int len, } } +static char lastpath[MAXPATHLEN] = ""; +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) +{ + struct file_struct *file; + item_list *rel_list; + char **ep, *slash; + int len, need_new_dir; + + flags &= ~FLAG_XFER_DIR; + + if (inc_recurse) { + if (lastpath_struct && F_PATHNAME(lastpath_struct) == pathname + && lastpath_len == limit - fname + && strncmp(lastpath, fname, lastpath_len) == 0) + need_new_dir = 0; + else + need_new_dir = 1; + } else + need_new_dir = 1; + + if (need_new_dir) { + int save_copy_links = copy_links; + int save_xfer_dirs = xfer_dirs; + + copy_links |= copy_unsafe_links; + xfer_dirs = 1; + + *limit = '\0'; + + for (slash = start; (slash = strchr(slash+1, '/')) != NULL; ) { + *slash = '\0'; + send_file_name(f, flist, fname, NULL, flags, ALL_FILTERS); + *slash = '/'; + } + + file = send_file_name(f, flist, fname, NULL, flags, ALL_FILTERS); + if (inc_recurse) { + if (file && !S_ISDIR(file->mode)) + file = NULL; + else if (file) + memset(F_DIR_RELS_P(file), 0, sizeof (item_list*)); + lastpath_struct = file; + } + + strlcpy(lastpath, fname, sizeof lastpath); + lastpath_len = limit - fname; + + *limit = '/'; + + copy_links = save_copy_links; + xfer_dirs = save_xfer_dirs; + + if (!inc_recurse) + return; + } + + if (!lastpath_struct) + return; /* dir must have vanished */ + + len = strlen(limit+1); + memcpy(&rel_list, F_DIR_RELS_P(lastpath_struct), sizeof rel_list); + if (!rel_list) { + if (!(rel_list = new0(item_list))) + out_of_memory("send_implied_dirs"); + memcpy(F_DIR_RELS_P(lastpath_struct), &rel_list, sizeof rel_list); + } + ep = EXPAND_ITEM_LIST(rel_list, char *, 32); + if (!(*ep = new_array(char, 1 + len + 1))) + out_of_memory("send_implied_dirs"); + **ep = is_dot_dir; + strlcpy(*ep + 1, limit+1, len + 1); +} + static void send1extra(int f, struct file_struct *file, struct file_list *flist) { char fbuf[MAXPATHLEN]; - int dlen; + item_list *rel_list; + int len, dlen, flags = FLAG_DIVERT_DIRS | FLAG_XFER_DIR; + size_t j; f_name(file, fbuf); dlen = strlen(fbuf); @@ -1528,7 +1611,51 @@ static void send1extra(int f, struct file_struct *file, struct file_list *flist) change_local_filter_dir(fbuf, dlen, send_dir_depth); - send_directory(f, flist, fbuf, dlen, FLAG_DIVERT_DIRS | FLAG_XFER_DIR); + if (file->flags & FLAG_XFER_DIR) + send_directory(f, flist, fbuf, dlen, flags); + + if (!relative_paths) + return; + + memcpy(&rel_list, F_DIR_RELS_P(file), sizeof rel_list); + if (!rel_list) + return; + + for (j = 0; j < rel_list->count; j++) { + char *slash, *ep = ((char**)rel_list->items)[j]; + int is_dot_dir = *ep; + + fbuf[dlen] = '/'; + len = strlcpy(fbuf + dlen + 1, ep+1, sizeof fbuf - dlen - 1); + free(ep); + if (len >= (int)sizeof fbuf) + continue; /* Impossible... */ + + slash = strchr(fbuf+dlen+1, '/'); + if (slash) { + send_implied_dirs(f, flist, fbuf, fbuf+dlen+1, slash, flags, is_dot_dir); + continue; + } + + if (is_dot_dir) { + STRUCT_STAT st; + if (link_stat(fbuf, &st, copy_dirlinks) != 0) { + io_error |= IOERR_GENERAL; + rsyserr(FERROR, errno, "link_stat %s failed", + full_fname(fbuf)); + continue; + } + send_file_name(f, flist, fbuf, &st, + recurse || xfer_dirs ? FLAG_TOP_DIR | flags : flags, + ALL_FILTERS); + } else { + send_file_name(f, flist, fbuf, NULL, + recurse ? FLAG_TOP_DIR | flags : flags, + ALL_FILTERS); + } + } + + free(rel_list); } void send_extra_file_list(int f, int at_least) @@ -1638,7 +1765,6 @@ struct file_list *send_file_list(int f, int argc, char *argv[]) int len, dirlen; STRUCT_STAT st; char *p, *dir; - char lastpath[MAXPATHLEN] = ""; struct file_list *flist; struct timeval start_tv, end_tv; int64 start_write; @@ -1817,7 +1943,12 @@ struct file_list *send_file_list(int f, int argc, char *argv[]) continue; } - if (implied_dirs && (p=strrchr(fbuf,'/')) && p != fbuf) { + 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; + } + } else 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; @@ -1829,29 +1960,10 @@ struct file_list *send_file_list(int f, int argc, char *argv[]) slash = fn; } *p = '/'; - if (fn != p || (*lp && *lp != '/')) { - int save_copy_links = copy_links; - int save_xfer_dirs = xfer_dirs; - int dir_flags = flags & ~FLAG_XFER_DIR; - copy_links |= copy_unsafe_links; - xfer_dirs = 1; - while ((slash = strchr(slash+1, '/')) != 0) { - *slash = '\0'; - send_file_name(f, flist, fbuf, NULL, - dir_flags, ALL_FILTERS); - *slash = '/'; - } - copy_links = save_copy_links; - xfer_dirs = save_xfer_dirs; - *p = '\0'; - strlcpy(lastpath, fbuf, sizeof lastpath); - *p = '/'; - } + if (fn != p || (*lp && *lp != '/')) + send_implied_dirs(f, flist, fbuf, slash, p, flags, 0); } - if (one_file_system) - filesystem_dev = st.st_dev; - if (recurse || (xfer_dirs && is_dot_dir)) { struct file_struct *file; int top_flags = FLAG_TOP_DIR | flags; @@ -2183,8 +2295,7 @@ struct file_list *flist_new(int flags, char *msg) { struct file_list *flist; - flist = new0(struct file_list); - if (!flist) + if (!(flist = new0(struct file_list))) out_of_memory(msg); if (flags & FLIST_TEMP) { @@ -2308,7 +2419,7 @@ static void clean_flist(struct file_list *flist, int strip_root) * list. Otherwise keep the first one. */ if (S_ISDIR(file->mode)) { struct file_struct *fp = flist->sorted[j]; - if (!S_ISDIR(fp->mode) || !(fp->flags & FLAG_XFER_DIR)) + if (!S_ISDIR(fp->mode)) keep = i, drop = j; else { if (am_sender) -- 2.34.1