]> git.ipfire.org Git - thirdparty/rsync.git/commitdiff
Fix a random flist data bug w/delete, inc-recurse, and no -o.
authorWayne Davison <wayned@samba.org>
Sun, 30 Jan 2011 03:25:53 +0000 (19:25 -0800)
committerWayne Davison <wayned@samba.org>
Sun, 30 Jan 2011 03:25:53 +0000 (19:25 -0800)
Fixes bug 7936.

flist.c
generator.c
log.c
rsync.c
rsync.h

diff --git a/flist.c b/flist.c
index 86e45412f78ebad71e6661858d31b943f7c674e1..59109bccce9dec0ed097bd0ea0cdffee88a39ad2 100644 (file)
--- 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)
index e14b72eb9f02453abc2f64e70e507e41a0e0b1e9..dd23100179976051f3682069c7c7945d62edc4ed 100644 (file)
@@ -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 02ac4ea13316cf266c4ab78b7848df806c7c5702..46161fe1e6d2afc03deaffa2ee579197c24ea4e9 100644 (file)
--- 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 e14760f426720ca1060ebcf1a768d8caa9888c3b..d9bd863fb1358e91770b9c39adb6ff3aa92ba846 100644 (file)
--- 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 5f914d1c1679cb15dc9eb9e9dfe53d5c1523a0cc..cd4cb8dcfa150ca558eeffb4c8bad8b02b77aa22 100644 (file)
--- a/rsync.h
+++ b/rsync.h
 
 /* 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)