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 6d39196..83effc0 100644 (file)
--- a/flist.c
+++ b/flist.c
@@ -4,7 +4,7 @@
  * Copyright (C) 1996 Andrew Tridgell
  * Copyright (C) 1996 Paul Mackerras
  * Copyright (C) 2001, 2002 Martin Pool <mbp@samba.org>
- * Copyright (C) 2002-2008 Wayne Davison
+ * Copyright (C) 2002-2009 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
@@ -425,8 +425,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;
@@ -441,6 +440,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)) {
+               /* 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))
@@ -725,8 +735,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;
@@ -801,7 +810,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);
@@ -942,8 +952,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);
@@ -1257,10 +1266,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;
@@ -1294,25 +1304,6 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
        else if (!pool)
                F_DEPTH(file) = extra_len / EXTRA_LEN;
 
-       /* This code is only used by the receiver when it is building
-        * a list of files for a delete pass. */
-       if (keep_dirlinks && linkname_len && flist) {
-               STRUCT_STAT st2;
-               int save_mode = file->mode;
-               file->mode = S_IFDIR; /* Find a directory with our name. */
-               if (flist_find(dir_flist, file) >= 0
-                && x_stat(thisname, &st2, NULL) == 0 && S_ISDIR(st2.st_mode)) {
-                       file->modtime = st2.st_mtime;
-                       file->len32 = 0;
-                       file->mode = st2.st_mode;
-                       if (uid_ndx)
-                               F_OWNER(file) = st2.st_uid;
-                       if (gid_ndx)
-                               F_GROUP(file) = st2.st_gid;
-               } else
-                       file->mode = save_mode;
-       }
-
        if (basename_len == 0+1) {
                if (!pool)
                        unmake_file(file);
@@ -1361,6 +1352,14 @@ static struct file_struct *send_file_name(int f, struct file_list *flist,
                if (preserve_links && S_ISLNK(file->mode)) {
                        symlink_name = F_SYMLINK(file);
                        symlink_len = strlen(symlink_name);
+                       if (symlink_len == 0) {
+                               io_error |= IOERR_GENERAL;
+                               f_name(file, fbuf);
+                               rprintf(FERROR_XFER,
+                                   "skipping symlink with 0-length value: %s\n",
+                                   full_fname(fbuf));
+                               return NULL;
+                       }
                } else {
                        symlink_name = NULL;
                        symlink_len = 0;
@@ -1597,6 +1596,19 @@ static void add_dirs_to_tree(int parent_ndx, struct file_list *from_flist,
                DIR_NEXT_SIBLING(dp) = -1;
 }
 
+static void interpret_stat_error(const char *fname, int is_dir)
+{
+       if (errno == ENOENT) {
+               io_error |= IOERR_VANISHED;
+               rprintf(FWARNING, "%s has vanished: %s\n",
+                       is_dir ? "directory" : "file", full_fname(fname));
+       } else {
+               io_error |= IOERR_GENERAL;
+               rsyserr(FERROR_XFER, errno, "link_stat %s failed",
+                       full_fname(fname));
+       }
+}
+
 /* This function is normally called by the sender, but the receiving side also
  * calls it from get_dirlist() with f set to -1 so that we just construct the
  * file list in memory without sending it over the wire.  Also, get_dirlist()
@@ -1616,6 +1628,11 @@ static void send_directory(int f, struct file_list *flist, char *fbuf, int len,
        assert(flist != NULL);
 
        if (!(d = opendir(fbuf))) {
+               if (errno == ENOENT) {
+                       if (am_sender) /* Can abuse this for vanished error w/ENOENT: */
+                               interpret_stat_error(fbuf, True);
+                       return;
+               }
                io_error |= IOERR_GENERAL;
                rsyserr(FERROR_XFER, errno, "opendir %s failed", full_fname(fbuf));
                return;
@@ -1787,9 +1804,7 @@ static void send1extra(int f, struct file_struct *file, struct file_list *flist)
                if (one_file_system) {
                        STRUCT_STAT st;
                        if (link_stat(fbuf, &st, copy_dirlinks) != 0) {
-                               io_error |= IOERR_GENERAL;
-                               rsyserr(FERROR_XFER, errno, "link_stat %s failed",
-                                       full_fname(fbuf));
+                               interpret_stat_error(fbuf, True);
                                return;
                        }
                        filesystem_dev = st.st_dev;
@@ -1824,9 +1839,7 @@ static void send1extra(int f, struct file_struct *file, struct file_list *flist)
                if (name_type != NORMAL_NAME) {
                        STRUCT_STAT st;
                        if (link_stat(fbuf, &st, 1) != 0) {
-                               io_error |= IOERR_GENERAL;
-                               rsyserr(FERROR_XFER, errno, "link_stat %s failed",
-                                       full_fname(fbuf));
+                               interpret_stat_error(fbuf, True);
                                continue;
                        }
                        send_file_name(f, flist, fbuf, &st, FLAG_TOP_DIR | flags, ALL_FILTERS);
@@ -2219,6 +2232,8 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
        if (numeric_ids <= 0 && !inc_recurse)
                send_id_list(f);
 
+       set_msg_fd_in(-1);
+
        /* send the io_error flag */
        if (protocol_version < 30)
                write_int(f, ignore_errors ? 0 : io_error);
@@ -2263,6 +2278,7 @@ struct file_list *recv_file_list(int f)
        struct file_list *flist;
        int dstart, flags;
        int64 start_read;
+       int save_verbose = verbose;
 
        if (!first_flist)
                rprintf(FLOG, "receiving file list\n");
@@ -2289,6 +2305,8 @@ struct file_list *recv_file_list(int f)
                dstart = 0;
        }
 
+       if (am_server && verbose > 2)
+               verbose = 2;
        while ((flags = read_byte(f)) != 0) {
                struct file_struct *file;
 
@@ -2313,6 +2331,7 @@ struct file_list *recv_file_list(int f)
                }
        }
        file_total += flist->used;
+       verbose = save_verbose;
 
        if (verbose > 2)
                rprintf(FINFO, "received %d names\n", flist->used);
@@ -2459,6 +2478,28 @@ int flist_find(struct file_list *flist, struct file_struct *f)
        return -1;
 }
 
+/* Search for an identically-named item in the file list.  Differs from
+ * flist_find in that an item that agrees with "f" in directory-ness is
+ * preferred but one that does not is still found. */
+int flist_find_ignore_dirness(struct file_list *flist, struct file_struct *f)
+{
+       mode_t save_mode;
+       int ndx;
+
+       /* First look for an item that agrees in directory-ness. */
+       ndx = flist_find(flist, f);
+       if (ndx >= 0)
+               return ndx;
+
+       /* Temporarily flip f->mode to look for an item of opposite
+        * directory-ness. */
+       save_mode = f->mode;
+       f->mode = S_ISDIR(f->mode) ? S_IFREG : S_IFDIR;
+       ndx = flist_find(flist, f);
+       f->mode = save_mode;
+       return ndx;
+}
+
 /*
  * Free up any resources a file_struct has allocated
  * and clear the file.
@@ -2976,7 +3017,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 (do_progress)