[patch] add "--ignore" option

Kirkwood, Matthew Matthew.Kirkwood at gs.com
Mon Jan 31 17:19:34 GMT 2005


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_list, 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) {
+			if(lp->match_flags & MATCHFLG_IGNORE) {
+				if (asprintf(&arg, "--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 --filter rule.
+This 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