X-Git-Url: https://mattmccutchen.net/rsync/rsync.git/blobdiff_plain/1c3344a105f656a45f6b46e1710c3fc4ab862a9a..d48810ba5b0b8d09b272092a70da1255c2346ab8:/util.c diff --git a/util.c b/util.c index 4e03a197..a8b17d53 100644 --- a/util.c +++ b/util.c @@ -4,11 +4,12 @@ * 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 version 2 as - * published by the Free Software Foundation. + * 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 @@ -16,11 +17,11 @@ * 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, write to the Free Software Foundation, Inc., - * 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. + * with this program; if not, visit the http://fsf.org website. */ #include "rsync.h" +#include "ifuncs.h" extern int verbose; extern int dry_run; @@ -28,10 +29,11 @@ extern int module_id; extern int modify_window; extern int relative_paths; extern int human_readable; +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; @@ -89,9 +91,9 @@ int fd_pair(int fd[2]) return ret; } -void print_child_argv(char **cmd) +void print_child_argv(const char *prefix, char **cmd) { - rprintf(FCLIENT, "opening connection using "); + rprintf(FCLIENT, "%s ", prefix); for (; *cmd; cmd++) { /* Look for characters that ought to be quoted. This * is not a great quoting algorithm, but it's @@ -145,12 +147,13 @@ 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); -#elif defined HAVE_UTIMBUF +#elif defined HAVE_STRUCT_UTIMBUF struct utimbuf tbuf; tbuf.actime = time(NULL); tbuf.modtime = modtime; @@ -259,38 +262,41 @@ static int safe_read(int desc, char *ptr, size_t len) return n_chars; } -/** Copy a file. +/* Copy a file. If ofd < 0, copy_file unlinks and opens the "dest" file. + * Otherwise, it just writes to and closes the provided file descriptor. * * This is used in conjunction with the --temp-dir, --backup, and * --copy-dest options. */ -int copy_file(const char *source, const char *dest, mode_t mode) +int copy_file(const char *source, const char *dest, int ofd, + mode_t mode, int create_bak_dir) { int ifd; - int ofd; char buf[1024 * 8]; int len; /* Number of bytes read into `buf'. */ - ifd = do_open(source, O_RDONLY, 0); - if (ifd == -1) { - rsyserr(FERROR, errno, "open %s", full_fname(source)); + if ((ifd = do_open(source, O_RDONLY, 0)) < 0) { + rsyserr(FERROR_XFER, errno, "open %s", full_fname(source)); return -1; } - if (robust_unlink(dest) && errno != ENOENT) { - rsyserr(FERROR, errno, "unlink %s", full_fname(dest)); - return -1; - } + if (ofd < 0) { + if (robust_unlink(dest) && errno != ENOENT) { + rsyserr(FERROR_XFER, errno, "unlink %s", full_fname(dest)); + return -1; + } - ofd = do_open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, mode); - if (ofd == -1) { - rsyserr(FERROR, errno, "open %s", full_fname(dest)); - close(ifd); - 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_XFER, errno, "open %s", full_fname(dest)); + close(ifd); + return -1; + } } 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; @@ -298,19 +304,19 @@ int copy_file(const char *source, const char *dest, mode_t mode) } 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; } @@ -366,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); } @@ -405,7 +411,7 @@ int robust_rename(const char *from, const char *to, const char *partialptr, return -1; to = partialptr; } - if (copy_file(from, to, mode) != 0) + if (copy_file(from, to, -1, mode, 0) != 0) return -2; do_unlink(from); return 1; @@ -460,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 */ @@ -501,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; } @@ -518,24 +519,25 @@ static int filter_server_path(char *arg) return 0; } -static void glob_expand_one(char *s, char ***argv_ptr, int *argc_ptr, - int *maxargs_ptr) +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; if (!(argv = realloc_array(argv, char *, maxargs))) - out_of_memory("glob_expand_one"); + out_of_memory("glob_expand"); *argv_ptr = argv; *maxargs_ptr = maxargs; } if (!*s) s = "."; s = argv[argc++] = strdup(s); - filter_server_path(s); + filter_daemon_path(s); #else glob_t globbuf; @@ -545,65 +547,97 @@ static void glob_expand_one(char *s, char ***argv_ptr, int *argc_ptr, s = "."; if (sanitize_paths) - s = sanitize_path(NULL, s, "", 0, NULL); - else + s = sanitize_path(NULL, s, "", 0, SP_KEEP_DOT_DIRS); + else { s = strdup(s); + 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_one"); + 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_one"); + out_of_memory("glob_expand"); } - } + } else + argv[argc++] = s; globfree(&globbuf); #endif *argc_ptr = argc; } /* This routine is only used in daemon mode. */ -void glob_expand(char *base1, char ***argv_ptr, int *argc_ptr, int *maxargs_ptr) +void glob_expand_module(char *base1, char *arg, char ***argv_ptr, int *argc_ptr, int *maxargs_ptr) { - char *s = (*argv_ptr)[*argc_ptr]; - char *p, *q; + char *p, *s; char *base = base1; int base_len = strlen(base); - if (!s || !*s) + if (!arg || !*arg) return; - if (strncmp(s, base, base_len) == 0) - s += base_len; + if (strncmp(arg, base, base_len) == 0) + arg += base_len; - if (!(s = strdup(s))) - out_of_memory("glob_expand"); + if (!(arg = strdup(arg))) + out_of_memory("glob_expand_module"); if (asprintf(&base," %s/", base1) <= 0) - out_of_memory("glob_expand"); + out_of_memory("glob_expand_module"); base_len++; - for (q = s; *q; q = p + base_len) { - if ((p = strstr(q, base)) != NULL) + for (s = arg; *s; s = p + base_len) { + if ((p = strstr(s, base)) != NULL) *p = '\0'; /* split it at this point */ - glob_expand_one(q, argv_ptr, argc_ptr, maxargs_ptr); + glob_expand(s, argv_ptr, argc_ptr, maxargs_ptr); if (!p) break; } - free(s); + free(arg); free(base); } @@ -685,11 +719,14 @@ int count_dir_elements(const char *p) return cnt; } -/* Turns multiple adjacent slashes into a single slash, gets rid of "./" - * elements (but not a trailing dot dir), removes a trailing slash, and - * optionally collapses ".." elements (except for those at the start of the - * string). If the resulting name would be empty, change it into a ".". */ -unsigned int clean_fname(char *name, BOOL collapse_dot_dot) +/* 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; int anchored; @@ -699,6 +736,10 @@ unsigned int clean_fname(char *name, BOOL collapse_dot_dot) if ((anchored = *f == '/') != 0) *t++ = *f++; + else if (flags & CFN_KEEP_DOT_DIRS && *f == '.' && f[1] == '/') { + *t++ = *f++; + *t++ = *f++; + } while (*f) { /* discard extra slashes */ if (*f == '/') { @@ -706,14 +747,16 @@ unsigned int clean_fname(char *name, BOOL collapse_dot_dot) continue; } if (*f == '.') { - /* discard "." dirs (but NOT a trailing '.'!) */ - if (f[1] == '/') { + /* discard interior "." dirs */ + if (f[1] == '/' && !(flags & CFN_KEEP_DOT_DIRS)) { f += 2; continue; } + if (f[1] == '\0' && flags & CFN_DROP_TRAILING_DOT_DIR) + break; /* collapse ".." dirs */ - if (collapse_dot_dot - && f[1] == '.' && (f[2] == '/' || !f[2])) { + if (flags & CFN_COLLAPSE_DOT_DOT_DIRS + && f[1] == '.' && (f[2] == '/' || !f[2])) { char *s = t - 1; if (s == name && anchored) { f += 2; @@ -731,7 +774,7 @@ unsigned int clean_fname(char *name, BOOL collapse_dot_dot) while (*f && (*t++ = *f++) != '/') {} } - if (t > name+anchored && t[-1] == '/') + if (t > name+anchored && t[-1] == '/' && !(flags & CFN_KEEP_TRAILING_SLASH)) t--; if (t == name) *t++ = '.'; @@ -749,11 +792,10 @@ unsigned int clean_fname(char *name, BOOL collapse_dot_dot) * rootdir will be ignored to avoid expansion of the string. * * The rootdir string contains a value to use in place of a leading slash. - * Specify NULL to get the default of lp_path(module_id). + * 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: @@ -764,21 +806,16 @@ unsigned int clean_fname(char *name, BOOL collapse_dot_dot) * 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); if (*p == '/') { if (!rootdir) - rootdir = lp_path(module_id); + rootdir = module_dir; rlen = strlen(rootdir); depth = 0; p++; @@ -795,32 +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; - 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; @@ -829,19 +856,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; } @@ -883,8 +903,10 @@ int push_dir(const char *dir, int set_path_only) if (len == 1 && *dir == '.') return 1; - if ((*dir == '/' ? len : curr_dir_len + 1 + len) >= sizeof curr_dir) + if ((*dir == '/' ? len : curr_dir_len + 1 + len) >= sizeof curr_dir) { + errno = ENAMETOOLONG; return 0; + } if (!set_path_only && chdir(dir)) return 0; @@ -898,13 +920,16 @@ int push_dir(const char *dir, int set_path_only) curr_dir_len += len; } - curr_dir_len = clean_fname(curr_dir, 1); + curr_dir_len = clean_fname(curr_dir, CFN_COLLAPSE_DOT_DOT_DIRS); if (sanitize_paths) { if (module_dirlen > curr_dir_len) module_dirlen = curr_dir_len; 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; } @@ -923,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; } @@ -981,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; } @@ -1252,11 +1280,11 @@ int _Insure_trap_error(int a1, int a2, int a3, int a4, int a5, int a6) #define MALLOC_MAX 0x40000000 -void *_new_array(unsigned int size, unsigned long num) +void *_new_array(unsigned long num, unsigned int size, int use_calloc) { if (num >= MALLOC_MAX/size) return NULL; - return malloc(size * num); + return use_calloc ? calloc(num, size) : malloc(num * size); } void *_realloc_array(void *ptr, unsigned int size, unsigned long num) @@ -1475,7 +1503,7 @@ void *expand_item_list(item_list *lp, size_t item_size, void *new_ptr; size_t new_size = lp->malloced; if (incr < 0) - new_size -= incr; /* increase slowly */ + new_size += -incr; /* increase slowly */ else if (new_size < (size_t)incr) new_size += incr; else