📄 exclude.c
字号:
/* * 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-2008 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"extern int verbose;extern int am_server;extern int am_sender;extern int eol_nulls;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;extern int sanitize_paths;extern int protocol_version;extern int module_id;extern char curr_dir[];extern unsigned int curr_dir_len;extern unsigned int module_dirlen;struct filter_list_struct filter_list = { 0, 0, "" };struct filter_list_struct cvs_filter_list = { 0, 0, " [global CVS]" };struct filter_list_struct daemon_filter_list = { 0, 0, " [daemon]" };/* Need room enough for ":MODS " prefix plus some room to grow. */#define MAX_RULE_PREFIX (16)#define MODIFIERS_MERGE_FILE "-+Cenw"#define MODIFIERS_INCL_EXCL "/!Crsp"#define MODIFIERS_HIDE_PROTECT "/!p"#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;/* 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 struct filter_struct **mergelist_parents;static int mergelist_cnt = 0;static int mergelist_size = 0;/* 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 free_filter(struct filter_struct *ex){ if (ex->match_flags & MATCHFLG_PERDIR_MERGE) { free(ex->u.mergelist->debug_type); free(ex->u.mergelist); mergelist_cnt--; } free(ex->pattern); free(ex);}/* Build a filter structure given a filter pattern. The value in "pat" * is not null-terminated. */static void add_rule(struct filter_list_struct *listp, const char *pat, unsigned int pat_len, uint32 mflags, int xflags){ struct filter_struct *ret; const char *cp; unsigned int pre_len, suf_len, slash_cnt = 0; if (verbose > 2) { rprintf(FINFO, "[%s] add_rule(%s%.*s%s)%s\n", who_am_i(), get_rule_prefix(mflags, pat, 0, NULL), (int)pat_len, pat, (mflags & MATCHFLG_DIRECTORY) ? "/" : "", listp->debug_type); } /* 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)) { uint32 mf = mflags & (MATCHFLG_RECEIVER_SIDE|MATCHFLG_SENDER_SIDE); if (am_sender) { if (mf == MATCHFLG_RECEIVER_SIDE) return; } else { if (mf == MATCHFLG_SENDER_SIDE) return; } } if (!(ret = new0(struct filter_struct))) out_of_memory("add_rule"); if (pat_len > 1 && pat[pat_len-1] == '/') { pat_len--; mflags |= MATCHFLG_DIRECTORY; } for (cp = pat; cp < pat + pat_len; cp++) { if (*cp == '/') slash_cnt++; } if (!(mflags & (MATCHFLG_ABS_PATH | MATCHFLG_MERGE_FILE)) && ((xflags & (XFLG_ANCHORED2ABS|XFLG_ABS_IF_SLASH) && *pat == '/') || (xflags & XFLG_ABS_IF_SLASH && slash_cnt))) { mflags |= MATCHFLG_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(mflags, MATCHFLG_DIRECTORY, MATCHFLG_INCLUDE)) { mflags &= ~MATCHFLG_DIRECTORY; suf_len = sizeof SLASH_WILD3_SUFFIX - 1; } else suf_len = 0; if (!(ret->pattern = new_array(char, pre_len + pat_len + suf_len + 1))) out_of_memory("add_rule"); if (pre_len) { memcpy(ret->pattern, dirbuf + module_dirlen, pre_len); for (cp = ret->pattern; cp < ret->pattern + pre_len; cp++) { if (*cp == '/') slash_cnt++; } } strlcpy(ret->pattern + pre_len, pat, pat_len + 1); pat_len += pre_len; if (suf_len) { memcpy(ret->pattern + pat_len, SLASH_WILD3_SUFFIX, suf_len+1); pat_len += suf_len; slash_cnt++; } if (strpbrk(ret->pattern, "*[?")) { mflags |= MATCHFLG_WILD; if ((cp = strstr(ret->pattern, "**")) != NULL) { mflags |= MATCHFLG_WILD2; /* If the pattern starts with **, note that. */ if (cp == ret->pattern) mflags |= MATCHFLG_WILD2_PREFIX; /* If the pattern ends with ***, note that. */ if (pat_len >= 3 && ret->pattern[pat_len-3] == '*' && ret->pattern[pat_len-2] == '*' && ret->pattern[pat_len-1] == '*') mflags |= MATCHFLG_WILD3_SUFFIX; } } if (mflags & MATCHFLG_PERDIR_MERGE) { struct filter_list_struct *lp; unsigned int len; int i; if ((cp = strrchr(ret->pattern, '/')) != NULL) cp++; else cp = ret->pattern; /* If the local merge file was already mentioned, don't * add it again. */ for (i = 0; i < mergelist_cnt; i++) { struct filter_struct *ex = mergelist_parents[i]; const char *s = strrchr(ex->pattern, '/'); if (s) s++; else s = ex->pattern; len = strlen(s); if (len == pat_len - (cp - ret->pattern) && memcmp(s, cp, len) == 0) { free_filter(ret); return; } } if (!(lp = new_array(struct filter_list_struct, 1))) out_of_memory("add_rule"); lp->head = lp->tail = NULL; if (asprintf(&lp->debug_type, " [per-dir %s]", cp) < 0) out_of_memory("add_rule"); ret->u.mergelist = lp; if (mergelist_cnt == mergelist_size) { mergelist_size += 5; mergelist_parents = realloc_array(mergelist_parents, struct filter_struct *, mergelist_size); if (!mergelist_parents) out_of_memory("add_rule"); } mergelist_parents[mergelist_cnt++] = ret; } else ret->u.slash_cnt = slash_cnt; ret->match_flags = mflags; if (!listp->tail) { ret->next = listp->head; listp->head = listp->tail = ret; } else { ret->next = listp->tail->next; listp->tail->next = ret; listp->tail = ret; }}static void clear_filter_list(struct filter_list_struct *listp){ if (listp->tail) { struct filter_struct *ent, *next; /* Truncate any inherited items from the local list. */ listp->tail->next = NULL; /* Now free everything that is left. */ for (ent = listp->head; ent; ent = next) { next = ent->next; free_filter(ent); } } listp->head = listp->tail = NULL;}/* This returns an expanded (absolute) filename for the merge-file name if * the name has any slashes in it OR if the parent_dirscan var is True; * otherwise it returns the original merge_file name. If the len_ptr value * is non-NULL the merge_file name is limited by the referenced length * value and will be updated with the length of the resulting name. We * always return a name that is null terminated, even if the merge_file * name was not. */static char *parse_merge_name(const char *merge_file, unsigned int *len_ptr, unsigned int prefix_skip){ static char buf[MAXPATHLEN]; char *fn, tmpbuf[MAXPATHLEN]; unsigned int fn_len; if (!parent_dirscan && *merge_file != '/') { /* Return the name unchanged it doesn't have any slashes. */ if (len_ptr) { const char *p = merge_file + *len_ptr; while (--p > merge_file && *p != '/') {} if (p == merge_file) { strlcpy(buf, merge_file, *len_ptr + 1); return buf; } } else if (strchr(merge_file, '/') == NULL) return (char *)merge_file; } fn = *merge_file == '/' ? buf : tmpbuf; if (sanitize_paths) { const char *r = prefix_skip ? "/" : NULL; /* null-terminate the name if it isn't already */ if (len_ptr && merge_file[*len_ptr]) { char *to = fn == buf ? tmpbuf : buf; strlcpy(to, merge_file, *len_ptr + 1); merge_file = to; } if (!sanitize_path(fn, merge_file, r, dirbuf_depth, SP_DEFAULT)) { rprintf(FERROR, "merge-file name overflows: %s\n", merge_file); return NULL; } fn_len = strlen(fn); } else { strlcpy(fn, merge_file, len_ptr ? *len_ptr + 1 : MAXPATHLEN); fn_len = clean_fname(fn, CFN_COLLAPSE_DOT_DOT_DIRS); } /* If the name isn't in buf yet, it's wasn't absolute. */ if (fn != buf) { int d_len = dirbuf_len - prefix_skip; if (d_len + fn_len >= MAXPATHLEN) { rprintf(FERROR, "merge-file name overflows: %s\n", fn); return NULL; } memcpy(buf, dirbuf + prefix_skip, d_len); memcpy(buf + d_len, fn, fn_len + 1); fn_len = clean_fname(buf, CFN_COLLAPSE_DOT_DOT_DIRS); } if (len_ptr) *len_ptr = fn_len; return buf;}/* Sets the dirbuf and dirbuf_len values. */void set_filter_dir(const char *dir, unsigned int dirlen){ unsigned int len; if (*dir != '/') { memcpy(dirbuf, curr_dir, curr_dir_len); dirbuf[curr_dir_len] = '/'; len = curr_dir_len + 1; if (len + dirlen >= MAXPATHLEN) dirlen = 0; } else len = 0; memcpy(dirbuf + len, dir, dirlen); dirbuf[dirlen + len] = '\0'; dirbuf_len = clean_fname(dirbuf, CFN_COLLAPSE_DOT_DOT_DIRS); if (dirbuf_len > 1 && dirbuf[dirbuf_len-1] == '.' && dirbuf[dirbuf_len-2] == '/') dirbuf_len -= 2; if (dirbuf_len != 1) dirbuf[dirbuf_len++] = '/'; dirbuf[dirbuf_len] = '\0'; if (sanitize_paths) dirbuf_depth = count_dir_elements(dirbuf + module_dirlen);}/* This routine takes a per-dir merge-file entry and finishes its setup. * If the name has a path portion then we check to see if it refers to a * parent directory of the first transfer dir. If it does, we scan all the * dirs from that point through the parent dir of the transfer dir looking * for the per-dir merge-file in each one. */static BOOL setup_merge_file(struct filter_struct *ex, struct filter_list_struct *lp){ char buf[MAXPATHLEN]; char *x, *y, *pat = ex->pattern; unsigned int len; if (!(x = parse_merge_name(pat, NULL, 0)) || *x != '/') return 0; y = strrchr(x, '/'); *y = '\0'; ex->pattern = strdup(y+1); if (!*x) x = "/"; if (*x == '/') strlcpy(buf, x, MAXPATHLEN); else pathjoin(buf, MAXPATHLEN, dirbuf, x); len = clean_fname(buf, CFN_COLLAPSE_DOT_DOT_DIRS); if (len != 1 && len < MAXPATHLEN-1) { buf[len++] = '/'; buf[len] = '\0'; } /* This ensures that the specified dir is a parent of the transfer. */ for (x = buf, y = dirbuf; *x && *x == *y; x++, y++) {} if (*x) y += strlen(y); /* nope -- skip the scan */ parent_dirscan = True; while (*y) { char save[MAXPATHLEN]; strlcpy(save, y, MAXPATHLEN); *y = '\0'; dirbuf_len = y - dirbuf; strlcpy(x, ex->pattern, MAXPATHLEN - (x - buf)); parse_filter_file(lp, buf, ex->match_flags, XFLG_ANCHORED2ABS); if (ex->match_flags & MATCHFLG_NO_INHERIT) lp->head = NULL; lp->tail = NULL; strlcpy(y, save, MAXPATHLEN); while ((*x++ = *y++) != '/') {} } parent_dirscan = False; free(pat); return 1;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -