X-Git-Url: https://mattmccutchen.net/rsync/rsync.git/blobdiff_plain/a5bb0902b4f7538855d60d3495648d397db03c0a..d48810ba5b0b8d09b272092a70da1255c2346ab8:/util.c diff --git a/util.c b/util.c index 5dd6ead4..a8b17d53 100644 --- a/util.c +++ b/util.c @@ -4,7 +4,7 @@ * Copyright (C) 1996-2000 Andrew Tridgell * Copyright (C) 1996 Paul Mackerras * Copyright (C) 2001, 2002 Martin Pool - * Copyright (C) 2003-2007 Wayne Davison + * 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 @@ -33,7 +33,7 @@ extern char *module_dir; extern unsigned int module_dirlen; extern mode_t orig_umask; extern char *partial_dir; -extern struct filter_list_struct server_filter_list; +extern struct filter_list_struct daemon_filter_list; int sanitize_paths = 0; @@ -503,14 +503,13 @@ int lock_range(int fd, int offset, int len) return fcntl(fd,F_SETLK,&lock) == 0; } -static int filter_server_path(char *arg) +static int filter_daemon_path(char *arg) { - char *s; - - if (server_filter_list.head) { + if (daemon_filter_list.head) { + char *s; for (s = arg; (s = strchr(s, '/')) != NULL; ) { *s = '\0'; - if (check_filter(&server_filter_list, arg, 1) < 0) { + if (check_filter(&daemon_filter_list, arg, 1) < 0) { /* We must leave arg truncated! */ return 1; } @@ -525,6 +524,8 @@ void glob_expand(char *s, char ***argv_ptr, int *argc_ptr, int *maxargs_ptr) char **argv = *argv_ptr; int argc = *argc_ptr; int maxargs = *maxargs_ptr; + int count, have_glob_results; + #if !defined HAVE_GLOB || !defined HAVE_GLOB_H if (argc == maxargs) { maxargs += MAX_ARGS; @@ -536,7 +537,7 @@ void glob_expand(char *s, char ***argv_ptr, int *argc_ptr, int *maxargs_ptr) if (!*s) s = "."; s = argv[argc++] = strdup(s); - filter_server_path(s); + filter_daemon_path(s); #else glob_t globbuf; @@ -546,32 +547,63 @@ void glob_expand(char *s, char ***argv_ptr, int *argc_ptr, int *maxargs_ptr) s = "."; if (sanitize_paths) - s = sanitize_path(NULL, s, "", 0); - else + s = sanitize_path(NULL, s, "", 0, SP_KEEP_DOT_DIRS); + else { s = strdup(s); - if (!s) - out_of_memory("glob_expand"); + if (!s) + out_of_memory("glob_expand"); + clean_fname(s, CFN_KEEP_DOT_DIRS + | CFN_KEEP_TRAILING_SLASH + | CFN_COLLAPSE_DOT_DOT_DIRS); + } memset(&globbuf, 0, sizeof globbuf); - if (!filter_server_path(s)) - glob(s, 0, NULL, &globbuf); - if (MAX((int)globbuf.gl_pathc, 1) > maxargs - argc) { - maxargs += globbuf.gl_pathc + MAX_ARGS; + glob(s, 0, NULL, &globbuf); + /* Note: we check the first match against the filter list, + * just in case the user specified a wildcard in the path. */ + if ((count = globbuf.gl_pathc) > 0) { + if (filter_daemon_path(globbuf.gl_pathv[0])) { + int slashes = 0; + char *cp; + /* Truncate original arg at glob's truncation point. */ + for (cp = globbuf.gl_pathv[0]; *cp; cp++) { + if (*cp == '/') + slashes++; + } + for (cp = s; *cp; cp++) { + if (*cp == '/') { + if (slashes-- <= 0) { + *cp = '\0'; + break; + } + } + } + have_glob_results = 0; + count = 1; + } else + have_glob_results = 1; + } else { + /* This truncates "s" at a filtered element, if present. */ + filter_daemon_path(s); + have_glob_results = 0; + count = 1; + } + if (count + argc > maxargs) { + maxargs += count + MAX_ARGS; if (!(argv = realloc_array(argv, char *, maxargs))) out_of_memory("glob_expand"); *argv_ptr = argv; *maxargs_ptr = maxargs; } - if (globbuf.gl_pathc == 0) - argv[argc++] = s; - else { + if (have_glob_results) { int i; free(s); - for (i = 0; i < (int)globbuf.gl_pathc; i++) { + for (i = 0; i < count; i++) { if (!(argv[argc++] = strdup(globbuf.gl_pathv[i]))) out_of_memory("glob_expand"); } - } + } else + argv[argc++] = s; globfree(&globbuf); #endif *argc_ptr = argc; @@ -687,13 +719,13 @@ int count_dir_elements(const char *p) return cnt; } -/* Turns multiple adjacent slashes into a single slash, drops interior "." - * elements, drops an intial "./" unless CFN_KEEP_LEADING_DOT_DIR is flagged, - * will even drop a trailing '.' after a '/' if CFN_DROP_TRAILING_DOT_DIR is - * flagged, removes a trailing slash (perhaps after removing the aforementioned - * dot) unless CFN_KEEP_TRAILING_SLASH is flagged, will even collapse ".." - * elements (except at the start of the string) if CFN_COLLAPSE_DOT_DOT_DIRS - * is flagged. If the resulting name would be empty, we return ".". */ +/* Turns multiple adjacent slashes into a single slash, drops all leading or + * interior "." elements unless CFN_KEEP_DOT_DIRS is flagged. Will also drop + * a trailing '.' after a '/' if CFN_DROP_TRAILING_DOT_DIR is flagged, removes + * a trailing slash (perhaps after removing the aforementioned dot) unless + * CFN_KEEP_TRAILING_SLASH is flagged, and will also collapse ".." elements + * (except at the start) if CFN_COLLAPSE_DOT_DOT_DIRS is flagged. If the + * resulting name would be empty, returns ".". */ unsigned int clean_fname(char *name, int flags) { char *limit = name - 1, *t = name, *f = name; @@ -704,7 +736,7 @@ unsigned int clean_fname(char *name, int flags) if ((anchored = *f == '/') != 0) *t++ = *f++; - else if (flags & CFN_KEEP_LEADING_DOT_DIR && *f == '.' && f[1] == '/') { + else if (flags & CFN_KEEP_DOT_DIRS && *f == '.' && f[1] == '/') { *t++ = *f++; *t++ = *f++; } @@ -716,7 +748,7 @@ unsigned int clean_fname(char *name, int flags) } if (*f == '.') { /* discard interior "." dirs */ - if (f[1] == '/') { + if (f[1] == '/' && !(flags & CFN_KEEP_DOT_DIRS)) { f += 2; continue; } @@ -773,10 +805,11 @@ unsigned int clean_fname(char *name, int flags) * ALWAYS collapses ".." elements (except for those at the start of the * string up to "depth" deep). If the resulting name would be empty, * change it into a ".". */ -char *sanitize_path(char *dest, const char *p, const char *rootdir, int depth) +char *sanitize_path(char *dest, const char *p, const char *rootdir, int depth, + int flags) { char *start, *sanp; - int rlen = 0, leave_one_dotdir = relative_paths; + int rlen = 0, drop_dot_dirs = !relative_paths || !(flags & SP_KEEP_DOT_DIRS); if (dest != p) { int plen = strlen(p); @@ -799,21 +832,22 @@ char *sanitize_path(char *dest, const char *p, const char *rootdir, int depth) } } + if (drop_dot_dirs) { + while (*p == '.' && p[1] == '/') + p += 2; + } + start = sanp = dest + rlen; + /* This loop iterates once per filename component in p, pointing at + * the start of the name (past any prior slash) for each iteration. */ while (*p) { /* discard leading or extra slashes */ if (*p == '/') { p++; continue; } - /* this loop iterates once per filename component in p. - * both p (and sanp if the original had a slash) should - * always be left pointing after a slash - */ - if (*p == '.' && (p[1] == '/' || p[1] == '\0')) { - if (leave_one_dotdir && p[1]) - leave_one_dotdir = 0; - else { + if (drop_dot_dirs) { + if (*p == '.' && (p[1] == '/' || p[1] == '\0')) { /* skip "." component */ p++; continue; @@ -826,10 +860,8 @@ char *sanitize_path(char *dest, const char *p, const char *rootdir, int depth) if (sanp != start) { /* back up sanp one level */ --sanp; /* now pointing at slash */ - while (sanp > start && sanp[-1] != '/') { - /* skip back up to slash */ + while (sanp > start && sanp[-1] != '/') sanp--; - } } continue; } @@ -895,8 +927,8 @@ int push_dir(const char *dir, int set_path_only) curr_dir_depth = count_dir_elements(curr_dir + module_dirlen); } - if (verbose >= 5) - rprintf(FINFO, "[%s] dir is now %s\n", who_am_i(), curr_dir); + if (verbose >= 5 && !set_path_only) + rprintf(FINFO, "[%s] push_dir(%s)\n", who_am_i(), curr_dir); return 1; } @@ -916,6 +948,9 @@ int pop_dir(const char *dir) if (sanitize_paths) curr_dir_depth = count_dir_elements(curr_dir + module_dirlen); + if (verbose >= 5) + rprintf(FINFO, "[%s] pop_dir(%s)\n", who_am_i(), curr_dir); + return 1; } @@ -974,13 +1009,13 @@ char *partial_dir_fname(const char *fname) fn = fname; if ((int)pathjoin(t, sz, partial_dir, fn) >= sz) return NULL; - if (server_filter_list.head) { + if (daemon_filter_list.head) { t = strrchr(partial_fname, '/'); *t = '\0'; - if (check_filter(&server_filter_list, partial_fname, 1) < 0) + if (check_filter(&daemon_filter_list, partial_fname, 1) < 0) return NULL; *t = '/'; - if (check_filter(&server_filter_list, partial_fname, 0) < 0) + if (check_filter(&daemon_filter_list, partial_fname, 0) < 0) return NULL; }