Added a remote-shell substitute for use in testing and for certain
[rsync/rsync.git] / generator.c
index bbc6923..4963d51 100644 (file)
@@ -7,8 +7,9 @@
  * Copyright (C) 2003-2007 Wayne Davison
  *
  * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -16,8 +17,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
+ * with this program; if not, visit the http://fsf.org website.
  */
 
 #include "rsync.h"
@@ -43,7 +43,6 @@ extern int preserve_specials;
 extern int preserve_hard_links;
 extern int preserve_perms;
 extern int preserve_times;
-extern int omit_dir_times;
 extern int uid_ndx;
 extern int gid_ndx;
 extern int delete_mode;
@@ -113,6 +112,7 @@ static int deldelay_fd = -1;
 static int lull_mod;
 static int dir_tweaking;
 static int need_retouch_dir_times;
+static int need_retouch_dir_perms;
 static const char *solo_file = NULL;
 
 /* For calling delete_item() and delete_dir_contents(). */
@@ -231,7 +231,7 @@ static enum delret delete_dir_contents(char *fname, int flags)
        dirlist = get_dirlist(fname, dlen, 0);
        ret = non_perishable_cnt ? DR_NOT_EMPTY : DR_SUCCESS;
 
-       if (!dirlist->count)
+       if (!dirlist->used)
                goto done;
 
        if (!(flags & DEL_RECURSE)) {
@@ -247,7 +247,7 @@ static enum delret delete_dir_contents(char *fname, int flags)
        /* We do our own recursion, so make delete_item() non-recursive. */
        flags = (flags & ~DEL_RECURSE) | DEL_DIR_IS_EMPTY;
 
-       for (j = dirlist->count; j--; ) {
+       for (j = dirlist->used; j--; ) {
                struct file_struct *fp = dirlist->files[j];
 
                if (fp->flags & FLAG_MOUNT_DIR) {
@@ -459,7 +459,7 @@ static void delete_in_dir(char *fbuf, struct file_struct *file, dev_t *fs_dev)
 
        /* If an item in dirlist is not found in flist, delete it
         * from the filesystem. */
-       for (i = dirlist->count; i--; ) {
+       for (i = dirlist->used; i--; ) {
                struct file_struct *fp = dirlist->files[i];
                if (!F_IS_ACTIVE(fp))
                        continue;
@@ -494,7 +494,7 @@ static void do_delete_pass(void)
        if (dry_run > 1 || list_only)
                return;
 
-       for (j = 0; j < cur_flist->count; j++) {
+       for (j = 0; j < cur_flist->used; j++) {
                struct file_struct *file = cur_flist->sorted[j];
 
                if (!(file->flags & FLAG_XFER_DIR))
@@ -553,7 +553,7 @@ void itemize(const char *fnamecmp, struct file_struct *file, int ndx, int statre
 {
        if (statret >= 0) { /* A from-dest-dir statret can == 1! */
                int keep_time = !preserve_times ? 0
-                   : S_ISDIR(file->mode) ? !omit_dir_times
+                   : S_ISDIR(file->mode) ? preserve_times > 1
                    : !S_ISLNK(file->mode);
 
                if (S_ISREG(file->mode) && F_LENGTH(file) != sxp->st.st_size)
@@ -784,7 +784,7 @@ static int find_fuzzy(struct file_struct *file, struct file_list *dirlist)
        fname_len = strlen(fname);
        fname_suf = find_filename_suffix(fname, fname_len, &fname_suf_len);
 
-       for (j = 0; j < dirlist->count; j++) {
+       for (j = 0; j < dirlist->used; j++) {
                struct file_struct *fp = dirlist->files[j];
                const char *suf, *name;
                int len, suf_len;
@@ -1091,6 +1091,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
 {
        static int missing_below = -1, excluded_below = -1;
        static const char *parent_dirname = "";
+       static struct file_struct *missing_dir = NULL, *excluded_dir = NULL;
        static struct file_list *fuzzy_dirlist = NULL;
        static int need_fuzzy_dirlist = 0;
        struct file_struct *fuzzy_file = NULL;
@@ -1112,14 +1113,17 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
 
        if (server_filter_list.head) {
                if (excluded_below >= 0) {
-                       if (F_DEPTH(file) > excluded_below)
+                       if (F_DEPTH(file) > excluded_below
+                        && (implied_dirs || f_name_has_prefix(file, excluded_dir)))
                                goto skipping;
                        excluded_below = -1;
                }
                if (check_filter(&server_filter_list, fname,
                                 S_ISDIR(file->mode)) < 0) {
-                       if (S_ISDIR(file->mode))
+                       if (S_ISDIR(file->mode)) {
                                excluded_below = F_DEPTH(file);
+                               excluded_dir = file;
+                       }
                  skipping:
                        if (verbose) {
                                rprintf(FINFO,
@@ -1131,7 +1135,8 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
        }
 
        if (missing_below >= 0) {
-               if (F_DEPTH(file) <= missing_below) {
+               if (F_DEPTH(file) <= missing_below
+                || (!implied_dirs && !f_name_has_prefix(file, missing_dir))) {
                        if (dry_run)
                                dry_run--;
                        missing_below = -1;
@@ -1200,6 +1205,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                                if (dry_run)
                                        dry_run++;
                                missing_below = F_DEPTH(file);
+                               missing_dir = file;
                        }
                        file->flags |= FLAG_MISSING_DIR;
                }
@@ -1214,11 +1220,12 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                 * (perhaps recursively) create it. */
                if (statret == 0 && !S_ISDIR(sx.st.st_mode)) {
                        if (delete_item(fname, sx.st.st_mode, "directory", del_opts) != 0)
-                               return;
+                               goto skipping_dir_contents;
                        statret = -1;
                }
                if (dry_run && statret != 0 && missing_below < 0) {
                        missing_below = F_DEPTH(file);
+                       missing_dir = file;
                        dry_run++;
                }
                real_ret = statret;
@@ -1252,9 +1259,11 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                                rsyserr(FERROR, errno,
                                        "recv_generator: mkdir %s failed",
                                        full_fname(fname));
+                         skipping_dir_contents:
                                rprintf(FERROR,
                                    "*** Skipping any contents from this failed directory ***\n");
                                missing_below = F_DEPTH(file);
+                               missing_dir = file;
                                file->flags |= FLAG_MISSING_DIR;
                                goto cleanup;
                        }
@@ -1262,11 +1271,27 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                if (set_file_attrs(fname, file, real_ret ? NULL : &real_sx, NULL, 0)
                    && verbose && code != FNONE && f_out != -1)
                        rprintf(code, "%s/\n", fname);
+
+               /* We need to ensure that the dirs in the transfer have writable
+                * permissions during the time we are putting files within them.
+                * This is then fixed after the transfer is done. */
+#ifdef HAVE_CHMOD
+               if (!am_root && !(file->mode & S_IWUSR) && dir_tweaking) {
+                       mode_t mode = file->mode | S_IWUSR;
+                       if (do_chmod(fname, mode) < 0) {
+                               rsyserr(FERROR, errno,
+                                       "failed to modify permissions on %s",
+                                       full_fname(fname));
+                       }
+                       need_retouch_dir_perms = 1;
+               }
+#endif
+
                if (real_ret != 0 && one_file_system)
                        real_sx.st.st_dev = filesystem_dev;
                if (inc_recurse) {
                        if (one_file_system) {
-                               uint32 *devp = F_DIRDEV_P(file);
+                               uint32 *devp = F_DIR_DEV_P(file);
                                DEV_MAJOR(devp) = major(real_sx.st.st_dev);
                                DEV_MINOR(devp) = minor(real_sx.st.st_dev);
                        }
@@ -1345,8 +1370,10 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                                statret = 1;
                }
 #ifdef SUPPORT_HARD_LINKS
-               if (preserve_hard_links && F_HLINK_NOT_LAST(file))
+               if (preserve_hard_links && F_HLINK_NOT_LAST(file)) {
+                       cur_flist->in_progress++;
                        goto cleanup;
+               }
 #endif
                if (do_symlink(sl, fname) != 0) {
                        rsyserr(FERROR, errno, "symlink %s -> \"%s\" failed",
@@ -1422,8 +1449,10 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                                statret = 1;
                }
 #ifdef SUPPORT_HARD_LINKS
-               if (preserve_hard_links && F_HLINK_NOT_LAST(file))
+               if (preserve_hard_links && F_HLINK_NOT_LAST(file)) {
+                       cur_flist->in_progress++;
                        goto cleanup;
+               }
 #endif
                if (verbose > 2) {
                        rprintf(FINFO, "mknod(%s, 0%o, [%ld,%ld])\n",
@@ -1542,8 +1571,10 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
 
        if (statret != 0) {
 #ifdef SUPPORT_HARD_LINKS
-               if (preserve_hard_links && F_HLINK_NOT_LAST(file))
+               if (preserve_hard_links && F_HLINK_NOT_LAST(file)) {
+                       cur_flist->in_progress++;
                        goto cleanup;
+               }
 #endif
                if (stat_errno == ENOENT)
                        goto notify_others;
@@ -1552,7 +1583,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                goto cleanup;
        }
 
-       if (append_mode > 0 && sx.st.st_size > F_LENGTH(file))
+       if (append_mode > 0 && sx.st.st_size >= F_LENGTH(file))
                goto cleanup;
 
        if (fnamecmp_type <= FNAMECMP_BASIS_DIR_HIGH)
@@ -1605,8 +1636,10 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
          pretend_missing:
                /* pretend the file didn't exist */
 #ifdef SUPPORT_HARD_LINKS
-               if (preserve_hard_links && F_HLINK_NOT_LAST(file))
+               if (preserve_hard_links && F_HLINK_NOT_LAST(file)) {
+                       cur_flist->in_progress++;
                        goto cleanup;
+               }
 #endif
                statret = real_ret = -1;
                goto notify_others;
@@ -1727,7 +1760,7 @@ static void touch_up_dirs(struct file_list *flist, int ndx)
 
        if (ndx < 0) {
                start = 0;
-               end = flist->count - 1;
+               end = flist->used - 1;
        } else
                start = end = ndx;
 
@@ -1735,15 +1768,15 @@ static void touch_up_dirs(struct file_list *flist, int ndx)
         * transfer and/or re-set any tweaked modified-time values. */
        for (i = start; i <= end; i++, counter++) {
                file = flist->files[i];
+               if (!S_ISDIR(file->mode))
+                       continue;
                if (verbose > 3) {
                        fname = f_name(file, NULL);
                        rprintf(FINFO, "touch_up_dirs: %s (%d)\n",
                                NS(fname), i);
                }
-               if (!F_IS_ACTIVE(file) || !S_ISDIR(file->mode)
-                || file->flags & FLAG_MISSING_DIR)
-                       continue;
-               if (!need_retouch_dir_times && file->mode & S_IWUSR)
+               if (!F_IS_ACTIVE(file) || file->flags & FLAG_MISSING_DIR
+                || (!need_retouch_dir_times && file->mode & S_IWUSR))
                        continue;
                fname = f_name(file, NULL);
                if (!(file->mode & S_IWUSR))
@@ -1768,7 +1801,7 @@ void check_for_finished_files(int itemizing, enum logcode code, int check_redo)
        while (preserve_hard_links && (ndx = get_hlink_num()) != -1) {
                flist = flist_for_ndx(ndx);
                assert(flist != NULL);
-               file = flist->files[ndx];
+               file = flist->files[ndx - flist->ndx_start];
                assert(file->flags & FLAG_HLINKED);
                finish_hard_link(file, f_name(file, fbuf), NULL, itemizing, code, -1);
        }
@@ -1822,13 +1855,6 @@ void check_for_finished_files(int itemizing, enum logcode code, int check_redo)
                        maybe_flush_socket(1);
                }
 
-               if (delete_during == 2 || !dir_tweaking) {
-                       /* Skip directory touch-up. */
-               } else if (first_flist->ndx_start != 0)
-                       touch_up_dirs(dir_flist, first_flist->parent_ndx);
-               else if (relative_paths && implied_dirs)
-                       touch_up_dirs(first_flist, -1);
-
                flist_free(first_flist); /* updates first_flist */
        }
 }
@@ -1839,7 +1865,6 @@ void generate_files(int f_out, const char *local_name)
        char fbuf[MAXPATHLEN];
        int itemizing;
        enum logcode code;
-       int need_retouch_dir_perms = 0;
        int save_do_progress = do_progress;
 
        if (protocol_version >= 29) {
@@ -1861,13 +1886,13 @@ void generate_files(int f_out, const char *local_name)
        }
        solo_file = local_name;
        dir_tweaking = !(list_only || solo_file || dry_run);
-       need_retouch_dir_times = preserve_times && !omit_dir_times;
+       need_retouch_dir_times = preserve_times > 1;
        lull_mod = allowed_lull * 5;
 
        if (verbose > 2)
                rprintf(FINFO, "generator starting pid=%ld\n", (long)getpid());
 
-       if (delete_before && !solo_file && cur_flist->count > 0)
+       if (delete_before && !solo_file && cur_flist->used > 0)
                do_delete_pass();
        if (delete_during == 2) {
                deldelay_size = BIGPATHBUFLEN * 4;
@@ -1895,16 +1920,25 @@ void generate_files(int f_out, const char *local_name)
        dflt_perms = (ACCESSPERMS & ~orig_umask);
 
        do {
-               if (inc_recurse && delete_during && cur_flist->ndx_start) {
+#ifdef SUPPORT_HARD_LINKS
+               if (inc_recurse && preserve_hard_links)
+                       match_hard_links(cur_flist);
+#endif
+               if (inc_recurse && cur_flist->ndx_start) {
                        struct file_struct *fp = dir_flist->files[cur_flist->parent_ndx];
-                       if (BITS_SETnUNSET(fp->flags, FLAG_XFER_DIR, FLAG_MISSING_DIR)) {
-                               dev_t dirdev;
-                               if (one_file_system) {
-                                       uint32 *devp = F_DIRDEV_P(fp);
-                                       dirdev = MAKEDEV(DEV_MAJOR(devp), DEV_MINOR(devp));
-                               } else
-                                       dirdev = MAKEDEV(0, 0);
-                               delete_in_dir(f_name(fp, fbuf), fp, &dirdev);
+                       f_name(fp, fbuf);
+                       ndx = cur_flist->ndx_start - 1;
+                       recv_generator(fbuf, fp, ndx, itemizing, code, f_out);
+                       if (delete_during && dry_run < 2) {
+                               if (BITS_SETnUNSET(fp->flags, FLAG_XFER_DIR, FLAG_MISSING_DIR)) {
+                                       dev_t dirdev;
+                                       if (one_file_system) {
+                                               uint32 *devp = F_DIR_DEV_P(fp);
+                                               dirdev = MAKEDEV(DEV_MAJOR(devp), DEV_MINOR(devp));
+                                       } else
+                                               dirdev = MAKEDEV(0, 0);
+                                       delete_in_dir(f_name(fp, fbuf), fp, &dirdev);
+                               }
                        }
                }
                for (i = cur_flist->low; i <= cur_flist->high; i++) {
@@ -1913,6 +1947,14 @@ void generate_files(int f_out, const char *local_name)
                        if (!F_IS_ACTIVE(file))
                                continue;
 
+                       if (inc_recurse && S_ISDIR(file->mode)) {
+                               /* Regular dirs are at the end, so we can stop. */
+                               if (F_DEPTH(file))
+                                       break;
+                               /* A dot-dir is at the start, so just skip it. */
+                               continue;
+                       }
+
 #ifdef ICONV_OPTION
                        if (ic_ndx)
                                ndx = F_NDX(file);
@@ -1926,24 +1968,6 @@ void generate_files(int f_out, const char *local_name)
                                f_name(file, fbuf);
                        recv_generator(fbuf, file, ndx, itemizing, code, f_out);
 
-                       /* We need to ensure that any dirs we create have
-                        * writeable permissions during the time we are putting
-                        * files within them.  This is then fixed after the
-                        * transfer is done. */
-#ifdef HAVE_CHMOD
-                       if (!am_root && S_ISDIR(file->mode)
-                        && !(file->mode & S_IWUSR) && dir_tweaking) {
-                               mode_t mode = file->mode | S_IWUSR;
-                               const char *fname = solo_file ? solo_file : fbuf;
-                               if (do_chmod(fname, mode) < 0) {
-                                       rsyserr(FERROR, errno,
-                                           "failed to modify permissions on %s",
-                                           full_fname(fname));
-                               }
-                               need_retouch_dir_perms = 1;
-                       }
-#endif
-
                        check_for_finished_files(itemizing, code, 0);
 
                        if (allowed_lull && !(i % lull_mod))
@@ -2012,8 +2036,7 @@ void generate_files(int f_out, const char *local_name)
        if (delete_after && !solo_file && file_total > 0)
                do_delete_pass();
 
-       if ((need_retouch_dir_perms || need_retouch_dir_times)
-        && dir_tweaking && (!inc_recurse || delete_during == 2))
+       if ((need_retouch_dir_perms || need_retouch_dir_times) && dir_tweaking)
                touch_up_dirs(dir_flist, -1);
 
        if (max_delete >= 0 && deletion_count > max_delete) {