Improved several items and added a new section describing what is
[rsync/rsync.git] / flist.c
diff --git a/flist.c b/flist.c
index 2ded8e6..f06a6ff 100644 (file)
--- a/flist.c
+++ b/flist.c
@@ -35,7 +35,6 @@ extern int am_root;
 extern int am_server;
 extern int am_daemon;
 extern int am_sender;
-extern int delete_during;
 extern int always_checksum;
 extern int module_id;
 extern int ignore_errors;
@@ -81,7 +80,6 @@ static struct file_list *received_flist;
 static dev_t filesystem_dev; /* used to implement -x */
 static int deletion_count = 0; /* used to implement --max-delete */
 
-static int flist_find(struct file_list *flist, struct file_struct *f);
 static void clean_flist(struct file_list *flist, int strip_root, int no_dups);
 static void output_flist(struct file_list *flist, const char *whose_list);
 
@@ -153,14 +151,14 @@ static void list_file_entry(struct file_struct *f)
                rprintf(FINFO, "%s %11.0f %s %s -> %s\n",
                        perms,
                        (double)f->length, timestring(f->modtime),
-                       f_name(f), f->u.link);
+                       safe_fname(f_name(f)), safe_fname(f->u.link));
        } else
 #endif
        {
                rprintf(FINFO, "%s %11.0f %s %s\n",
                        perms,
                        (double)f->length, timestring(f->modtime),
-                       f_name(f));
+                       safe_fname(f_name(f)));
        }
 }
 
@@ -194,7 +192,7 @@ static int readlink_stat(const char *path, STRUCT_STAT *buffer, char *linkbuf)
                if (copy_unsafe_links && unsafe_symlink(linkbuf, path)) {
                        if (verbose > 1) {
                                rprintf(FINFO,"copying unsafe symlink \"%s\" -> \"%s\"\n",
-                                       path, linkbuf);
+                                       safe_fname(path), safe_fname(linkbuf));
                        }
                        return do_stat(path, buffer);
                }
@@ -554,7 +552,7 @@ static void receive_file_entry(struct file_list *flist, int ndx,
        if (l2 >= MAXPATHLEN - l1) {
                rprintf(FERROR,
                        "overflow: flags=0x%x l1=%d l2=%d lastname=%s\n",
-                       flags, l1, l2, lastname);
+                       flags, l1, l2, safe_fname(lastname));
                overflow("receive_file_entry");
        }
 
@@ -772,7 +770,8 @@ struct file_struct *make_file(char *fname, struct file_list *flist,
 
        if (strlcpy(thisname, fname, sizeof thisname)
            >= sizeof thisname - flist_dir_len) {
-               rprintf(FINFO, "skipping overly long name: %s\n", fname);
+               rprintf(FINFO, "skipping overly long name: %s\n",
+                       safe_fname(fname));
                return NULL;
        }
        clean_fname(thisname, 0);
@@ -817,7 +816,7 @@ struct file_struct *make_file(char *fname, struct file_list *flist,
                goto skip_filters;
 
        if (S_ISDIR(st.st_mode) && !xfer_dirs) {
-               rprintf(FINFO, "skipping directory %s\n", thisname);
+               rprintf(FINFO, "skipping directory %s\n", safe_fname(thisname));
                return NULL;
        }
 
@@ -843,7 +842,7 @@ skip_filters:
 
        if (verbose > 2) {
                rprintf(FINFO, "[%s] make_file(%s,*,%d)\n",
-                       who_am_i(), thisname, filter_level);
+                       who_am_i(), safe_fname(thisname), filter_level);
        }
 
        if ((basename = strrchr(thisname, '/')) != NULL) {
@@ -1050,7 +1049,7 @@ static void send_directory(int f, struct file_list *flist,
        if (errno) {
                io_error |= IOERR_GENERAL;
                *p = '\0';
-               rsyserr(FERROR, errno, "readdir(%s)", fbuf);
+               rsyserr(FERROR, errno, "readdir(%s)", full_fname(fbuf));
        }
 
        closedir(d);
@@ -1139,7 +1138,8 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
                }
 
                if (S_ISDIR(st.st_mode) && !xfer_dirs) {
-                       rprintf(FINFO, "skipping directory %s\n", fname);
+                       rprintf(FINFO, "skipping directory %s\n",
+                               safe_fname(fname));
                        continue;
                }
 
@@ -1318,7 +1318,7 @@ struct file_list *recv_file_list(int f)
 
                if (verbose > 2) {
                        rprintf(FINFO, "recv_file_name(%s)\n",
-                               f_name(flist->files[i]));
+                               safe_fname(f_name(flist->files[i])));
                }
        }
        receive_file_entry(NULL, 0, 0, 0); /* Signal that we're done. */
