Change sending/receiving/storing of the rdev value for special files.
[rsync/rsync.git] / flist.c
diff --git a/flist.c b/flist.c
index 90c0ce5..2af7e88 100644 (file)
--- a/flist.c
+++ b/flist.c
@@ -51,6 +51,7 @@ extern int preserve_links;
 extern int preserve_hard_links;
 extern int preserve_devices;
 extern int preserve_specials;
+extern int missing_args;
 extern int uid_ndx;
 extern int gid_ndx;
 extern int eol_nulls;
@@ -431,7 +432,7 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
                        stats.num_symlinks++;
                else if (IS_DEVICE(file->mode))
                        stats.num_devices++;
-               else
+               else if (IS_SPECIAL(file->mode))
                        stats.num_specials++;
                xflags = 0;
        }
@@ -441,8 +442,7 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
        else
                mode = file->mode;
 
-       if ((preserve_devices && IS_DEVICE(mode))
-        || (preserve_specials && IS_SPECIAL(mode))) {
+       if (preserve_devices && IS_DEVICE(mode)) {
                if (protocol_version < 28) {
                        if (tmp_rdev == rdev)
                                xflags |= XMIT_SAME_RDEV_pre28;
@@ -457,6 +457,17 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
                        if (protocol_version < 30 && (uint32)minor(rdev) <= 0xFFu)
                                xflags |= XMIT_RDEV_MINOR_8_pre30;
                }
+       } else if (preserve_specials && IS_SPECIAL(mode) && protocol_version < 31) {
+               /* Special files don't need an rdev number, so just make
+                * the historical transmission of the value efficient. */
+               if (protocol_version < 28)
+                       xflags |= XMIT_SAME_RDEV_pre28;
+               else {
+                       rdev = MAKEDEV(major(rdev), 0);
+                       xflags |= XMIT_SAME_RDEV_MAJOR;
+                       if (protocol_version < 30)
+                               xflags |= XMIT_RDEV_MINOR_8_pre30;
+               }
        } else if (protocol_version < 28)
                rdev = MAKEDEV(0, 0);
        if (!preserve_uid || ((uid_t)F_OWNER(file) == uid && *lastname))
@@ -592,7 +603,7 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
                }
        }
        if ((preserve_devices && IS_DEVICE(mode))
-        || (preserve_specials && IS_SPECIAL(mode))) {
+        || (preserve_specials && IS_SPECIAL(mode) && protocol_version < 31)) {
                if (protocol_version < 28) {
                        if (!(xflags & XMIT_SAME_RDEV_pre28))
                                write_int(f, (int)rdev);
@@ -761,8 +772,7 @@ static struct file_struct *recv_file_entry(struct file_list *flist,
                                uid = F_OWNER(first);
                        if (preserve_gid)
                                gid = F_GROUP(first);
-                       if ((preserve_devices && IS_DEVICE(mode))
-                        || (preserve_specials && IS_SPECIAL(mode))) {
+                       if (preserve_devices && IS_DEVICE(mode)) {
                                uint32 *devp = F_RDEV_P(first);
                                rdev = MAKEDEV(DEV_MAJOR(devp), DEV_MINOR(devp));
                                extra_len += DEV_EXTRA_CNT * EXTRA_LEN;
@@ -793,7 +803,7 @@ static struct file_struct *recv_file_entry(struct file_list *flist,
        if (!(xflags & XMIT_SAME_MODE))
                mode = from_wire_mode(read_int(f));
 
-       if (chmod_modes && !S_ISLNK(mode))
+       if (chmod_modes && !S_ISLNK(mode) && mode)
                mode = tweak_mode(mode, chmod_modes);
 
        if (preserve_uid && !(xflags & XMIT_SAME_UID)) {
@@ -821,7 +831,7 @@ static struct file_struct *recv_file_entry(struct file_list *flist,
        }
 
        if ((preserve_devices && IS_DEVICE(mode))
-        || (preserve_specials && IS_SPECIAL(mode))) {
+        || (preserve_specials && IS_SPECIAL(mode) && protocol_version < 31)) {
                if (protocol_version < 28) {
                        if (!(xflags & XMIT_SAME_RDEV_pre28))
                                rdev = (dev_t)read_int(f);
@@ -837,7 +847,8 @@ static struct file_struct *recv_file_entry(struct file_list *flist,
                                rdev_minor = read_int(f);
                        rdev = MAKEDEV(rdev_major, rdev_minor);
                }
-               extra_len += DEV_EXTRA_CNT * EXTRA_LEN;
+               if (IS_DEVICE(mode))
+                       extra_len += DEV_EXTRA_CNT * EXTRA_LEN;
                file_length = 0;
        } else if (protocol_version < 28)
                rdev = MAKEDEV(0, 0);
@@ -978,8 +989,7 @@ static struct file_struct *recv_file_entry(struct file_list *flist,
                }
        }
 
-       if ((preserve_devices && IS_DEVICE(mode))
-        || (preserve_specials && IS_SPECIAL(mode))) {
+       if (preserve_devices && IS_DEVICE(mode)) {
                uint32 *devp = F_RDEV_P(file);
                DEV_MAJOR(devp) = major(rdev);
                DEV_MINOR(devp) = minor(rdev);
@@ -1124,8 +1134,10 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
        if (sanitize_paths)
                sanitize_path(thisname, thisname, "", 0, SP_DEFAULT);
 
-       if (stp && S_ISDIR(stp->st_mode)) {
-               st = *stp; /* Needed for "symlink/." with --relative. */
+       if (stp && (S_ISDIR(stp->st_mode) || stp->st_mode == 0)) {
+               /* This is needed to handle a "symlink/." with a --relative
+                * dir, or a request to delete a specific file. */
+               st = *stp;
                *linkname = '\0'; /* make IBM code checker happy */
        } else if (readlink_stat(thisname, &st, linkname) != 0) {
                int save_errno = errno;
@@ -1166,6 +1178,11 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
                                full_fname(thisname));
                }
                return NULL;
+       } else if (st.st_mode == 0) {
+               io_error |= IOERR_GENERAL;
+               rprintf(FINFO, "skipping file with bogus (zero) st_mode: %s\n",
+                       full_fname(thisname));
+               return NULL;
        }
 
        if (filter_level == NO_FILTERS)
@@ -1299,10 +1316,11 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
 #endif
 
 #ifdef HAVE_STRUCT_STAT_ST_RDEV
-       if (IS_DEVICE(st.st_mode) || IS_SPECIAL(st.st_mode)) {
+       if (IS_DEVICE(st.st_mode)) {
                tmp_rdev = st.st_rdev;
                st.st_size = 0;
-       }
+       } else if (IS_SPECIAL(st.st_mode))
+               st.st_size = 0;
 #endif
 
        file->flags = flags;
@@ -1364,7 +1382,7 @@ static struct file_struct *send_file_name(int f, struct file_list *flist,
        if (!file)
                return NULL;
 
-       if (chmod_modes && !S_ISLNK(file->mode))
+       if (chmod_modes && !S_ISLNK(file->mode) && file->mode)
                file->mode = tweak_mode(file->mode, chmod_modes);
 
        if (f >= 0) {
@@ -2169,14 +2187,26 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
                if (link_stat(fbuf, &st, copy_dirlinks || name_type != NORMAL_NAME) != 0
                 || (name_type != DOTDIR_NAME && is_daemon_excluded(fbuf, S_ISDIR(st.st_mode)))
                 || (relative_paths && path_is_daemon_excluded(fbuf, 1))) {
-                       io_error |= IOERR_GENERAL;
-                       rsyserr(FERROR_XFER, errno, "link_stat %s failed",
-                               full_fname(fbuf));
-                       continue;
+                       if (errno != ENOENT || missing_args == 0) {
+                               /* This is a transfer error, but inhibit deletion
+                                * only if we might be omitting an existing file. */
+                               if (errno != ENOENT)
+                                       io_error |= IOERR_GENERAL;
+                               rsyserr(FERROR_XFER, errno, "link_stat %s failed",
+                                       full_fname(fbuf));
+                               continue;
+                       } else if (missing_args == 1) {
+                               /* Just ignore the arg. */
+                               continue;
+                       } else /* (missing_args == 2) */ {
+                               /* Send the arg as a "missing" entry with
+                                * mode 0, which tells the generator to delete it. */
+                               memset(&st, 0, sizeof st);
+                       }
                }
 
                /* A dot-dir should not be excluded! */
-               if (name_type != DOTDIR_NAME
+               if (name_type != DOTDIR_NAME && st.st_mode != 0
                 && is_excluded(fbuf, S_ISDIR(st.st_mode) != 0, ALL_FILTERS))
                        continue;
 
@@ -3086,7 +3116,7 @@ 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, 0);
+       send_directory(ignore_filter_rules ? -2 : -1, dirlist, dirname, dlen, FLAG_CONTENT_DIR);
        xfer_dirs = save_xfer_dirs;
        recurse = save_recurse;
        if (INFO_GTE(PROGRESS, 1))