From: Wayne Davison Date: Fri, 21 Mar 2008 05:51:03 +0000 (-0700) Subject: Changed the arg-globbing routine to use a custom arg-globbing algorithm X-Git-Url: https://mattmccutchen.net/rsync/rsync.git/commitdiff_plain/4d30f17671bdcc0f17facb133ea283eea6f0dc12 Changed the arg-globbing routine to use a custom arg-globbing algorithm that does not include any daemon-excluded items in the matches. It is also not subverted by the presence of one or more dot-dir elements in an arg. --- diff --git a/Makefile.in b/Makefile.in index e8e8cb3d..b91b3e97 100644 --- a/Makefile.in +++ b/Makefile.in @@ -111,7 +111,7 @@ TRIMSLASH_OBJ = trimslash.o syscall.o lib/compat.o lib/snprintf.o trimslash$(EXEEXT): $(TRIMSLASH_OBJ) $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(TRIMSLASH_OBJ) $(LIBS) -T_UNSAFE_OBJ = t_unsafe.o syscall.o util.o t_stub.o lib/compat.o lib/snprintf.o +T_UNSAFE_OBJ = t_unsafe.o syscall.o util.o t_stub.o lib/compat.o lib/snprintf.o lib/wildmatch.o t_unsafe$(EXEEXT): $(T_UNSAFE_OBJ) $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(T_UNSAFE_OBJ) $(LIBS) diff --git a/configure.in b/configure.in index ba217779..39e27186 100644 --- a/configure.in +++ b/configure.in @@ -311,7 +311,7 @@ AC_HEADER_SYS_WAIT AC_CHECK_HEADERS(sys/fcntl.h sys/select.h fcntl.h sys/time.h sys/unistd.h \ unistd.h utime.h grp.h compat.h sys/param.h ctype.h sys/wait.h \ sys/ioctl.h sys/filio.h string.h stdlib.h sys/socket.h sys/mode.h \ - sys/un.h sys/attr.h glob.h mcheck.h arpa/inet.h arpa/nameser.h locale.h \ + sys/un.h sys/attr.h mcheck.h arpa/inet.h arpa/nameser.h locale.h \ netdb.h malloc.h float.h limits.h iconv.h libcharset.h langinfo.h \ sys/acl.h acl/libacl.h attr/xattr.h sys/xattr.h sys/extattr.h \ popt.h popt/popt.h) @@ -550,7 +550,7 @@ AC_FUNC_UTIME_NULL AC_FUNC_ALLOCA AC_CHECK_FUNCS(waitpid wait4 getcwd strdup chown chmod lchmod mknod mkfifo \ fchmod fstat ftruncate strchr readlink link utime utimes lutimes strftime \ - memmove lchown vsnprintf snprintf vasprintf asprintf setsid glob strpbrk \ + memmove lchown vsnprintf snprintf vasprintf asprintf setsid strpbrk \ strlcat strlcpy strtol mallinfo getgroups setgroups geteuid getegid \ setlocale setmode open64 lseek64 mkstemp64 mtrace va_copy __va_copy \ strerror putenv iconv_open locale_charset nl_langinfo getxattr \ diff --git a/rsync.h b/rsync.h index 7f7e3531..6be36d33 100644 --- a/rsync.h +++ b/rsync.h @@ -336,10 +336,6 @@ enum msgcode { #endif #endif -#ifdef HAVE_GLOB_H -#include -#endif - /* these are needed for the uid/gid mapping code */ #include #include diff --git a/util.c b/util.c index a8b17d53..ad29be3d 100644 --- a/util.c +++ b/util.c @@ -503,53 +503,132 @@ int lock_range(int fd, int offset, int len) return fcntl(fd,F_SETLK,&lock) == 0; } -static int filter_daemon_path(char *arg) +#define ENSURE_MEMSPACE(buf, type, sz, req) \ + if ((req) >= sz && !(buf = realloc_array(buf, type, sz *= 2))) \ + out_of_memory("ENSURE_MEMSPACE") + +static inline void call_glob_match(const char *name, int len, int from_glob, + char *arg, int abpos, int fbpos); + +static char *arg_buf, *filt_buf, **glob_argv; +static int absize, fbsize, glob_maxargs, glob_argc; + +static void glob_match(char *arg, int abpos, int fbpos) { - if (daemon_filter_list.head) { - char *s; - for (s = arg; (s = strchr(s, '/')) != NULL; ) { - *s = '\0'; - if (check_filter(&daemon_filter_list, arg, 1) < 0) { - /* We must leave arg truncated! */ - return 1; - } - *s++ = '/'; + int len; + char *slash; + + while (*arg == '.' && arg[1] == '/') { + if (fbpos < 0) { + if (fbsize < absize) + filt_buf = realloc_array(filt_buf, char, fbsize = absize); + memcpy(filt_buf, arg_buf, abpos + 1); + fbpos = abpos; } + ENSURE_MEMSPACE(arg_buf, char, absize, abpos + 2); + arg_buf[abpos++] = *arg++; + arg_buf[abpos++] = *arg++; + arg_buf[abpos] = '\0'; } - return 0; + if ((slash = strchr(arg, '/')) != NULL) { + *slash = '\0'; + len = slash - arg; + } else + len = strlen(arg); + if (strpbrk(arg, "*?[")) { + struct dirent *di; + DIR *d; + + if (!(d = opendir(abpos ? arg_buf : "."))) + return; + while ((di = readdir(d)) != NULL) { + char *dname = d_name(di); + if (dname[0] == '.' && (dname[1] == '\0' + || (dname[1] == '.' && dname[2] == '\0'))) + continue; + if (!wildmatch(arg, dname)) + continue; + call_glob_match(dname, strlen(dname), 1, + slash ? arg + len + 1 : NULL, + abpos, fbpos); + } + closedir(d); + } else { + call_glob_match(arg, len, 0, + slash ? arg + len + 1 : NULL, + abpos, fbpos); + } + if (slash) + *slash = '/'; } -void glob_expand(char *s, char ***argv_ptr, int *argc_ptr, int *maxargs_ptr) +static inline void call_glob_match(const char *name, int len, int from_glob, + char *arg, int abpos, int fbpos) { - 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; - if (!(argv = realloc_array(argv, char *, maxargs))) - out_of_memory("glob_expand"); - *argv_ptr = argv; - *maxargs_ptr = maxargs; + char *use_buf; + + ENSURE_MEMSPACE(arg_buf, char, absize, abpos + len + 2); + memcpy(arg_buf + abpos, name, len); + abpos += len; + arg_buf[abpos] = '\0'; + + if (fbpos >= 0) { + ENSURE_MEMSPACE(filt_buf, char, fbsize, fbpos + len + 2); + memcpy(filt_buf + fbpos, name, len); + fbpos += len; + filt_buf[fbpos] = '\0'; + use_buf = filt_buf; + } else + use_buf = arg_buf; + + if (from_glob || arg) { + STRUCT_STAT st; + int is_dir; + + if (do_stat(arg_buf, &st) != 0) { + if (from_glob) + return; + is_dir = 0; + } else { + is_dir = S_ISDIR(st.st_mode) != 0; + if (arg && !is_dir) + return; + } + + if (daemon_filter_list.head + && check_filter(&daemon_filter_list, use_buf, is_dir) < 0) { + if (from_glob) + return; + arg = NULL; + } } - if (!*s) - s = "."; - s = argv[argc++] = strdup(s); - filter_daemon_path(s); -#else - glob_t globbuf; - if (maxargs <= argc) - return; - if (!*s) - s = "."; + if (arg) { + arg_buf[abpos++] = '/'; + arg_buf[abpos] = '\0'; + if (fbpos >= 0) { + filt_buf[fbpos++] = '/'; + filt_buf[fbpos] = '\0'; + } + glob_match(arg, abpos, fbpos); + } else { + ENSURE_MEMSPACE(glob_argv, char *, glob_maxargs, glob_argc + 1); + if (!(glob_argv[glob_argc++] = strdup(arg_buf))) + out_of_memory("glob_match"); + } +} + +/* This routine performs wild-card expansion of the pathname in "arg". Any + * daemon-excluded files/dirs will not be matched by the wildcards. */ +void glob_expand(const char *arg, char ***argv_p, int *argc_p, int *maxargs_p) +{ + char *s; + int save_argc = *argc_p; if (sanitize_paths) - s = sanitize_path(NULL, s, "", 0, SP_KEEP_DOT_DIRS); + s = sanitize_path(NULL, arg, "", 0, SP_KEEP_DOT_DIRS); else { - s = strdup(s); + s = strdup(arg); if (!s) out_of_memory("glob_expand"); clean_fname(s, CFN_KEEP_DOT_DIRS @@ -557,60 +636,40 @@ void glob_expand(char *s, char ***argv_ptr, int *argc_ptr, int *maxargs_ptr) | CFN_COLLAPSE_DOT_DOT_DIRS); } - memset(&globbuf, 0, sizeof globbuf); - 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 (have_glob_results) { - int i; - free(s); - for (i = 0; i < count; i++) { - if (!(argv[argc++] = strdup(globbuf.gl_pathv[i]))) - out_of_memory("glob_expand"); - } + if (!(arg_buf = new_array(char, absize = MAXPATHLEN))) + out_of_memory("glob_expand"); + *arg_buf = '\0'; + filt_buf = NULL; + fbsize = 0; + + glob_argc = *argc_p; + glob_argv = *argv_p; + glob_maxargs = *maxargs_p; + + if (glob_maxargs < MAX_ARGS + && !(glob_argv = realloc_array(glob_argv, char *, glob_maxargs = MAX_ARGS))) + out_of_memory("glob_expand"); + + glob_match(s, 0, -1); + + /* The arg didn't match anything, so add the failed arg to the list. */ + if (glob_argc == save_argc) { + ENSURE_MEMSPACE(glob_argv, char *, glob_maxargs, glob_argc + 1); + glob_argv[glob_argc++] = s; } else - argv[argc++] = s; - globfree(&globbuf); -#endif - *argc_ptr = argc; + free(s); + + *maxargs_p = glob_maxargs; + *argv_p = glob_argv; + *argc_p = glob_argc; + + if (filt_buf) + free(filt_buf); + free(arg_buf); } /* This routine is only used in daemon mode. */ -void glob_expand_module(char *base1, char *arg, char ***argv_ptr, int *argc_ptr, int *maxargs_ptr) +void glob_expand_module(char *base1, char *arg, char ***argv_p, int *argc_p, int *maxargs_p) { char *p, *s; char *base = base1; @@ -632,7 +691,7 @@ void glob_expand_module(char *base1, char *arg, char ***argv_ptr, int *argc_ptr, for (s = arg; *s; s = p + base_len) { if ((p = strstr(s, base)) != NULL) *p = '\0'; /* split it at this point */ - glob_expand(s, argv_ptr, argc_ptr, maxargs_ptr); + glob_expand(s, argv_p, argc_p, maxargs_p); if (!p) break; }