@@ -1372,7 +1372,7 @@ static int file_compare(struct file_struct **file1, struct file_struct **file2)
 }
 
 
-static int flist_find(struct file_list *flist, struct file_struct *f)
+int flist_find(struct file_list *flist, struct file_struct *f)
 {
        int low = flist->low, high = flist->high;
        int ret, mid, mid_up;
@@ -1474,7 +1474,7 @@ static void clean_flist(struct file_list *flist, int strip_root, int no_dups)
                        if (verbose > 1 && !am_server) {
                                rprintf(FINFO,
                                        "removing duplicate name %s from file list %d\n",
-                                       f_name(flist->files[i]), i);
+                                       safe_fname(f_name(flist->files[i])), i);
                        }
                        /* Make sure that if we unduplicate '.', that we don't
                         * lose track of a user-specified top directory. */
@@ -1527,7 +1527,7 @@ static void output_flist(struct file_list *flist, const char *whose_list)
                        sprintf(depthbuf, "%d", file->dir.depth);
                rprintf(FINFO, "[%s] i=%d %s %s%s%s%s mode=0%o len=%.0f%s%s flags=%x\n",
                        whose_list, i, am_sender ? NS(file->dir.root) : depthbuf,
-                       file->dirname ? file->dirname : "",
+                       file->dirname ? safe_fname(file->dirname) : "",
                        file->dirname ? "/" : "", NS(file->basename),
                        S_ISDIR(file->mode) ? "/" : "", (int)file->mode,
                        (double)file->length, uidbuf, gidbuf, file->flags);
@@ -1535,12 +1535,19 @@ static void output_flist(struct file_list *flist, const char *whose_list)
 }
 
 
-enum fnc_state { fnc_DIR, fnc_SLASH, fnc_BASE };
+enum fnc_state { fnc_DIR, fnc_SLASH, fnc_BASE, fnc_TRAILING };
 
-/* Compare the names of two file_struct entities, just like strcmp()
- * would do if it were operating on the joined strings.  We assume
- * that there are no 0-length strings.
- */
+/* Compare the names of two file_struct entities, similar to how strcmp()
+ * would do if it were operating on the joined strings.  The only difference
+ * is that, beginning with protocol_version 29, a directory name will always
+ * sort immediately prior to its contents (previously "foo." would sort in
+ * between directory "foo" and "foo/bar").  We do this by assuming that a dir
+ * has a trailing slash for comparison purposes, but only if we aren't about
+ * to match a file of the same name (because we need all identically named
+ * items to match each other).  The dirname component can be an empty string,
+ * but the basename component cannot (and never is in the current codebase).
+ * The basename component may be NULL, in which case it is sorted to the end
+ * of the list (as a removed item). */
 int f_name_cmp(struct file_struct *f1, struct file_struct *f2)
 {
        int dif;
@@ -1590,6 +1597,11 @@ int f_name_cmp(struct file_struct *f1, struct file_struct *f2)
                                c1 = (uchar*)f1->basename;
                                break;
                        case fnc_BASE:
+                               state1 = fnc_TRAILING;
+                               if (protocol_version >= 29 && S_ISDIR(f1->mode))
+                                       c1 = (uchar*)"/";
+                               break;
+                       case fnc_TRAILING:
                                break;
                        }
                }
@@ -1604,8 +1616,13 @@ int f_name_cmp(struct file_struct *f1, struct file_struct *f2)
                                c2 = (uchar*)f2->basename;
                                break;
                        case fnc_BASE:
-                               if (!*c1)
+                               if (state1 == fnc_TRAILING)
                                        return 0;
+                               state2 = fnc_TRAILING;
+                               if (protocol_version >= 29 && S_ISDIR(f2->mode))
+                                       c2 = (uchar*)"/";
+                               break;
+                       case fnc_TRAILING:
                                break;
                        }
                }
@@ -1736,8 +1753,7 @@ void delete_missing(struct file_list *full_list, struct file_list *dir_list,
                        continue;
                mode = dir_list->files[i]->mode;
                if ((j = flist_find(full_list, dir_list->files[i])) < 0
-                   || (delete_during && S_ISDIR(mode)
-                    && !S_ISDIR(full_list->files[j]->mode))) {
+                   || (S_ISDIR(mode) && !S_ISDIR(full_list->files[j]->mode))) {
                        char *f = f_name(dir_list->files[i]);
                        if (make_backups && (backup_dir || !is_backup_file(f))
                          && !S_ISDIR(mode)) {