From c499002e51db14b3b0c3b33680dc0955fe37206d Mon Sep 17 00:00:00 2001 From: Wayne Davison Date: Sat, 29 Jan 2011 19:25:53 -0800 Subject: [PATCH 1/3] Fix a random flist data bug w/delete, inc-recurse, and no -o. Fixes bug 7936. --- flist.c | 14 +++++++++----- generator.c | 30 +++++++----------------------- log.c | 2 -- rsync.c | 2 -- rsync.h | 11 ++++++++++- 5 files changed, 26 insertions(+), 33 deletions(-) diff --git a/flist.c b/flist.c index 86e45412..59109bcc 100644 --- a/flist.c +++ b/flist.c @@ -52,12 +52,9 @@ extern int preserve_hard_links; extern int preserve_devices; extern int preserve_specials; extern int delete_during; -extern int uid_ndx; -extern int gid_ndx; extern int eol_nulls; extern int relative_paths; extern int implied_dirs; -extern int file_extra_cnt; extern int ignore_perishable; extern int non_perishable_cnt; extern int prune_empty_dirs; @@ -1230,6 +1227,9 @@ struct file_struct *make_file(const char *fname, struct file_list *flist, linkname_len = 0; #endif + if (!uid_ndx && flags & FLAG_DEL_NEEDS_UID) + extra_len += EXTRA_LEN; + #if SIZEOF_CAPITAL_OFF_T >= 8 if (st.st_size > 0xFFFFFFFFu && S_ISREG(st.st_mode)) extra_len += EXTRA_LEN; @@ -1288,6 +1288,8 @@ struct file_struct *make_file(const char *fname, struct file_list *flist, file->mode = st.st_mode; if (uid_ndx) /* Check uid_ndx instead of preserve_uid for del support */ F_OWNER(file) = st.st_uid; + else if (flags & FLAG_DEL_NEEDS_UID) + F_DEL_OWNER(file) = st.st_uid; if (gid_ndx) /* Check gid_ndx instead of preserve_gid for del support */ F_GROUP(file) = st.st_gid; @@ -3044,13 +3046,14 @@ char *f_name(const struct file_struct *f, char *fbuf) * of the dirname string, and also indicates that "dirname" is a MAXPATHLEN * buffer (the functions we call will append names onto the end, but the old * dir value will be restored on exit). */ -struct file_list *get_dirlist(char *dirname, int dlen, int ignore_filter_rules) +struct file_list *get_dirlist(char *dirname, int dlen, int flags) { struct file_list *dirlist; char dirbuf[MAXPATHLEN]; int save_recurse = recurse; int save_xfer_dirs = xfer_dirs; int save_prune_empty_dirs = prune_empty_dirs; + int senddir_fd = flags & GDL_IGNORE_FILTER_RULES ? -2 : -1; if (dlen < 0) { dlen = strlcpy(dirbuf, dirname, MAXPATHLEN); @@ -3063,7 +3066,8 @@ struct file_list *get_dirlist(char *dirname, int dlen, int ignore_filter_rules) recurse = 0; xfer_dirs = 1; - send_directory(ignore_filter_rules ? -2 : -1, dirlist, dirname, dlen, FLAG_CONTENT_DIR); + send_directory(senddir_fd, dirlist, dirname, dlen, + FLAG_CONTENT_DIR | (flags & GDL_DEL_NEEDS_UID ? FLAG_DEL_NEEDS_UID : 0)); xfer_dirs = save_xfer_dirs; recurse = save_recurse; if (do_progress) diff --git a/generator.c b/generator.c index e14b72eb..dd231001 100644 --- a/generator.c +++ b/generator.c @@ -44,8 +44,6 @@ extern int preserve_hard_links; extern int preserve_executability; extern int preserve_perms; extern int preserve_times; -extern int uid_ndx; -extern int gid_ndx; extern int delete_mode; extern int delete_before; extern int delete_during; @@ -169,19 +167,12 @@ static enum delret delete_item(char *fbuf, uint16 mode, uint16 flags) do_chmod(fbuf, mode | S_IWUSR); if (S_ISDIR(mode) && !(flags & DEL_DIR_IS_EMPTY)) { - int save_uid_ndx = uid_ndx; /* This only happens on the first call to delete_item() since * delete_dir_contents() always calls us w/DEL_DIR_IS_EMPTY. */ - if (!uid_ndx) - uid_ndx = ++file_extra_cnt; ignore_perishable = 1; /* If DEL_RECURSE is not set, this just reports emptiness. */ ret = delete_dir_contents(fbuf, flags); ignore_perishable = 0; - if (!save_uid_ndx) { - --file_extra_cnt; - uid_ndx = 0; - } if (ret == DR_NOT_EMPTY || ret == DR_AT_LIMIT) goto check_ret; /* OK: try to delete the directory. */ @@ -260,7 +251,7 @@ static enum delret delete_dir_contents(char *fname, uint16 flags) save_filters = push_local_filters(fname, dlen); non_perishable_cnt = 0; - dirlist = get_dirlist(fname, dlen, 0); + dirlist = get_dirlist(fname, dlen, GDL_DEL_NEEDS_UID); ret = non_perishable_cnt ? DR_NOT_EMPTY : DR_SUCCESS; if (!dirlist->used) @@ -282,6 +273,7 @@ static enum delret delete_dir_contents(char *fname, uint16 flags) for (j = dirlist->used; j--; ) { struct file_struct *fp = dirlist->files[j]; + uid_t fp_owner = uid_ndx ? F_OWNER(fp) : F_DEL_OWNER(fp); if (fp->flags & FLAG_MOUNT_DIR && S_ISDIR(fp->mode)) { if (verbose > 1) { @@ -294,7 +286,7 @@ static enum delret delete_dir_contents(char *fname, uint16 flags) } strlcpy(p, fp->basename, remainder); - if (!(fp->mode & S_IWUSR) && !am_root && (uid_t)F_OWNER(fp) == our_uid) + if (!(fp->mode & S_IWUSR) && !am_root && fp_owner == our_uid) do_chmod(fname, fp->mode | S_IWUSR); /* Save stack by recursing to ourself directly. */ if (S_ISDIR(fp->mode)) { @@ -472,7 +464,6 @@ static void delete_in_dir(char *fbuf, struct file_struct *file, dev_t *fs_dev) struct file_list *dirlist; char delbuf[MAXPATHLEN]; int dlen, i; - int save_uid_ndx = uid_ndx; if (!fbuf) { change_local_filter_dir(NULL, 0, 0); @@ -504,10 +495,7 @@ static void delete_in_dir(char *fbuf, struct file_struct *file, dev_t *fs_dev) return; } - if (!uid_ndx) - uid_ndx = ++file_extra_cnt; - - dirlist = get_dirlist(fbuf, dlen, 0); + dirlist = get_dirlist(fbuf, dlen, GDL_DEL_NEEDS_UID); /* If an item in dirlist is not found in flist, delete it * from the filesystem. */ @@ -526,7 +514,8 @@ static void delete_in_dir(char *fbuf, struct file_struct *file, dev_t *fs_dev) * a delete_item call with a DEL_MAKE_ROOM flag. */ if (flist_find_ignore_dirness(cur_flist, fp) < 0) { int flags = DEL_RECURSE; - if (!(fp->mode & S_IWUSR) && !am_root && (uid_t)F_OWNER(fp) == our_uid) + uid_t fp_owner = uid_ndx ? F_OWNER(fp) : F_DEL_OWNER(fp); + if (!(fp->mode & S_IWUSR) && !am_root && fp_owner == our_uid) flags |= DEL_NO_UID_WRITE; f_name(fp, delbuf); if (delete_during == 2) { @@ -538,11 +527,6 @@ static void delete_in_dir(char *fbuf, struct file_struct *file, dev_t *fs_dev) } flist_free(dirlist); - - if (!save_uid_ndx) { - --file_extra_cnt; - uid_ndx = 0; - } } /* This deletes any files on the receiving side that are not present on the @@ -1367,7 +1351,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx, if (need_fuzzy_dirlist && S_ISREG(file->mode)) { strlcpy(fnamecmpbuf, dn, sizeof fnamecmpbuf); - fuzzy_dirlist = get_dirlist(fnamecmpbuf, -1, 1); + fuzzy_dirlist = get_dirlist(fnamecmpbuf, -1, GDL_IGNORE_FILTER_RULES); need_fuzzy_dirlist = 0; } diff --git a/log.c b/log.c index 02ac4ea1..46161fe1 100644 --- a/log.c +++ b/log.c @@ -35,8 +35,6 @@ extern int msg_fd_out; extern int allow_8bit_chars; extern int protocol_version; extern int preserve_times; -extern int uid_ndx; -extern int gid_ndx; extern int progress_is_active; extern int stdout_format_has_i; extern int stdout_format_has_o_or_i; diff --git a/rsync.c b/rsync.c index e14760f4..d9bd863f 100644 --- a/rsync.c +++ b/rsync.c @@ -42,8 +42,6 @@ extern int am_generator; extern int am_starting_up; extern int allow_8bit_chars; extern int protocol_version; -extern int uid_ndx; -extern int gid_ndx; extern int inc_recurse; extern int inplace; extern int flist_eof; diff --git a/rsync.h b/rsync.h index 5f914d1c..cd4cb8dc 100644 --- a/rsync.h +++ b/rsync.h @@ -83,7 +83,12 @@ /* These flags are passed to functions but not stored. */ -#define FLAG_DIVERT_DIRS (1<<16)/* sender */ +#define FLAG_DEL_NEEDS_UID (1<<15)/* generator, but must be unique */ +#define FLAG_DIVERT_DIRS (1<<16)/* sender, but must be unique */ + +/* These flags are for get_dirlist(). */ +#define GDL_IGNORE_FILTER_RULES (1<<0) +#define GDL_DEL_NEEDS_UID (1<<1) #define BITS_SET(val,bits) (((val) & (bits)) == (bits)) #define BITS_SETnUNSET(val,onbits,offbits) (((val) & ((onbits)|(offbits))) == (onbits)) @@ -695,6 +700,10 @@ extern int xattrs_ndx; #define F_SUM(f) ((char*)OPT_EXTRA(f, LEN64_BUMP(f) + HLINK_BUMP(f) \ + SUM_EXTRA_CNT - 1)) +/* When deleting w/o --owner, we put the UID info last (like F_SUM(), but smaller). + * This is OK, because delete lists never need checksums. */ +#define F_DEL_OWNER(f) OPT_EXTRA(f, LEN64_BUMP(f) + HLINK_BUMP(f))->unum + /* Some utility defines: */ #define F_IS_ACTIVE(f) (f)->basename[0] #define F_IS_HLINKED(f) ((f)->flags & FLAG_HLINKED) -- 2.34.1 From 2064c28d6ddcc4bc11abe3092620a9730045f845 Mon Sep 17 00:00:00 2001 From: Wayne Davison Date: Sat, 29 Jan 2011 20:52:38 -0800 Subject: [PATCH 2/3] Move FLAG_DEL_NEEDS_UID into the "not stored" section. --- rsync.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rsync.h b/rsync.h index cd4cb8dc..f7b208b8 100644 --- a/rsync.h +++ b/rsync.h @@ -83,8 +83,8 @@ /* These flags are passed to functions but not stored. */ -#define FLAG_DEL_NEEDS_UID (1<<15)/* generator, but must be unique */ #define FLAG_DIVERT_DIRS (1<<16)/* sender, but must be unique */ +#define FLAG_DEL_NEEDS_UID (1<<17)/* generator, but must be unique */ /* These flags are for get_dirlist(). */ #define GDL_IGNORE_FILTER_RULES (1<<0) -- 2.34.1 From 83b94efa6b60a3ff5eee4c5f7812c617a90a03f6 Mon Sep 17 00:00:00 2001 From: Wayne Davison Date: Sat, 29 Jan 2011 22:10:43 -0800 Subject: [PATCH 3/3] Switch over to Matt's idea of using FLAG_OWNED_BY_US. --- flist.c | 15 ++++++--------- generator.c | 10 ++++------ rsync.h | 12 ++++-------- 3 files changed, 14 insertions(+), 23 deletions(-) diff --git a/flist.c b/flist.c index 59109bcc..7dec1528 100644 --- a/flist.c +++ b/flist.c @@ -67,6 +67,7 @@ extern int use_safe_inc_flist; extern int need_unsorted_flist; extern int sender_symlink_iconv; extern int unsort_ndx; +extern uid_t our_uid; extern struct stats stats; extern char *filesfrom_host; @@ -1227,9 +1228,6 @@ struct file_struct *make_file(const char *fname, struct file_list *flist, linkname_len = 0; #endif - if (!uid_ndx && flags & FLAG_DEL_NEEDS_UID) - extra_len += EXTRA_LEN; - #if SIZEOF_CAPITAL_OFF_T >= 8 if (st.st_size > 0xFFFFFFFFu && S_ISREG(st.st_mode)) extra_len += EXTRA_LEN; @@ -1286,12 +1284,12 @@ struct file_struct *make_file(const char *fname, struct file_list *flist, } #endif file->mode = st.st_mode; - if (uid_ndx) /* Check uid_ndx instead of preserve_uid for del support */ + if (preserve_uid) F_OWNER(file) = st.st_uid; - else if (flags & FLAG_DEL_NEEDS_UID) - F_DEL_OWNER(file) = st.st_uid; - if (gid_ndx) /* Check gid_ndx instead of preserve_gid for del support */ + if (preserve_gid) F_GROUP(file) = st.st_gid; + if (am_generator && st.st_uid == our_uid) + file->flags |= FLAG_OWNED_BY_US; if (basename != thisname) file->dirname = lastdir; @@ -3066,8 +3064,7 @@ struct file_list *get_dirlist(char *dirname, int dlen, int flags) recurse = 0; xfer_dirs = 1; - send_directory(senddir_fd, dirlist, dirname, dlen, - FLAG_CONTENT_DIR | (flags & GDL_DEL_NEEDS_UID ? FLAG_DEL_NEEDS_UID : 0)); + send_directory(senddir_fd, dirlist, dirname, dlen, FLAG_CONTENT_DIR); xfer_dirs = save_xfer_dirs; recurse = save_recurse; if (do_progress) diff --git a/generator.c b/generator.c index dd231001..0c306045 100644 --- a/generator.c +++ b/generator.c @@ -251,7 +251,7 @@ static enum delret delete_dir_contents(char *fname, uint16 flags) save_filters = push_local_filters(fname, dlen); non_perishable_cnt = 0; - dirlist = get_dirlist(fname, dlen, GDL_DEL_NEEDS_UID); + dirlist = get_dirlist(fname, dlen, 0); ret = non_perishable_cnt ? DR_NOT_EMPTY : DR_SUCCESS; if (!dirlist->used) @@ -273,7 +273,6 @@ static enum delret delete_dir_contents(char *fname, uint16 flags) for (j = dirlist->used; j--; ) { struct file_struct *fp = dirlist->files[j]; - uid_t fp_owner = uid_ndx ? F_OWNER(fp) : F_DEL_OWNER(fp); if (fp->flags & FLAG_MOUNT_DIR && S_ISDIR(fp->mode)) { if (verbose > 1) { @@ -286,7 +285,7 @@ static enum delret delete_dir_contents(char *fname, uint16 flags) } strlcpy(p, fp->basename, remainder); - if (!(fp->mode & S_IWUSR) && !am_root && fp_owner == our_uid) + if (!(fp->mode & S_IWUSR) && !am_root && fp->flags & FLAG_OWNED_BY_US) do_chmod(fname, fp->mode | S_IWUSR); /* Save stack by recursing to ourself directly. */ if (S_ISDIR(fp->mode)) { @@ -495,7 +494,7 @@ static void delete_in_dir(char *fbuf, struct file_struct *file, dev_t *fs_dev) return; } - dirlist = get_dirlist(fbuf, dlen, GDL_DEL_NEEDS_UID); + dirlist = get_dirlist(fbuf, dlen, 0); /* If an item in dirlist is not found in flist, delete it * from the filesystem. */ @@ -514,8 +513,7 @@ static void delete_in_dir(char *fbuf, struct file_struct *file, dev_t *fs_dev) * a delete_item call with a DEL_MAKE_ROOM flag. */ if (flist_find_ignore_dirness(cur_flist, fp) < 0) { int flags = DEL_RECURSE; - uid_t fp_owner = uid_ndx ? F_OWNER(fp) : F_DEL_OWNER(fp); - if (!(fp->mode & S_IWUSR) && !am_root && fp_owner == our_uid) + if (!(fp->mode & S_IWUSR) && !am_root && fp->flags & FLAG_OWNED_BY_US) flags |= DEL_NO_UID_WRITE; f_name(fp, delbuf); if (delete_during == 2) { diff --git a/rsync.h b/rsync.h index f7b208b8..e6238de3 100644 --- a/rsync.h +++ b/rsync.h @@ -65,6 +65,7 @@ /* These flags are used in the live flist data. */ #define FLAG_TOP_DIR (1<<0) /* sender/receiver/generator */ +#define FLAG_OWNED_BY_US (1<<0) /* generator: set by make_file() for aux flists only */ #define FLAG_FILE_SENT (1<<1) /* sender/receiver/generator */ #define FLAG_DIR_CREATED (1<<1) /* generator */ #define FLAG_CONTENT_DIR (1<<2) /* sender/receiver/generator */ @@ -83,13 +84,12 @@ /* These flags are passed to functions but not stored. */ -#define FLAG_DIVERT_DIRS (1<<16)/* sender, but must be unique */ -#define FLAG_DEL_NEEDS_UID (1<<17)/* generator, but must be unique */ +#define FLAG_DIVERT_DIRS (1<<16) /* sender, but must be unique */ /* These flags are for get_dirlist(). */ #define GDL_IGNORE_FILTER_RULES (1<<0) -#define GDL_DEL_NEEDS_UID (1<<1) +/* Some helper macros for matching bits. */ #define BITS_SET(val,bits) (((val) & (bits)) == (bits)) #define BITS_SETnUNSET(val,onbits,offbits) (((val) & ((onbits)|(offbits))) == (onbits)) #define BITS_EQUAL(b1,b2,mask) (((unsigned)(b1) & (unsigned)(mask)) \ @@ -100,7 +100,7 @@ /* This is used when working on a new protocol version in CVS, and should * be a new non-zero value for each CVS change that affects the protocol. - * It must ALWAYS be 0 when the protocol goes final! */ + * It must ALWAYS be 0 when the protocol goes final (and NEVER before)! */ #define SUBPROTOCOL_VERSION 0 /* We refuse to interoperate with versions that are not in this range. @@ -700,10 +700,6 @@ extern int xattrs_ndx; #define F_SUM(f) ((char*)OPT_EXTRA(f, LEN64_BUMP(f) + HLINK_BUMP(f) \ + SUM_EXTRA_CNT - 1)) -/* When deleting w/o --owner, we put the UID info last (like F_SUM(), but smaller). - * This is OK, because delete lists never need checksums. */ -#define F_DEL_OWNER(f) OPT_EXTRA(f, LEN64_BUMP(f) + HLINK_BUMP(f))->unum - /* Some utility defines: */ #define F_IS_ACTIVE(f) (f)->basename[0] #define F_IS_HLINKED(f) ((f)->flags & FLAG_HLINKED) -- 2.34.1