[patch] add "--ignore" option

Matthew Kirkwood matthew at hairy.beasts.org
Tue Feb 1 17:16:59 GMT 2005


On Mon, 31 Jan 2005, Wayne Davison wrote:

> > The patch below adds a "--ignore" option to rsync, which means
> > "--exclude-but-dont-delete-even-if-we-specified--delete-excluded".
>
> Firstly, let me compliment you on the patch -- it was very complete.
> (Aside:  it helps to attach the patch instead of paste it into the
> email to avoid line wrapping.)

Thanks!  (I've attached a copy which hopefully isn't wrapped
or tab-damaged.)

> One alternative to this new option is to have a separate set of excludes
> for the sending side (which decides what files gets sent) and for the
> receiving side (which decides what files get deleted) and then avoid
> using --delete-excluded.
[..]
>     -f "-s *.no_send" -f "-r *.no_delete" -f "- *.no_send+no_delete"
>
> Something like that would be fairly easy to add.  What do you think?

Does that give any additional useful behaviour?  The syntax
seems a little painful.  Additionally, isn't it the case
that, until now, the sender never needed to pass options to
the receiver?

If you prefer it this way, I'll try to hack it in.  I would
really like to see this functionality in storck rsync.

Matthew.
-------------- next part --------------
Hi,

The patch below adds a "--ignore" option to rsync, which means
"--exclude-but-dont-delete-even-if-we-specified--delete-excluded".

I need this for a few tasks, the simplest of which is to have rsync resist
trying to delete NetApp filers' ".snapshot" directories.

The change is fairly simple (the boolean filter returns become tri-state),
and works for me both locally and across rsh, but it may not be "right".
I'm not wedded to the implementation, but I do need the functionality,
and I'm happy to be guided in the right direction.

I am subscribed to this list from my personal email account (copied),
but I'd be grateful to remain cc:ed at this address on any follow-up.

Cheers,
Matthew.


diff -ur ../rsync-HEAD-20050125-1221GMT.orig/exclude.c ./exclude.c
--- ../rsync-HEAD-20050125-1221GMT.orig/exclude.c	Tue Jan 25 12:21:14 2005
+++ ./exclude.c	Thu Jan 27 16:52:33 2005
@@ -117,7 +117,8 @@
 		rprintf(FINFO, "[%s] make_filter(%.*s, %s%s)\n",
 			who_am_i(), (int)pat_len, pat,
 			mflags & MATCHFLG_PERDIR_MERGE ? "per-dir-merge"
-			: mflags & MATCHFLG_INCLUDE ? "include" : "exclude",
+			: mflags & MATCHFLG_INCLUDE ? "include"
+			: mflags & MATCHFLG_IGNORE ? "ignore" : "exclude",
 			listp->debug_type);
 	}
 
