1997 lines · 60.1 KB · C
RawDownload
/* * The filter include/exclude routines. * * Copyright (C) 1996-2001 Andrew Tridgell <tridge@samba.org> * Copyright (C) 1996 Paul Mackerras * Copyright (C) 2002 Martin Pool * Copyright (C) 2003-2024 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 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 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * 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, visit the http://fsf.org website. */ #include "rsync.h"#include "ifuncs.h" extern int am_server;extern int am_sender;extern int am_generator;extern int eol_nulls;extern int io_error;extern int xfer_dirs;extern int recurse;extern int local_server;extern int prune_empty_dirs;extern int ignore_perishable;extern int relative_paths;extern int delete_mode;extern int delete_excluded;extern int cvs_exclude;extern int sanitize_paths;extern int protocol_version;extern int trust_sender_args;extern int module_id;extern int operator_path_resolve; /* Set while the daemon loads its own filter parameters; see parse_filter_file(). */int daemon_config_filter_file = 0; /* Where the rule text now being parsed came from, when that is a file's * CONTENTS rather than an argument.  A rule that fails to parse used to be * echoed back verbatim, and the peer chooses which file gets merged (a * per-directory merge rule travels over the protocol, so no argument of ours * ever names it), which made the filter parser a read-any-line oracle: any * line that is not valid filter syntax came straight back in the error. * Report where the bad rule is, not what it says. */static int rule_src_in_file = 0;   /* parsing a file's contents right now */static const char *rule_src_file = NULL;   /* ...and its name is safe to show */static int rule_src_line = 0;/* Where a file whose own name we must NOT print was named, which is a location * we CAN print: it keeps the diagnostic useful without echoing the pathname a * merge rule supplied. */static const char *rule_src_named_at = NULL; /* True while the text we are handling came out of a file's contents: either we * are parsing that file right now, or this is a deferred per-dir merge whose * NAME came from one and which carries the provenance on the rule. */#define TEXT_FROM_FILE(template) \	(rule_src_in_file \	 || ((template) && (template)->rflags & FILTRULE_FROM_FILE)) /* "FILE line N", or just "FILE" when the count is not a line count. */static const char *rule_src_where(void){	static char buf[MAXPATHLEN + 32]; 	if (!rule_src_file) {		if (!rule_src_named_at)			return "a file read earlier";   /* origin not retained */		snprintf(buf, sizeof buf, "a file named at %s", rule_src_named_at);		return buf;	}	if (rule_src_line < 0)		return rule_src_file;	snprintf(buf, sizeof buf, "%s line %d", rule_src_file, rule_src_line);	return buf;} /* THE chokepoint.  Every diagnostic string that is, or is built from, a filter * rule's own text -- a pattern, a merge-file name, a path composed from one -- * must be passed through rule_text() on its way to rprintf().  When the rule * came from an argument the text is returned unchanged, because it is the * user's own and hiding it only makes typos harder to fix.  When it came from * a FILE's contents it is replaced by a description of where it came from, * because the peer chooses which file gets merged and any line of it that * reaches a message is a line the peer can read back. * * Doing it here rather than at each site is the point: a message added later * cannot reintroduce the leak by forgetting to check, and there is one place * to audit.  `template' is the rule the text belongs to, or NULL when the only * thing that matters is whether we are parsing a file right now. * * The returned buffer is rotated, so two calls in one rprintf() are safe. */static const char *rule_text_len(const filter_rule *template,				 const char *text, int len){	static char buf[2][BIGPATHBUFLEN];	static int which = 0;	char *b = buf[which]; 	which ^= 1;	if (!TEXT_FROM_FILE(template)) {		if (len < 0)			return text;		snprintf(b, sizeof buf[0], "%.*s", len, text);		return b;	}	snprintf(b, sizeof buf[0], "<rule from %s>", rule_src_where());	return b;} static const char *rule_text(const filter_rule *template, const char *text){	return rule_text_len(template, text, -1);} /* For the extra detail some messages add ABOUT the text -- a character of it, * an offset into it.  Dropped along with the text it describes. */static const char *rule_detail(const filter_rule *template, const char *detail){	return TEXT_FROM_FILE(template) ? "" : detail;} static void filter_rule_err(const char *msg, const char *rulestr){	rprintf(FERROR, "%s: %s\n", msg, rule_text(NULL, rulestr));	exit_cleanup(RERR_SYNTAX);} extern char curr_dir[MAXPATHLEN];extern unsigned int curr_dir_len;extern unsigned int module_dirlen; filter_rule_list filter_list = { .debug_type = "" };filter_rule_list cvs_filter_list = { .debug_type = " [global CVS]" };filter_rule_list daemon_filter_list = { .debug_type = " [daemon]" };filter_rule_list implied_filter_list = { .debug_type = " [implied]" }; int saw_xattr_filter = 0;int trust_sender_args = 0;int trust_sender_filter = 0; /* Need room enough for ":MODS " prefix plus some room to grow. */#define MAX_RULE_PREFIX (16) #define SLASH_WILD3_SUFFIX "/***" /* 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 * trailing slash appended, and the variable dirbuf_len contains the length * of this path prefix.  The path is always absolute. */static char dirbuf[MAXPATHLEN+1];static unsigned int dirbuf_len = 0;static int dirbuf_depth; /* This is True when we're scanning parent dirs for per-dir merge-files. */static BOOL parent_dirscan = False; #define MAX_MERGE_DEPTH 32static int merge_depth = 0; /* This array contains a list of all the currently active per-dir merge * files.  This makes it easier to save the appropriate values when we * "push" down into each subdirectory. */static filter_rule **mergelist_parents;static int mergelist_cnt = 0;static int mergelist_size = 0; #define LOCAL_RULE   1#define REMOTE_RULE  2static uchar cur_elide_value = REMOTE_RULE; /* Each filter_list_struct describes a singly-linked list by keeping track * of both the head and tail pointers.  The list is slightly unusual in that * a parent-dir's content can be appended to the end of the local list in a * special way:  the last item in the local list has its "next" pointer set * to point to the inherited list, but the local list's tail pointer points * at the end of the local list.  Thus, if the local list is empty, the head * will be pointing at the inherited content but the tail will be NULL.  To * help you visualize this, here are the possible list arrangements: * * Completely Empty                     Local Content Only * ==================================   ==================================== * head -> NULL                         head -> Local1 -> Local2 -> NULL * tail -> NULL                         tail -------------^ * * Inherited Content Only               Both Local and Inherited Content * ==================================   ==================================== * head -> Parent1 -> Parent2 -> NULL   head -> L1 -> L2 -> P1 -> P2 -> NULL * tail -> NULL                         tail ---------^ * * This means that anyone wanting to traverse the whole list to use it just * needs to start at the head and use the "next" pointers until it goes * NULL.  To add new local content, we insert the item after the tail item * and update the tail (obviously, if "tail" was NULL, we insert it at the * head).  To clear the local list, WE MUST NOT FREE THE INHERITED CONTENT * because it is shared between the current list and our parent list(s). * The easiest way to handle this is to simply truncate the list after the * tail item and then free the local list from the head.  When inheriting * the list for a new local dir, we just save off the filter_list_struct * values (so we can pop back to them later) and set the tail to NULL. */ static void teardown_mergelist(filter_rule *ex){	int j; 	if (!ex->u.mergelist)		return; 	if (DEBUG_GTE(FILTER, 2)) {		rprintf(FINFO, "[%s] deactivating mergelist #%d%s\n",			who_am_i(), mergelist_cnt - 1,			ex->u.mergelist->debug_type);	} 	free(ex->u.mergelist->debug_type);	free(ex->u.mergelist); 	for (j = 0; j < mergelist_cnt; j++) {		if (mergelist_parents[j] == ex) {			mergelist_parents[j] = NULL;			break;		}	}	while (mergelist_cnt && mergelist_parents[mergelist_cnt-1] == NULL)		mergelist_cnt--;} static void free_filter(filter_rule *ex){	if (ex->rflags & FILTRULE_PERDIR_MERGE)		teardown_mergelist(ex);	free(ex->pattern);	free(ex);} static void free_filters(filter_rule *ent){	while (ent) {		filter_rule *next = ent->next;		free_filter(ent);		ent = next;	}} /* Build a filter structure given a filter pattern.  The value in "pat" * is not null-terminated.  "rule" is either held or freed, so the * caller should not free it. */static void add_rule(filter_rule_list *listp, const char *pat, unsigned int pat_len,		     filter_rule *rule, int xflags){	const char *cp;	unsigned int pre_len, suf_len, slash_cnt = 0;	char *mention_rule_suffix; 	if (DEBUG_GTE(FILTER, 1) && pat_len && (pat[pat_len-1] == ' ' || pat[pat_len-1] == '\t'))		mention_rule_suffix = " -- CAUTION: trailing whitespace!";	else		mention_rule_suffix = DEBUG_GTE(FILTER, 2) ? "" : NULL;	if (mention_rule_suffix) {		rprintf(FINFO, "[%s] add_rule(%s%s)%s%s\n",			who_am_i(), rule_detail(rule, get_rule_prefix(rule, pat, 0, NULL)),			rule_text_len(rule, pat, (int)pat_len),			listp->debug_type, rule_detail(rule, mention_rule_suffix));	} 	/* These flags also indicate that we're reading a list that	 * needs to be filtered now, not post-filtered later. */	if (xflags & (XFLG_ANCHORED2ABS|XFLG_ABS_IF_SLASH)		&& (rule->rflags & FILTRULES_SIDES)			== (am_sender ? FILTRULE_RECEIVER_SIDE : FILTRULE_SENDER_SIDE)) {		/* This filter applies only to the other side.  Drop it. */		free_filter(rule);		return;	} 	if (pat_len > 1 && pat[pat_len-1] == '/') {		pat_len--;		rule->rflags |= FILTRULE_DIRECTORY;	} 	for (cp = pat; cp < pat + pat_len; cp++) {		if (*cp == '/')			slash_cnt++;	} 	if (!(rule->rflags & (FILTRULE_ABS_PATH | FILTRULE_MERGE_FILE))	 && ((xflags & (XFLG_ANCHORED2ABS|XFLG_ABS_IF_SLASH) && *pat == '/')	  || (xflags & XFLG_ABS_IF_SLASH && slash_cnt))) {		rule->rflags |= FILTRULE_ABS_PATH;		if (*pat == '/')			pre_len = dirbuf_len - module_dirlen - 1;		else			pre_len = 0;	} else		pre_len = 0; 	/* The daemon wants dir-exclude rules to get an appended "/" + "***". */	if (xflags & XFLG_DIR2WILD3	 && BITS_SETnUNSET(rule->rflags, FILTRULE_DIRECTORY, FILTRULE_INCLUDE)) {		rule->rflags &= ~FILTRULE_DIRECTORY;		suf_len = sizeof SLASH_WILD3_SUFFIX - 1;	} else		suf_len = 0; 	rule->pattern = new_array(char, pre_len + pat_len + suf_len + 1);	if (pre_len) {		memcpy(rule->pattern, dirbuf + module_dirlen, pre_len);		for (cp = rule->pattern; cp < rule->pattern + pre_len; cp++) {			if (*cp == '/')				slash_cnt++;		}	}	rule->elide = 0;	strlcpy(rule->pattern + pre_len, pat, pat_len + 1);	pat_len += pre_len;	if (suf_len) {		memcpy(rule->pattern + pat_len, SLASH_WILD3_SUFFIX, suf_len+1);		pat_len += suf_len;		slash_cnt++;	} 	if (strpbrk(rule->pattern, "*[?")) {		rule->rflags |= FILTRULE_WILD;		if ((cp = strstr(rule->pattern, "**")) != NULL) {			rule->rflags |= FILTRULE_WILD2;			/* If the pattern starts with **, note that. */			if (cp == rule->pattern)				rule->rflags |= FILTRULE_WILD2_PREFIX;			/* If the pattern ends with ***, note that. */			if (pat_len >= 3			 && rule->pattern[pat_len-3] == '*'			 && rule->pattern[pat_len-2] == '*'			 && rule->pattern[pat_len-1] == '*')				rule->rflags |= FILTRULE_WILD3_SUFFIX;		}	} 	if (rule->rflags & FILTRULE_PERDIR_MERGE) {		filter_rule_list *lp;		unsigned int len;		int i; 		if ((cp = strrchr(rule->pattern, '/')) != NULL)			cp++;		else			cp = rule->pattern; 		/* If the local merge file was already mentioned, don't		 * add it again. */		for (i = 0; i < mergelist_cnt; i++) {			filter_rule *ex = mergelist_parents[i];			const char *s;			if (!ex)				continue;			s = strrchr(ex->pattern, '/');			if (s)				s++;			else				s = ex->pattern;			len = strlen(s);			if (len == pat_len - (cp - rule->pattern) && memcmp(s, cp, len) == 0) {				free_filter(rule);				return;			}		} 		lp = new_array0(filter_rule_list, 1);		if (asprintf(&lp->debug_type, " [per-dir %s]", rule_text(rule, cp)) < 0)			out_of_memory("add_rule");		rule->u.mergelist = lp; 		if (mergelist_cnt == mergelist_size) {			mergelist_size += 5;			mergelist_parents = realloc_array(mergelist_parents, filter_rule *, mergelist_size);		}		if (DEBUG_GTE(FILTER, 2)) {			rprintf(FINFO, "[%s] activating mergelist #%d%s\n",				who_am_i(), mergelist_cnt, lp->debug_type);		}		mergelist_parents[mergelist_cnt++] = rule;	} else		rule->u.slash_cnt = slash_cnt; 	if (!listp->tail) {		rule->next = listp->head;		listp->head = listp->tail = rule;	} else {		rule->next = listp->tail->next;		listp->tail->next = rule;		listp->tail = rule;	}} /* If the wildcards failed, the remote shell might give us a file matching the literal * wildcards.  Since "*" & "?" already match themselves, this just needs to deal with * failed "[foo]" idioms. */static void maybe_add_literal_brackets_rule(filter_rule const *based_on, int arg_len){	filter_rule *rule;	const char *arg = based_on->pattern, *cp;	char *p;	int cnt = 0; 	if (arg_len < 0)		arg_len = strlen(arg); 	for (cp = arg; *cp; cp++) {		if (*cp == '\\' && cp[1]) {			cp++;		} else if (*cp == '[')			cnt++;	}	if (!cnt)		return; 	rule = new0(filter_rule);	rule->rflags = based_on->rflags;	rule->u.slash_cnt = based_on->u.slash_cnt;	p = rule->pattern = new_array(char, arg_len + cnt + 1);	for (cp = arg; *cp; ) {		if (*cp == '\\' && cp[1]) {			*p++ = *cp++;		} else if (*cp == '[')			*p++ = '\\';		*p++ = *cp++;	}	*p++ = '\0'; 	rule->next = implied_filter_list.head;	implied_filter_list.head = rule;	if (DEBUG_GTE(FILTER, 3)) {		rprintf(FINFO, "[%s] add_implied_include(%s%s)\n", who_am_i(), rule->pattern,			rule->rflags & FILTRULE_DIRECTORY ? "/" : "");	}} static char *partial_string_buf = NULL;static int partial_string_len = 0;void implied_include_partial_string(const char *s_start, const char *s_end){	partial_string_len = s_end - s_start;	if (partial_string_len <= 0 || partial_string_len >= MAXPATHLEN) { /* too-large should be impossible... */		partial_string_len = 0;		return;	}	if (!partial_string_buf)		partial_string_buf = new_array(char, MAXPATHLEN);	memcpy(partial_string_buf, s_start, partial_string_len);} void free_implied_include_partial_string(){	if (partial_string_buf) {		if (partial_string_len)			add_implied_include("", 0);		free(partial_string_buf);		partial_string_buf = NULL;	}	partial_string_len = 0; /* paranoia */} /* Each arg the client sends to the remote sender turns into an implied include * that the receiver uses to validate the file list from the sender. */void add_implied_include(const char *arg, int skip_daemon_module){	int arg_len, saw_wild = 0, saw_live_open_brkt = 0, backslash_cnt = 0;	int slash_cnt = 0;	const char *cp;	char *p;	if (trust_sender_args)		return;	if (partial_string_len) {		arg_len = strlen(arg);		if (partial_string_len + arg_len >= MAXPATHLEN) {			partial_string_len = 0;			return; /* Should be impossible... */		}		memcpy(partial_string_buf + partial_string_len, arg, arg_len + 1);		partial_string_len = 0;		arg = partial_string_buf;	}	if (skip_daemon_module) {		if ((cp = strchr(arg, '/')) != NULL)			arg = cp + 1;		else			arg = "";	}	if (relative_paths) {

Lines 1–500 of 1997.