Added the --delete-missing-args option to delete specified
authorWayne Davison <wayned@samba.org>
Sat, 28 Feb 2009 17:22:51 +0000 (09:22 -0800)
committerWayne Davison <wayned@samba.org>
Sat, 28 Feb 2009 17:25:26 +0000 (09:25 -0800)
files on the receiver that don't exist on the sender.

NEWS
flist.c
generator.c
options.c
rsync.yo

diff --git a/NEWS b/NEWS
index bc44c5e..ee0661b 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -34,6 +34,10 @@ Changes since 3.0.4:
       control over what is output.  Added an extra type of --progress output
       using --info=progress2.
 
       control over what is output.  Added an extra type of --progress output
       using --info=progress2.
 
+    - Added the --delete-missing option to delete user-specified files on the
+      receiver that are missing on the sender (normally the absence of user-
+      specified files generates an error).
+
     - Added a "T" (terabyte) category to the --human-readable size suffixes.
 
     - Enhanced the --stats output: 1) to mention how many files were created
     - Added a "T" (terabyte) category to the --human-readable size suffixes.
 
     - Enhanced the --stats output: 1) to mention how many files were created
diff --git a/flist.c b/flist.c
index 90c0ce5..7d01c9c 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 preserve_hard_links;
 extern int preserve_devices;
 extern int preserve_specials;
+extern int delete_missing_args;
 extern int uid_ndx;
 extern int gid_ndx;
 extern int eol_nulls;
 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++;
                        stats.num_symlinks++;
                else if (IS_DEVICE(file->mode))
                        stats.num_devices++;
-               else
+               else if (IS_SPECIAL(file->mode))
                        stats.num_specials++;
                xflags = 0;
        }
                        stats.num_specials++;
                xflags = 0;
        }
@@ -1124,8 +1125,10 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
        if (sanitize_paths)
                sanitize_path(thisname, thisname, "", 0, SP_DEFAULT);
 
        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;
                *linkname = '\0'; /* make IBM code checker happy */
        } else if (readlink_stat(thisname, &st, linkname) != 0) {
                int save_errno = errno;
@@ -1166,6 +1169,11 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
                                full_fname(thisname));
                }
                return NULL;
                                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)
        }
 
        if (filter_level == NO_FILTERS)
@@ -2169,10 +2177,15 @@ 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))) {
                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 && delete_missing_args) {
+                               /* Rsync will treat a mode of 0 as deleted. */
+                               memset(&st, 0, sizeof st);
+                       } else {
+                               io_error |= IOERR_GENERAL;
+                               rsyserr(FERROR_XFER, errno, "link_stat %s failed",
+                                       full_fname(fbuf));
+                               continue;
+                       }
                }
 
                /* A dot-dir should not be excluded! */
                }
 
                /* A dot-dir should not be excluded! */
index cd67f0f..a9f3374 100644 (file)
@@ -50,6 +50,7 @@ extern int delete_mode;
 extern int delete_before;
 extern int delete_during;
 extern int delete_after;
 extern int delete_before;
 extern int delete_during;
 extern int delete_after;
+extern int delete_missing_args;
 extern int msgdone_cnt;
 extern int ignore_errors;
 extern int remove_source_files;
 extern int msgdone_cnt;
 extern int ignore_errors;
 extern int remove_source_files;
@@ -1172,6 +1173,12 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                stat_errno = errno;
        }
 
                stat_errno = errno;
        }
 