@@ -563,9 +564,10 @@
 	 * case we add it back in here. */
 
 	if (verbose >= 2) {
-		rprintf(FINFO, "[%s] %scluding %s %s because of pattern %s%s%s\n",
+		rprintf(FINFO, "[%s] %sing %s %s because of pattern %s%s%s\n",
 			who_am_i(),
-			ent->match_flags & MATCHFLG_INCLUDE ? "in" : "ex",
+			ent->match_flags & MATCHFLG_INCLUDE ? "includ"
+				: ent->match_flags & MATCHFLG_IGNORE ? "ignor" : "exclud",
 			name_is_dir ? "directory" : "file", name, ent->pattern,
 			ent->match_flags & MATCHFLG_DIRECTORY ? "/" : "", type);
 	}
@@ -573,29 +575,34 @@
 
 
 /*
- * Return -1 if file "name" is defined to be excluded by the specified
- * exclude list, 1 if it is included, and 0 if it was not matched.
+ * Return M_EXCLUDE if file "name" is defined to be excluded by the specified
+ * exclude list, M_INCLUDE if it is included, M_IGNORE if it is flagged to be
+ * ignored, and M_NOMATCH (aka 0) if it was not matched.
  */
-int check_filter(struct filter_list_struct *listp, char *name, int name_is_dir)
+enum matchtype check_filter(struct filter_list_struct *listp, char *name, int name_is_dir)
 {
 	struct filter_struct *ent;
 
 	for (ent = listp->head; ent; ent = ent->next) {
 		if (ent->match_flags & MATCHFLG_PERDIR_MERGE) {
-			int rc = check_filter(ent->u.mergelist, name,
-					      name_is_dir);
-			if (rc)
+			enum matchtype rc = check_filter(ent->u.mergelist, name,
+							 name_is_dir);
+			if (rc != M_NOMATCH)
 				return rc;
 			continue;
 		}
 		if (rule_matches(name, ent, name_is_dir)) {
 			report_filter_result(name, ent, name_is_dir,
 					      listp->debug_type);
-			return ent->match_flags & MATCHFLG_INCLUDE ? 1 : -1;
+			if (ent->match_flags & MATCHFLG_INCLUDE)
+				return M_INCLUDE;
+			if (ent->match_flags & MATCHFLG_IGNORE)
+				return M_IGNORE;
+			return M_EXCLUDE;
 		}
 	}
 
-	return 0;
+	return M_NOMATCH;
 }
 
 
@@ -625,7 +632,7 @@
 		return NULL;
 
 	/* Figure out what kind of a filter rule "s" is pointing at. */
-	if (!(xflags & (XFLG_DEF_INCLUDE | XFLG_DEF_EXCLUDE))) {
+	if (!(xflags & (XFLG_DEF_INCLUDE | XFLG_DEF_EXCLUDE | XFLG_DEF_IGNORE))) {
 		char *mods = "";
 		switch (*s) {
 		case ':':
@@ -695,6 +702,8 @@
 	} else {
 		if (xflags & XFLG_DEF_INCLUDE)
 			mflags |= MATCHFLG_INCLUDE;
+		else if (xflags & XFLG_DEF_IGNORE)
+			mflags |= MATCHFLG_IGNORE;
 		if (*s == '!')
 			mflags |= MATCHFLG_CLEAR_LIST; /* Tentative! */
 	}
@@ -712,7 +721,7 @@
 		len = strlen(s);
 
 	if (mflags & MATCHFLG_CLEAR_LIST) {
-		if (!(xflags & (XFLG_DEF_INCLUDE | XFLG_DEF_EXCLUDE)) && len) {
+		if (!(xflags & (XFLG_DEF_INCLUDE | XFLG_DEF_EXCLUDE | XFLG_DEF_IGNORE)) && len) {
 			rprintf(FERROR,
 				"'!' rule has trailing characters: %s\n", p);
 			exit_cleanup(RERR_SYNTAX);
@@ -794,6 +803,8 @@
 					continue;
 				if (mflags & MATCHFLG_INCLUDE)
 					flgs |= XFLG_DEF_INCLUDE;
+				else if (mflags & MATCHFLG_IGNORE)
+					flgs |= XFLG_DEF_IGNORE;
 				else if (mflags & MATCHFLG_NO_PREFIXES)
 					flgs |= XFLG_DEF_EXCLUDE;
 				add_filter_file(listp, p, flgs);
diff -ur ../rsync-HEAD-20050125-1221GMT.orig/flist.c ./flist.c
--- ../rsync-HEAD-20050125-1221GMT.orig/flist.c	Tue Jan 25 12:21:14 2005
+++ ./flist.c	Thu Jan 27 17:12:25 2005
@@ -223,8 +223,9 @@
 /* This function is used to check if a file should be included/excluded
  * from the list of files based on its name and type etc.  The value of
  * filter_level is set to either SERVER_FILTERS or ALL_FILTERS. */
-static int is_excluded(char *fname, int is_dir, int filter_level)
+static enum matchtype is_excluded(char *fname, int is_dir, int filter_level)
 {
+	enum matchtype r;
 #if 0 /* This currently never happens, so avoid a useless compare. */
 	if (filter_level == NO_FILTERS)
 		return 0;
@@ -241,14 +242,19 @@
 		}
 	}
 	if (server_filter_list.head
-	    && check_filter(&server_filter_list, fname, is_dir) < 0)
-		return 1;
-	if (filter_level != ALL_FILTERS)
-		return 0;
+	    && (r = check_filter(&server_filter_list, fname, is_dir)) != M_NOMATCH)
+		return r;
+//	if (filter_level != ALL_FILTERS)
+//		return M_NOMATCH;
 	if (filter_list.head
-	    && check_filter(&filter_list, fname, is_dir) < 0)
-		return 1;
-	return 0;
+	    && (r = check_filter(&filter_lit, fname, is_dir)) != M_NOMATCH) {
+		if (filter_level != ALL_FILTERS) {
+			if (r == M_IGNORE)
+				return r;
+		} else
+			return r;
+	}
+	return M_NOMATCH;
 }
 
 /* used by the one_file_system code */
@@ -786,9 +792,10 @@
 	if (readlink_stat(thisname, &st, linkname) != 0) {
 		int save_errno = errno;
 		/* See if file is excluded before reporting an error. */
-		if (filter_level != NO_FILTERS
-		    && is_excluded(thisname, 0, filter_level))
-			return NULL;
+		if (filter_level != NO_FILTERS) {
+			if (is_excluded(thisname, 0, filter_level) == M_EXCLUDE)
+				return NULL;
+		}
 		if (save_errno == ENOENT) {
 #if SUPPORT_LINKS
 			/* Avoid "vanished" error if symlink points nowhere. */
@@ -830,8 +837,12 @@
 	    && S_ISDIR(st.st_mode))
 		flags |= FLAG_MOUNT_POINT;
 
-	if (is_excluded(thisname, S_ISDIR(st.st_mode) != 0, filter_level))
-		return NULL;
+	{
+	    enum matchtype m = is_excluded(thisname, S_ISDIR(st.st_mode) != 0, filter_level);
+		if ((m == M_EXCLUDE) || (m == M_IGNORE)) {
+			return NULL;
+		}
+	}
 
 	if (lp_ignore_nonreadable(module_id)) {
 #if SUPPORT_LINKS
@@ -981,7 +992,8 @@
 
 	/* f is set to -1 when calculating deletion file list */
 	file = make_file(fname, flist,
-	    f == -1 && delete_excluded? SERVER_FILTERS : ALL_FILTERS);
+		f == -1 && delete_excluded? SERVER_FILTERS : ALL_FILTERS);
+
 
 	if (!file)
 		return;
diff -ur ../rsync-HEAD-20050125-1221GMT.orig/generator.c ./generator.c
--- ../rsync-HEAD-20050125-1221GMT.orig/generator.c	Tue Jan 25 12:21:14 2005
+++ ./generator.c	Thu Jan 27 16:54:18 2005
@@ -256,7 +256,7 @@
 
 	if (server_filter_list.head
 	    && check_filter(&server_filter_list, fname,
-			    S_ISDIR(file->mode)) < 0) {
+			    S_ISDIR(file->mode)) == M_EXCLUDE) {
 		if (verbose) {
 			rprintf(FINFO, "skipping server-excluded file \"%s\"\n",
 				safe_fname(fname));
diff -ur ../rsync-HEAD-20050125-1221GMT.orig/options.c ./options.c
--- ../rsync-HEAD-20050125-1221GMT.orig/options.c	Tue Jan 25 12:21:14 2005
+++ ./options.c	Mon Jan 31 14:28:25 2005
@@ -307,6 +307,8 @@
   rprintf(F,"     --exclude-from=FILE     exclude patterns listed in FILE\n");
   rprintf(F,"     --include=PATTERN       don't exclude files matching PATTERN\n");
   rprintf(F,"     --include-from=FILE     don't exclude patterns listed in FILE\n");
+  rprintf(F,"     --ignore=PATTERN        don't do anything with files matching PATTERN\n");
+  rprintf(F,"     --ignore-from=FILE      don't do anything with patterns listed in FILE\n");
   rprintf(F,"     --files-from=FILE       read FILE for list of source-file names\n");
   rprintf(F," -0, --from0                 all *-from file lists are delimited by nulls\n");
   rprintf(F,"     --version               print version number\n");
@@ -336,6 +338,7 @@
       OPT_FILTER, OPT_COMPARE_DEST, OPT_COPY_DEST, OPT_LINK_DEST,
       OPT_INCLUDE, OPT_INCLUDE_FROM, OPT_MODIFY_WINDOW,
       OPT_READ_BATCH, OPT_WRITE_BATCH, OPT_TIMEOUT, OPT_MAX_SIZE,
+	  OPT_IGNORE, OPT_IGNORE_FROM,
       OPT_REFUSED_BASE = 9000};
 
 static struct poptOption long_options[] = {
@@ -359,8 +362,10 @@
   {"filter",          'f', POPT_ARG_STRING, 0, OPT_FILTER, 0, 0 },
   {"exclude",          0,  POPT_ARG_STRING, 0, OPT_EXCLUDE, 0, 0 },
   {"include",          0,  POPT_ARG_STRING, 0, OPT_INCLUDE, 0, 0 },
+  {"ignore" ,          0,  POPT_ARG_STRING, 0, OPT_IGNORE, 0, 0 },
   {"exclude-from",     0,  POPT_ARG_STRING, 0, OPT_EXCLUDE_FROM, 0, 0 },
   {"include-from",     0,  POPT_ARG_STRING, 0, OPT_INCLUDE_FROM, 0, 0 },
+  {"ignore-from",      0,  POPT_ARG_STRING, 0, OPT_IGNORE_FROM, 0, 0 },
   {"safe-links",       0,  POPT_ARG_NONE,   &safe_symlinks, 0, 0, 0 },
   {"help",            'h', POPT_ARG_NONE,   0, 'h', 0, 0 },
   {"backup",          'b', POPT_ARG_NONE,   &make_backups, 0, 0, 0 },
@@ -656,20 +661,26 @@
 				   XFLG_DEF_INCLUDE);
 			break;
 
+		case OPT_IGNORE:
+			add_filter(&filter_list, poptGetOptArg(pc),
+				   XFLG_DEF_IGNORE);
+			break;
+
 		case OPT_EXCLUDE_FROM:
 		case OPT_INCLUDE_FROM:
+		case OPT_IGNORE_FROM:
 			arg = poptGetOptArg(pc);
 			if (sanitize_paths)
 				arg = sanitize_path(NULL, arg, NULL, 0);
 			if (server_filter_list.head) {
 				char *cp = (char *)arg;
 				clean_fname(cp, 1);
-				if (check_filter(&server_filter_list, cp, 0) < 0)
+				if (check_filter(&server_filter_list, cp, 0) == M_EXCLUDE)
 					goto options_rejected;
 			}
 			add_filter_file(&filter_list, arg, XFLG_FATAL_ERRORS
-				| (opt == OPT_INCLUDE_FROM ? XFLG_DEF_INCLUDE
-							   : XFLG_DEF_EXCLUDE));
+				| (opt == OPT_INCLUDE_FROM ? XFLG_DEF_INCLUDE :
+				   opt == OPT_EXCLUDE_FROM ? XFLG_DEF_EXCLUDE : XFLG_DEF_IGNORE));
 			break;
 
 		case 'h':
@@ -926,28 +937,28 @@
 		int i;
 		if (tmpdir) {
 			clean_fname(tmpdir, 1);
-			if (check_filter(elp, tmpdir, 1) < 0)
+			if (check_filter(elp, tmpdir, 1) == M_EXCLUDE)
 				goto options_rejected;
 		}
 		if (partial_dir) {
 			clean_fname(partial_dir, 1);
-			if (check_filter(elp, partial_dir, 1) < 0)
+			if (check_filter(elp, partial_dir, 1) == M_EXCLUDE)
 				goto options_rejected;
 		}
 		for (i = 0; i < basis_dir_cnt; i++) {
 			clean_fname(basis_dir[i], 1);
-			if (check_filter(elp, basis_dir[i], 1) < 0)
+			if (check_filter(elp, basis_dir[i], 1) == M_EXCLUDE)
 				goto options_rejected;
 		}
 		if (backup_dir) {
 			clean_fname(backup_dir, 1);
-			if (check_filter(elp, backup_dir, 1) < 0)
+			if (check_filter(elp, backup_dir, 1) == M_EXCLUDE)
 				goto options_rejected;
 		}
 	}
 	if (server_filter_list.head && files_from) {
 		clean_fname(files_from, 1);
-		if (check_filter(&server_filter_list, files_from, 0) < 0) {
+		if (check_filter(&server_filter_list, files_from, 0) == M_EXCLUDE) {
 		    options_rejected:
 			snprintf(err_buf, sizeof err_buf,
 			    "Your options have been rejected by the server.\n");
@@ -1218,6 +1229,24 @@
 			args[ac++] = "--force";
 	}
 
+	{
+		struct filter_struct *lp;
+		for(lp = filter_list.head; lp; lp = lp->next) {
+	--ignore='%s'", lp->pattern) < 0)
+					goto oom;
+				args[ac++] = arg;
+			}
+		}
+		for(lp = server_filter_list.head; lp; lp = lp->next) {
+			if(lp->match_flags & MATCHFLG_IGNORE) {
+				if (asprintf(&arg, "--ignore='%s'", lp->pattern) < 0)
+					goto oom;
+				args[ac++] = arg;
+			}
+		}
+	}
+
 	if (size_only)
 		args[ac++] = "--size-only";
 
diff -ur ../rsync-HEAD-20050125-1221GMT.orig/proto.h ./proto.h
--- ../rsync-HEAD-20050125-1221GMT.orig/proto.h	Tue Jan 25 12:21:14 2005
+++ ./proto.h	Thu Jan 27 16:54:02 2005
@@ -47,7 +47,8 @@
 void set_filter_dir(const char *dir, unsigned int dirlen);
 void *push_local_filters(const char *dir, unsigned int dirlen);
 void pop_local_filters(void *mem);
-int check_filter(struct filter_list_struct *listp, char *name, int name_is_dir);
+enum matchtype { M_NOMATCH=0, M_INCLUDE, M_EXCLUDE, M_IGNORE };
+enum matchtype check_filter(struct filter_list_struct *listp, char *name, int name_is_dir);
 void add_filter(struct filter_list_struct *listp, const char *pattern,
 		int xflags);
 void add_filter_file(struct filter_list_struct *listp, const char *fname,
diff -ur ../rsync-HEAD-20050125-1221GMT.orig/receiver.c ./receiver.c
--- ../rsync-HEAD-20050125-1221GMT.orig/receiver.c	Tue Jan 25 12:21:14 2005
+++ ./receiver.c	Thu Jan 27 16:24:26 2005
@@ -362,7 +362,7 @@
 
 		if (server_filter_list.head
 		    && check_filter(&server_filter_list, fname,
-				     S_ISDIR(file->mode)) < 0) {
+				     S_ISDIR(file->mode)) == M_EXCLUDE) {
 			rprintf(FERROR, "attempt to hack rsync failed.\n");
 			exit_cleanup(RERR_PROTOCOL);
 		}
@@ -556,3 +556,4 @@
 
 	return 0;
 }
+
diff -ur ../rsync-HEAD-20050125-1221GMT.orig/rsync.h ./rsync.h
--- ../rsync-HEAD-20050125-1221GMT.orig/rsync.h	Tue Jan 25 12:21:14 2005
+++ ./rsync.h	Thu Jan 27 14:47:03 2005
@@ -114,6 +114,7 @@
 #define XFLG_DIRECTORY	 	(1<<4)
 #define XFLG_NO_PREFIXES 	(1<<5)
 #define XFLG_ABS_PATH	 	(1<<6)
+#define XFLG_DEF_IGNORE		(1<<7)
 
 #define PERMS_REPORT		(1<<0)
 #define PERMS_SKIP_MTIME	(1<<1)
@@ -517,6 +518,7 @@
 #define MATCHFLG_PERDIR_MERGE	(1<<11)/* merge-file is searched per-dir */
 #define MATCHFLG_EXCLUDE_SELF	(1<<12)/* merge-file name should be excluded */
 #define MATCHFLG_FINISH_SETUP	(1<<13)/* per-dir merge file needs setup */
+#define MATCHFLG_IGNORE		(1<<14)/* pretend that we didn't even see this */
 struct filter_struct {
 	struct filter_struct *next;
 	char *pattern;
diff -ur ../rsync-HEAD-20050125-1221GMT.orig/rsync.yo ./rsync.yo
--- ../rsync-HEAD-20050125-1221GMT.orig/rsync.yo	Tue Jan 25 03:17:00 2005
+++ ./rsync.yo	Fri Jan 28 11:14:37 2005
@@ -372,6 +372,8 @@
      --exclude-from=FILE     exclude patterns listed in FILE
      --include=PATTERN       don't exclude files matching PATTERN
      --include-from=FILE     don't exclude patterns listed in FILE
+     --ignore=PATTERN        don't do anything with files matching PATTERN
+     --ignore-from=FILE      don't do anything with patterns listed in FILE
      --files-from=FILE       read FILE for list of source-file names
  -0  --from0                 all file lists are delimited by nulls
      --version               print version number
@@ -838,6 +840,16 @@
 from a file.
 If em(FILE) is "-" the list will be read from standard input.
 
+dit(bf(--ignore=PATTERN)) This option is a simplified form of the
+--filter option that defaults to an ignore rule and does not allow
+the full rule-parsing syntax of normal filter rules.
+
+See the FILTER RULES section for detailed information on this option.
+
+dit(bf(--ignore-from=FILE)) This specifies a list of ignore patterns
+from a file.
+If em(FILE) is "-" the list will be read from standard input.
+
 dit(bf(--files-from=FILE)) Using this option allows you to specify the
 exact list of files to transfer (as read from the specified FILE or "-"
 for standard input).  It also tweaks the default behavior of rsync to make
@@ -877,8 +889,8 @@
 
 dit(bf(-0, --from0)) This tells rsync that the filenames it reads from a
 file are terminated by a null ('\0') character, not a NL, CR, or CR+LF.
-This affects --exclude-from, --include-from, --files-from, and any
-merged files specified in a -- affects --exclude-from, --include-from, --ignore-from, --files-from,
+and any merged files specified in a --filter rule.
 It does not affect --cvs-exclude (since all names read from a .cvsignore
 file are split on whitespace).
 
diff -ur ../rsync-HEAD-20050125-1221GMT.orig/t_stub.c ./t_stub.c
--- ../rsync-HEAD-20050125-1221GMT.orig/t_stub.c	Tue Jan 25 12:21:14 2005
+++ ./t_stub.c	Thu Jan 27 13:52:08 2005
@@ -56,12 +56,12 @@
 	exit(code);
 }
 
- int check_filter(UNUSED(struct filter_list_struct *listp), UNUSED(char *name),
+ enum matchtype check_filter(UNUSED(struct filter_list_struct *listp), UNUSED(char *name),
 		   UNUSED(int name_is_dir))
 {
 	/* This function doesn't really get called in this test context, so
-	 * just return 0. */
-	return 0;
+	 * just return M_NOMATCH. */
+	return M_NOMATCH;
 }
 
  char *lp_name(UNUSED(int mod))
diff -ur ../rsync-HEAD-20050125-1221GMT.orig/util.c ./util.c
--- ../rsync-HEAD-20050125-1221GMT.orig/util.c	Tue Jan 25 12:21:14 2005
+++ ./util.c	Thu Jan 27 13:51:19 2005
@@ -486,7 +486,7 @@
 	if (server_filter_list.head) {
 		for (s = arg; (s = strchr(s, '/')) != NULL; ) {
 			*s = '\0';
-			if (check_filter(&server_filter_list, arg, 1) < 0) {
+			if (check_filter(&server_filter_list, arg, 1) == M_EXCLUDE) {
 				/* We must leave arg truncated! */
 				return 1;
 			}
@@ -967,7 +967,7 @@
 	if ((int)pathjoin(t, sz, partial_dir, fn) >= sz)
 		return NULL;
 	if (server_filter_list.head
-	    && check_filter(&server_filter_list, partial_fname, 0) < 0)
+	    && check_filter(&server_filter_list, partial_fname, 0) == M_EXCLUDE)
 		return NULL;
 
 	return partial_fname;


More information about the rsync mailing list