* Copyright (C) 1996-2001 Andrew Tridgell <tridge@samba.org>
* Copyright (C) 1996 Paul Mackerras
* Copyright (C) 2002 Martin Pool
- * Copyright (C) 2003, 2004, 2005, 2006 Wayne Davison
+ * 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 as published by
- * the Free Software Foundation; either version 2 of the License, or
+ * 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,
* 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"
extern int io_error;
extern int local_server;
extern int prune_empty_dirs;
+extern int ignore_perishable;
extern int delete_mode;
extern int delete_excluded;
extern int cvs_exclude;
#define MAX_RULE_PREFIX (16)
#define MODIFIERS_MERGE_FILE "-+Cenw"
-#define MODIFIERS_INCL_EXCL "/!Crs"
-#define MODIFIERS_HIDE_PROTECT "/!"
+#define MODIFIERS_INCL_EXCL "/!Crsp"
+#define MODIFIERS_HIDE_PROTECT "/!p"
/* The dirbuf is set by push_local_filters() to the current subdirectory
* relative to curr_dir that is being processed. The path always has a
}
}
- if (!(ret = new(struct filter_struct)))
+ if (!(ret = new0(struct filter_struct)))
out_of_memory("add_rule");
- memset(ret, 0, sizeof ret[0]);
if (!(mflags & (MATCHFLG_ABS_PATH | MATCHFLG_MERGE_FILE))
&& ((xflags & (XFLG_ANCHORED2ABS|XFLG_ABS_IF_SLASH) && *pat == '/')
merge_file);
return NULL;
}
+ fn_len = strlen(fn);
} else {
strlcpy(fn, merge_file, len_ptr ? *len_ptr + 1 : MAXPATHLEN);
- clean_fname(fn, 1);
+ fn_len = clean_fname(fn, 1);
}
- fn_len = strlen(fn);
- if (fn == buf)
- goto done;
-
- if (dirbuf_len + fn_len >= MAXPATHLEN) {
- rprintf(FERROR, "merge-file name overflows: %s\n", fn);
- return NULL;
+ /* If the name isn't in buf yet, it's wasn't absolute. */
+ if (fn != buf) {
+ if (dirbuf_len + fn_len >= MAXPATHLEN) {
+ rprintf(FERROR, "merge-file name overflows: %s\n", fn);
+ return NULL;
+ }
+ memcpy(buf, dirbuf + prefix_skip, dirbuf_len - prefix_skip);
+ memcpy(buf + dirbuf_len - prefix_skip, fn, fn_len + 1);
+ fn_len = clean_fname(buf, 1);
}
- memcpy(buf, dirbuf + prefix_skip, dirbuf_len - prefix_skip);
- memcpy(buf + dirbuf_len - prefix_skip, fn, fn_len + 1);
- fn_len = clean_fname(buf, 1);
- done:
if (len_ptr)
*len_ptr = fn_len;
return buf;
free(pop);
}
+void change_local_filter_dir(const char *dname, int dlen, int dir_depth)
+{
+ static int cur_depth = -1;
+ static void *filt_array[MAXPATHLEN/2+1];
+
+ if (!dname) {
+ for ( ; cur_depth >= 0; cur_depth--) {
+ if (filt_array[cur_depth]) {
+ pop_local_filters(filt_array[cur_depth]);
+ filt_array[cur_depth] = NULL;
+ }
+ }
+ return;
+ }
+
+ assert(dir_depth < MAXPATHLEN/2+1);
+
+ for ( ; cur_depth >= dir_depth; cur_depth--) {
+ if (filt_array[cur_depth]) {
+ pop_local_filters(filt_array[cur_depth]);
+ filt_array[cur_depth] = NULL;
+ }
+ }
+
+ cur_depth = dir_depth;
+ filt_array[cur_depth] = push_local_filters(dname, dlen);
+}
+
static int rule_matches(char *name, struct filter_struct *ex, int name_is_dir)
{
int slash_handling, str_cnt = 0, anchored_match = 0;
char *p, *pattern = ex->pattern;
const char *strings[16]; /* more than enough */
+ if (*name == '/')
+ name++;
if (!*name)
return 0;
if (*pattern == '/') {
anchored_match = 1;
pattern++;
- if (strings[0][0] == '/')
- strings[0]++;
}
if (!anchored_match && ex->u.slash_cnt
if (litmatch_array(pattern, strings, slash_handling))
return ret_match;
} else if (anchored_match) {
- if (strcmp(strings[0], pattern) == 0)
+ if (strcmp(name, pattern) == 0)
return ret_match;
} else {
int l1 = strlen(name);
struct filter_struct *ent;
for (ent = listp->head; ent; ent = ent->next) {
+ if (ignore_perishable && ent->match_flags & MATCHFLG_PERISHABLE)
+ continue;
if (ent->match_flags & MATCHFLG_PERDIR_MERGE) {
int rc = check_filter(ent->u.mergelist, name,
name_is_dir);
case 'n':
new_mflags |= MATCHFLG_NO_INHERIT;
break;
+ case 'p':
+ new_mflags |= MATCHFLG_PERISHABLE;
+ break;
case 'r':
new_mflags |= MATCHFLG_RECEIVER_SIDE;
break;
exit_cleanup(RERR_SYNTAX);
}
+ /* --delete-excluded turns an un-modified include/exclude into a
+ * sender-side rule. We also affect per-dir merge files that take
+ * no prefixes as a simple optimization. */
+ if (delete_excluded
+ && !(new_mflags & (MATCHFLG_RECEIVER_SIDE|MATCHFLG_SENDER_SIDE))
+ && (!(new_mflags & MATCHFLG_PERDIR_MERGE)
+ || new_mflags & MATCHFLG_NO_PREFIXES))
+ new_mflags |= MATCHFLG_SENDER_SIDE;
+
*len_ptr = len;
*mflags_ptr = new_mflags;
return (const char *)s;
static void get_cvs_excludes(uint32 mflags)
{
- char *p, fname[MAXPATHLEN];
static int initialized = 0;
+ char *p, fname[MAXPATHLEN];
if (initialized)
return;
initialized = 1;
- parse_rule(&cvs_filter_list, default_cvsignore, mflags, 0);
+ parse_rule(&cvs_filter_list, default_cvsignore,
+ mflags | (protocol_version >= 30 ? MATCHFLG_PERISHABLE : 0),
+ 0);
p = module_id >= 0 && lp_use_chroot(module_id) ? "/" : getenv("HOME");
if (p && pathjoin(fname, MAXPATHLEN, p, ".cvsignore") < MAXPATHLEN)
&pat_len, &new_mflags);
if (!cp)
break;
+
+ pattern = cp + pat_len;
+
if (pat_len >= MAXPATHLEN) {
- rprintf(FERROR, "discarding over-long filter: %s\n",
- cp);
+ rprintf(FERROR, "discarding over-long filter: %.*s\n",
+ (int)pat_len, cp);
continue;
}
- pattern = cp + pat_len;
if (new_mflags & MATCHFLG_CLEAR_LIST) {
if (verbose > 2) {
}
len = pat_len;
if (new_mflags & MATCHFLG_EXCLUDE_SELF) {
- const char *name = strrchr(cp, '/');
- if (name)
- len -= ++name - cp;
- else
- name = cp;
+ const char *name = cp + len;
+ while (name > cp && name[-1] != '/') name--;
+ len -= name - cp;
add_rule(listp, name, len, 0, 0);
new_mflags &= ~MATCHFLG_EXCLUDE_SELF;
len = pat_len;
else
legal_len = 0;
+ if (match_flags & MATCHFLG_NEGATE)
+ *op++ = '!';
if (match_flags & MATCHFLG_CVS_IGNORE)
*op++ = 'C';
else {
&& (!for_xfer || protocol_version >= 29
|| (delete_excluded && am_sender)))
*op++ = 'r';
+ if (match_flags & MATCHFLG_PERISHABLE) {
+ if (!for_xfer || protocol_version >= 30)
+ *op++ = 'p';
+ else if (am_sender)
+ return NULL;
+ }
if (op - buf > legal_len)
return NULL;
if (legal_len)
int elide = 0;
char *p;
+ /* Note we need to check delete_excluded here in addition to
+ * the code in parse_rule_tok() because some rules may have
+ * been added before we found the --delete-excluded option.
+ * We must also elide any CVS merge-file rules to avoid a
+ * backward compatibility problem, and we elide any no-prefix
+ * merge files as an optimization (since they can only have
+ * include/exclude rules). */
if (ent->match_flags & MATCHFLG_SENDER_SIDE)
elide = am_sender ? 1 : -1;
if (ent->match_flags & MATCHFLG_RECEIVER_SIDE)
elide = elide ? 0 : am_sender ? -1 : 1;
- else if (delete_excluded && !elide)
+ else if (delete_excluded && !elide
+ && (!(ent->match_flags & MATCHFLG_PERDIR_MERGE)
+ || ent->match_flags & MATCHFLG_NO_PREFIXES))
elide = am_sender ? 1 : -1;
if (elide < 0) {
if (prev)
if (!p) {
rprintf(FERROR,
"filter rules are too modern for remote rsync.\n");
- exit_cleanup(RERR_SYNTAX);
+ exit_cleanup(RERR_PROTOCOL);
}
if (f_out < 0)
continue;