--- embedaddon/rsync/flist.c 2013/10/14 07:51:14 1.1.1.2 +++ embedaddon/rsync/flist.c 2016/11/01 09:54:32 1.1.1.3 @@ -4,7 +4,7 @@ * Copyright (C) 1996 Andrew Tridgell * Copyright (C) 1996 Paul Mackerras * Copyright (C) 2001, 2002 Martin Pool - * Copyright (C) 2002-2013 Wayne Davison + * Copyright (C) 2002-2015 Wayne Davison * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -736,8 +736,11 @@ static struct file_struct *recv_file_entry(int f, stru } #endif - if (*thisname) - clean_fname(thisname, 0); + if (*thisname + && (clean_fname(thisname, CFN_REFUSE_DOT_DOT_DIRS) < 0 || (!relative_paths && *thisname == '/'))) { + rprintf(FERROR, "ABORTING due to unsafe pathname from sender: %s\n", thisname); + exit_cleanup(RERR_PROTOCOL); + } if (sanitize_paths) sanitize_path(thisname, thisname, "", 0, SP_DEFAULT); @@ -945,7 +948,14 @@ static struct file_struct *recv_file_entry(int f, stru memcpy(bp, basename, basename_len); #ifdef SUPPORT_HARD_LINKS - if (xflags & XMIT_HLINKED) + if (xflags & XMIT_HLINKED +#ifndef CAN_HARDLINK_SYMLINK + && !S_ISLNK(mode) +#endif +#ifndef CAN_HARDLINK_SPECIAL + && !IS_SPECIAL(mode) && !IS_DEVICE(mode) +#endif + ) file->flags |= FLAG_HLINKED; #endif file->modtime = (time_t)modtime; @@ -1156,7 +1166,7 @@ struct file_struct *make_file(const char *fname, struc if (sanitize_paths) sanitize_path(thisname, thisname, "", 0, SP_DEFAULT); - if (stp && (S_ISDIR(stp->st_mode) || stp->st_mode == 0)) { + if (stp && (S_ISDIR(stp->st_mode) || IS_MISSING_FILE(*stp))) { /* This is needed to handle a "symlink/." with a --relative * dir, or a request to delete a specific file. */ st = *stp; @@ -1200,7 +1210,7 @@ struct file_struct *make_file(const char *fname, struc full_fname(thisname)); } return NULL; - } else if (st.st_mode == 0) { + } else if (IS_MISSING_FILE(st)) { io_error |= IOERR_GENERAL; rprintf(FINFO, "skipping file with bogus (zero) st_mode: %s\n", full_fname(thisname)); @@ -1303,7 +1313,7 @@ struct file_struct *make_file(const char *fname, struc #endif if (always_checksum && am_sender && S_ISREG(st.st_mode)) { - file_checksum(thisname, tmp_sum, st.st_size); + file_checksum(thisname, &st, tmp_sum); if (sender_keeps_checksum) extra_len += SUM_EXTRA_CNT * EXTRA_LEN; } @@ -2290,7 +2300,7 @@ struct file_list *send_file_list(int f, int argc, char } else fn = p; send_implied_dirs(f, flist, fbuf, fbuf, p, flags, - st.st_mode == 0 ? MISSING_NAME : name_type); + IS_MISSING_FILE(st) ? MISSING_NAME : name_type); if (fn == p) continue; } @@ -2425,8 +2435,9 @@ struct file_list *send_file_list(int f, int argc, char return flist; } -struct file_list *recv_file_list(int f) +struct file_list *recv_file_list(int f, int dir_ndx) { + const char *good_dirname = NULL; struct file_list *flist; int dstart, flags; int64 start_read; @@ -2482,6 +2493,23 @@ struct file_list *recv_file_list(int f) flist_expand(flist, 1); file = recv_file_entry(f, flist, flags); + if (inc_recurse) { + static const char empty_dir[] = "\0"; + const char *cur_dir = file->dirname ? file->dirname : empty_dir; + if (relative_paths && *cur_dir == '/') + cur_dir++; + if (cur_dir != good_dirname) { + const char *d = dir_ndx >= 0 ? f_name(dir_flist->files[dir_ndx], NULL) : empty_dir; + if (strcmp(cur_dir, d) != 0) { + rprintf(FERROR, + "ABORTING due to invalid path from sender: %s/%s\n", + cur_dir, file->basename); + exit_cleanup(RERR_PROTOCOL); + } + good_dirname = cur_dir; + } + } + if (S_ISREG(file->mode)) { /* Already counted */ } else if (S_ISDIR(file->mode)) { @@ -2553,6 +2581,9 @@ struct file_list *recv_file_list(int f) rprintf(FINFO, "[%s] flist_eof=1\n", who_am_i()); } + /* The --relative option sends paths with a leading slash, so we need + * to specify the strip_root option here. We rejected leading slashes + * for a non-relative transfer in recv_file_entry(). */ flist_sort_and_clean(flist, relative_paths); if (protocol_version < 30) { @@ -2602,7 +2633,7 @@ void recv_additional_file_list(int f) rprintf(FINFO, "[%s] receiving flist for dir %d\n", who_am_i(), ndx); } - flist = recv_file_list(f); + flist = recv_file_list(f, ndx); flist->parent_ndx = ndx; } } @@ -2656,6 +2687,34 @@ int flist_find(struct file_list *flist, struct file_st high = mid - 1; } return -1; +} + +/* Search for a name in the file list. You must specify want_dir_match as: + * 1=match directories, 0=match non-directories, or -1=match either. */ +int flist_find_name(struct file_list *flist, const char *fname, int want_dir_match) +{ + struct { /* We have to create a temporary file_struct for the search. */ + struct file_struct f; + char name_space[MAXPATHLEN]; + } t; + char fbuf[MAXPATHLEN]; + const char *slash = strrchr(fname, '/'); + const char *basename = slash ? slash+1 : fname; + + memset(&t.f, 0, FILE_STRUCT_LEN); + memcpy((void *)t.f.basename, basename, strlen(basename)+1); + + if (slash) { + strlcpy(fbuf, fname, slash - fname + 1); + t.f.dirname = fbuf; + } else + t.f.dirname = NULL; + + t.f.mode = want_dir_match > 0 ? S_IFDIR : S_IFREG; + + if (want_dir_match < 0) + return flist_find_ignore_dirness(flist, &t.f); + return flist_find(flist, &t.f); } /* Search for an identically-named item in the file list. Differs from