X-Git-Url: https://mattmccutchen.net/rsync/rsync.git/blobdiff_plain/4f9b139ab9e8d46895c92865335b729f967abef2..91f625cee02e1db0ebd6f40d3ccfaf655b72c9ab:/util.c diff --git a/util.c b/util.c index 75b6e3fc..99dff805 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; @@ -147,8 +147,9 @@ int set_modtime(const char *fname, time_t modtime, mode_t mode) t[1].tv_usec = 0; # ifdef HAVE_LUTIMES if (S_ISLNK(mode)) { - lutimes(fname, t); - return 0; /* ignore errors */ + if (lutimes(fname, t) < 0) + return errno == ENOSYS ? 1 : -1; + return 0; } # endif return utimes(fname, t); @@ -274,20 +275,20 @@ int copy_file(const char *source, const char *dest, int ofd, int len; /* Number of bytes read into `buf'. */ if ((ifd = do_open(source, O_RDONLY, 0)) < 0) { - rsyserr(FERROR, errno, "open %s", full_fname(source)); + rsyserr(FERROR_XFER, errno, "open %s", full_fname(source)); return -1; } if (ofd < 0) { if (robust_unlink(dest) && errno != ENOENT) { - rsyserr(FERROR, errno, "unlink %s", full_fname(dest)); + rsyserr(FERROR_XFER, errno, "unlink %s", full_fname(dest)); return -1; } if ((ofd = do_open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, mode)) < 0 && (!create_bak_dir || errno != ENOENT || make_bak_dir(dest) < 0 || (ofd = do_open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, mode)) < 0)) { - rsyserr(FERROR, errno, "open %s", full_fname(dest)); + rsyserr(FERROR_XFER, errno, "open %s", full_fname(dest)); close(ifd); return -1; } @@ -295,7 +296,7 @@ int copy_file(const char *source, const char *dest, int ofd, while ((len = safe_read(ifd, buf, sizeof buf)) > 0) { if (full_write(ofd, buf, len) < 0) { - rsyserr(FERROR, errno, "write %s", full_fname(dest)); + rsyserr(FERROR_XFER, errno, "write %s", full_fname(dest)); close(ifd); close(ofd); return -1; @@ -303,19 +304,19 @@ int copy_file(const char *source, const char *dest, int ofd, } if (len < 0) { - rsyserr(FERROR, errno, "read %s", full_fname(source)); + rsyserr(FERROR_XFER, errno, "read %s", full_fname(source)); close(ifd); close(ofd); return -1; } if (close(ifd) < 0) { - rsyserr(FINFO, errno, "close failed on %s", + rsyserr(FWARNING, errno, "close failed on %s", full_fname(source)); } if (close(ofd) < 0) { - rsyserr(FERROR, errno, "close failed on %s", + rsyserr(FERROR_XFER, errno, "close failed on %s", full_fname(dest)); return -1; } @@ -371,7 +372,7 @@ int robust_unlink(const char *fname) } while ((rc = access(path, 0)) == 0 && counter != start); if (verbose > 0) { - rprintf(FINFO,"renaming %s to %s because of text busy\n", + rprintf(FWARNING, "renaming %s to %s because of text busy\n", fname, path); } @@ -465,31 +466,27 @@ void kill_all(int sig) } /** Turn a user name into a uid */ -int name_to_uid(const char *name, uid_t *uid) +int name_to_uid(const char *name, uid_t *uid_p) { struct passwd *pass; if (!name || !*name) return 0; - pass = getpwnam(name); - if (pass) { - *uid = pass->pw_uid; - return 1; - } - return 0; + if (!(pass = getpwnam(name))) + return 0; + *uid_p = pass->pw_uid; + return 1; } /** Turn a group name into a gid */ -int name_to_gid(const char *name, gid_t *gid) +int name_to_gid(const char *name, gid_t *gid_p) { struct group *grp; if (!name || !*name) return 0; - grp = getgrnam(name); - if (grp) { - *gid = grp->gr_gid; - return 1; - } - return 0; + if (!(grp = getgrnam(name))) + return 0; + *gid_p = grp->gr_gid; + return 1; } /** Lock a byte range in a open file */ @@ -506,82 +503,186 @@ int lock_range(int fd, int offset, int len) return fcntl(fd,F_SETLK,&lock) == 0; } -static int filter_server_path(char *arg) -{ - char *s; +#define ENSURE_MEMSPACE(buf, type, sz, req) \ + if ((req) >= sz && !(buf = realloc_array(buf, type, sz *= 2))) \ + out_of_memory("ENSURE_MEMSPACE") - if (server_filter_list.head) { - for (s = arg; (s = strchr(s, '/')) != NULL; ) { - *s = '\0'; - if (check_filter(&server_filter_list, arg, 1) < 0) { - /* We must leave arg truncated! */ - return 1; +static inline void call_glob_match(const char *name, int len, int from_glob, + char *arg, int abpos, int fbpos); + +static struct glob_data { + char *arg_buf, *filt_buf, **argv; + int absize, fbsize, maxargs, argc; +} glob; + +static void glob_match(char *arg, int abpos, int fbpos) +{ + int len; + char *slash; + + while (*arg == '.' && arg[1] == '/') { + if (fbpos < 0) { + if (glob.fbsize < glob.absize) { + glob.filt_buf = realloc_array(glob.filt_buf, + char, glob.fbsize = glob.absize); } - *s++ = '/'; + memcpy(glob.filt_buf, glob.arg_buf, abpos + 1); + fbpos = abpos; } + ENSURE_MEMSPACE(glob.arg_buf, char, glob.absize, abpos + 2); + glob.arg_buf[abpos++] = *arg++; + glob.arg_buf[abpos++] = *arg++; + glob.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 ? glob.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; -#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(glob.arg_buf, char, glob.absize, abpos + len + 2); + memcpy(glob.arg_buf + abpos, name, len); + abpos += len; + glob.arg_buf[abpos] = '\0'; + + if (fbpos >= 0) { + ENSURE_MEMSPACE(glob.filt_buf, char, glob.fbsize, fbpos + len + 2); + memcpy(glob.filt_buf + fbpos, name, len); + fbpos += len; + glob.filt_buf[fbpos] = '\0'; + use_buf = glob.filt_buf; + } else + use_buf = glob.arg_buf; + + if (from_glob || arg) { + STRUCT_STAT st; + int is_dir; + + if (do_stat(glob.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_server_path(s); -#else - glob_t globbuf; - if (maxargs <= argc) - return; - if (!*s) - s = "."; + if (arg) { + glob.arg_buf[abpos++] = '/'; + glob.arg_buf[abpos] = '\0'; + if (fbpos >= 0) { + glob.filt_buf[fbpos++] = '/'; + glob.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(glob.arg_buf))) + out_of_memory("glob_match"); + } +} - if (sanitize_paths) - s = sanitize_path(NULL, s, "", 0, NULL); - else - s = strdup(s); - if (!s) - out_of_memory("glob_expand"); +/* This routine performs wild-card expansion of the pathname in "arg". Any + * daemon-excluded files/dirs will not be matched by the wildcards. Returns 0 + * if a wild-card string is the only returned item (due to matching nothing). */ +int glob_expand(const char *arg, char ***argv_p, int *argc_p, int *maxargs_p) +{ + int ret, save_argc; + char *s; - 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; - if (!(argv = realloc_array(argv, char *, maxargs))) - out_of_memory("glob_expand"); - *argv_ptr = argv; - *maxargs_ptr = maxargs; + if (!arg) { + if (glob.filt_buf) + free(glob.filt_buf); + free(glob.arg_buf); + memset(&glob, 0, sizeof glob); + return -1; } - if (globbuf.gl_pathc == 0) - argv[argc++] = s; + + if (sanitize_paths) + s = sanitize_path(NULL, arg, "", 0, SP_KEEP_DOT_DIRS); else { - int i; + s = strdup(arg); + if (!s) + out_of_memory("glob_expand"); + clean_fname(s, CFN_KEEP_DOT_DIRS + | CFN_KEEP_TRAILING_SLASH + | CFN_COLLAPSE_DOT_DOT_DIRS); + } + + if (glob.absize < MAXPATHLEN + && !(glob.arg_buf = realloc_array(glob.arg_buf, char, glob.absize = MAXPATHLEN))) + out_of_memory("glob_expand"); + *glob.arg_buf = '\0'; + + glob.argc = save_argc = *argc_p; + glob.argv = *argv_p; + glob.maxargs = *maxargs_p; + + if (glob.maxargs < 100 + && !(glob.argv = realloc_array(glob.argv, char *, glob.maxargs = 100))) + 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; + ret = 0; + } else { free(s); - for (i = 0; i < (int)globbuf.gl_pathc; i++) { - if (!(argv[argc++] = strdup(globbuf.gl_pathv[i]))) - out_of_memory("glob_expand"); - } + ret = 1; } - globfree(&globbuf); -#endif - *argc_ptr = argc; + + *maxargs_p = glob.maxargs; + *argv_p = glob.argv; + *argc_p = glob.argc; + + return ret; } /* 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; @@ -603,7 +704,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; } @@ -690,13 +791,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; @@ -707,7 +808,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++; } @@ -719,7 +820,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; } @@ -766,8 +867,7 @@ unsigned int clean_fname(char *name, int flags) * Specify NULL to get the default of "module_dir". * * The depth var is a count of how many '..'s to allow at the start of the - * path. If symlink is set, combine its value with the "p" value to get - * the target path, and **return NULL if any '..'s try to escape**. + * path. * * We also clean the path in a manner similar to clean_fname() but with a * few differences: @@ -778,15 +878,10 @@ unsigned int clean_fname(char *name, int flags) * 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, - const char *symlink) + int flags) { - char *start, *sanp, *save_dest = dest; - int rlen = 0, leave_one_dotdir = relative_paths; - - if (symlink && *symlink == '/') { - p = symlink; - symlink = ""; - } + char *start, *sanp; + int rlen = 0, drop_dot_dirs = !relative_paths || !(flags & SP_KEEP_DOT_DIRS); if (dest != p) { int plen = strlen(p); @@ -809,32 +904,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; - while (1) { - if (*p == '\0') { - if (!symlink || !*symlink) - break; - while (sanp != start && sanp[-1] != '/') { - /* strip last element */ - sanp--; - } - /* Append a relative symlink */ - p = symlink; - symlink = ""; - } + /* 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; @@ -843,19 +928,12 @@ char *sanitize_path(char *dest, const char *p, const char *rootdir, int depth, if (*p == '.' && p[1] == '.' && (p[2] == '/' || p[2] == '\0')) { /* ".." component followed by slash or end */ if (depth <= 0 || sanp != start) { - if (symlink && sanp == start) { - if (!save_dest) - free(dest); - return NULL; - } p += 2; 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; } @@ -921,6 +999,9 @@ int push_dir(const char *dir, int set_path_only) curr_dir_depth = count_dir_elements(curr_dir + module_dirlen); } + if (verbose >= 5 && !set_path_only) + rprintf(FINFO, "[%s] push_dir(%s)\n", who_am_i(), curr_dir); + return 1; } @@ -939,6 +1020,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; } @@ -997,13 +1081,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; }