+       if (delete_missing_args && file->mode == 0) {
+               if (statret == 0)
+                       delete_item(fname, sx.st.st_mode, del_opts);
+               return;
+       }
+
        if (ignore_non_existing > 0 && statret == -1 && stat_errno == ENOENT) {
                if (is_dir) {
                        if (is_dir < 0)
        if (ignore_non_existing > 0 && statret == -1 && stat_errno == ENOENT) {
                if (is_dir) {
                        if (is_dir < 0)
index 5f8018f..51697ad 100644 (file)
--- a/options.c
+++ b/options.c
@@ -69,6 +69,7 @@ int delete_during = 0;
 int delete_before = 0;
 int delete_after = 0;
 int delete_excluded = 0;
 int delete_before = 0;
 int delete_after = 0;
 int delete_excluded = 0;
+int delete_missing_args = 0;
 int remove_source_files = 0;
 int one_file_system = 0;
 int protocol_version = PROTOCOL_VERSION;
 int remove_source_files = 0;
 int one_file_system = 0;
 int protocol_version = PROTOCOL_VERSION;
@@ -718,6 +719,7 @@ void usage(enum logcode F)
   rprintf(F,"     --delete-delay          find deletions during, delete after\n");
   rprintf(F,"     --delete-after          receiver deletes after transfer, not during\n");
   rprintf(F,"     --delete-excluded       also delete excluded files from destination dirs\n");
   rprintf(F,"     --delete-delay          find deletions during, delete after\n");
   rprintf(F,"     --delete-after          receiver deletes after transfer, not during\n");
   rprintf(F,"     --delete-excluded       also delete excluded files from destination dirs\n");
+  rprintf(F,"     --delete-missing-args   receiver deletes each missing source arg\n");
   rprintf(F,"     --ignore-errors         delete even if there are I/O errors\n");
   rprintf(F,"     --force                 force deletion of directories even if not empty\n");
   rprintf(F,"     --max-delete=NUM        don't delete more than NUM files\n");
   rprintf(F,"     --ignore-errors         delete even if there are I/O errors\n");
   rprintf(F,"     --force                 force deletion of directories even if not empty\n");
   rprintf(F,"     --max-delete=NUM        don't delete more than NUM files\n");
@@ -908,6 +910,7 @@ static struct poptOption long_options[] = {
   {"delete-delay",     0,  POPT_ARG_VAL,    &delete_during, 2, 0, 0 },
   {"delete-after",     0,  POPT_ARG_NONE,   &delete_after, 0, 0, 0 },
   {"delete-excluded",  0,  POPT_ARG_NONE,   &delete_excluded, 0, 0, 0 },
   {"delete-delay",     0,  POPT_ARG_VAL,    &delete_during, 2, 0, 0 },
   {"delete-after",     0,  POPT_ARG_NONE,   &delete_after, 0, 0, 0 },
   {"delete-excluded",  0,  POPT_ARG_NONE,   &delete_excluded, 0, 0, 0 },
+  {"delete-missing-args",0,POPT_ARG_NONE,   &delete_missing_args, 0, 0, 0 },
   {"remove-sent-files",0,  POPT_ARG_VAL,    &remove_source_files, 2, 0, 0 }, /* deprecated */
   {"remove-source-files",0,POPT_ARG_VAL,    &remove_source_files, 1, 0, 0 },
   {"force",            0,  POPT_ARG_VAL,    &force_delete, 1, 0, 0 },
   {"remove-sent-files",0,  POPT_ARG_VAL,    &remove_source_files, 2, 0, 0 }, /* deprecated */
   {"remove-source-files",0,POPT_ARG_VAL,    &remove_source_files, 1, 0, 0 },
   {"force",            0,  POPT_ARG_VAL,    &force_delete, 1, 0, 0 },
@@ -1922,7 +1925,7 @@ int parse_arguments(int *argc_p, const char ***argv_p)
                return 0;
        }
 
                return 0;
        }
 
-       if (delete_mode && refused_delete) {
+       if (refused_delete && (delete_mode || delete_missing_args)) {
                create_refuse_error(refused_delete);
                return 0;
        }
                create_refuse_error(refused_delete);
                return 0;
        }
@@ -2479,6 +2482,9 @@ void server_options(char **args, int *argc_p)
                }
        }
 
                }
        }
 
+       if (delete_missing_args)
+               args[ac++] = "--delete-missing-args";
+
        if (modify_window_set) {
                if (asprintf(&arg, "--modify-window=%d", modify_window) < 0)
                        goto oom;
        if (modify_window_set) {
                if (asprintf(&arg, "--modify-window=%d", modify_window) < 0)
                        goto oom;
index afa5061..2ff224f 100644 (file)
--- a/rsync.yo
+++ b/rsync.yo
@@ -375,6 +375,7 @@ to the detailed description below for a complete description.  verb(
      --delete-delay          find deletions during, delete after
      --delete-after          receiver deletes after transfer, not before
      --delete-excluded       also delete excluded files from dest dirs
      --delete-delay          find deletions during, delete after
      --delete-after          receiver deletes after transfer, not before
      --delete-excluded       also delete excluded files from dest dirs
+     --delete-missing-args   receiver deletes each missing source arg
      --ignore-errors         delete even if there are I/O errors
      --force                 force deletion of dirs even if not empty
      --max-delete=NUM        don't delete more than NUM files
      --ignore-errors         delete even if there are I/O errors
      --force                 force deletion of dirs even if not empty
      --max-delete=NUM        don't delete more than NUM files
@@ -1263,6 +1264,13 @@ this way on the receiver, and for a way to protect files from
 bf(--delete-excluded).
 See bf(--delete) (which is implied) for more details on file-deletion.
 
 bf(--delete-excluded).
 See bf(--delete) (which is implied) for more details on file-deletion.
 
+dit(bf(--delete-missing-args)) Any source arg that is found to me missing is
+treated as a request to delete the file by the receiver rather than the sender
+generating a missing-file error.  Does not affect vanished files discovered
+through recursive scanning of directories, just the args mentioned on the
+command-line or in the bf(--files-from) list.  Can be used with or without any
+other type of delete processing.
+
 dit(bf(--ignore-errors)) Tells bf(--delete) to go ahead and delete files
 even when there are I/O errors.
 
 dit(bf(--ignore-errors)) Tells bf(--delete) to go ahead and delete files
 even when there are I/O errors.