X-Git-Url: https://mattmccutchen.net/rsync/rsync.git/blobdiff_plain/2cd421d80978b9a1ab7ec0bd0eca50c03ef38a33..1b42f628f495ff0cdaa8a7c219d8ce33192281fe:/util.c diff --git a/util.c b/util.c index d8d396cf..7a320258 100644 --- a/util.c +++ b/util.c @@ -4,11 +4,11 @@ * Copyright (C) 1996-2000 Andrew Tridgell * Copyright (C) 1996 Paul Mackerras * Copyright (C) 2001, 2002 Martin Pool - * Copyright (C) 2003, 2004, 2005, 2006 Wayne Davison + * Copyright (C) 2003-2007 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 2 of the License, or + * 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, @@ -17,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; @@ -29,6 +29,7 @@ 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; @@ -45,7 +46,7 @@ void set_nonblocking(int fd) { int val; - if ((val = fcntl(fd, F_GETFL, 0)) == -1) + if ((val = fcntl(fd, F_GETFL)) == -1) return; if (!(val & NONBLOCK_FLAG)) { val |= NONBLOCK_FLAG; @@ -58,7 +59,7 @@ void set_blocking(int fd) { int val; - if ((val = fcntl(fd, F_GETFL, 0)) == -1) + if ((val = fcntl(fd, F_GETFL)) == -1) return; if (val & NONBLOCK_FLAG) { val &= ~NONBLOCK_FLAG; @@ -90,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 @@ -109,19 +110,19 @@ void print_child_argv(char **cmd) rprintf(FCLIENT, "\n"); } -void out_of_memory(char *str) +NORETURN void out_of_memory(const char *str) { rprintf(FERROR, "ERROR: out of memory in %s [%s]\n", str, who_am_i()); exit_cleanup(RERR_MALLOC); } -void overflow_exit(char *str) +NORETURN void overflow_exit(const char *str) { rprintf(FERROR, "ERROR: buffer overflow in %s [%s]\n", str, who_am_i()); exit_cleanup(RERR_MALLOC); } -int set_modtime(char *fname, time_t modtime, mode_t mode) +int set_modtime(const char *fname, time_t modtime, mode_t mode) { #if !defined HAVE_LUTIMES || !defined HAVE_UTIMES if (S_ISLNK(mode)) @@ -145,8 +146,10 @@ int set_modtime(char *fname, time_t modtime, mode_t mode) t[1].tv_sec = modtime; t[1].tv_usec = 0; # ifdef HAVE_LUTIMES - if (S_ISLNK(mode)) - return lutimes(fname, t); + if (S_ISLNK(mode)) { + lutimes(fname, t); + return 0; /* ignore errors */ + } # endif return utimes(fname, t); #elif defined HAVE_UTIMBUF @@ -214,7 +217,7 @@ int create_directory_path(char *fname) * * Derived from GNU C's cccp.c. */ -int full_write(int desc, char *ptr, size_t len) +int full_write(int desc, const char *ptr, size_t len) { int total_written; @@ -359,7 +362,7 @@ int robust_unlink(const char *fname) /* start where the last one left off to reduce chance of clashes */ start = counter; do { - sprintf(&path[pos], "%03d", counter); + snprintf(&path[pos], MAX_RENAMES_DIGITS+1, "%03d", counter); if (++counter >= MAX_RENAMES) counter = 1; } while ((rc = access(path, 0)) == 0 && counter != start); @@ -382,7 +385,7 @@ int robust_unlink(const char *fname) * across filesystems, -2 if copy_file() failed, and -1 on other errors. * If partialptr is not NULL and we need to do a copy, copy the file into * the active partial-dir instead of over the destination file. */ -int robust_rename(char *from, char *to, char *partialptr, +int robust_rename(const char *from, const char *to, const char *partialptr, int mode) { int tries = 4; @@ -459,7 +462,7 @@ void kill_all(int sig) } /** Turn a user name into a uid */ -int name_to_uid(char *name, uid_t *uid) +int name_to_uid(const char *name, uid_t *uid) { struct passwd *pass; if (!name || !*name) @@ -473,7 +476,7 @@ int name_to_uid(char *name, uid_t *uid) } /** Turn a group name into a gid */ -int name_to_gid(char *name, gid_t *gid) +int name_to_gid(const char *name, gid_t *gid) { struct group *grp; if (!name || !*name) @@ -517,8 +520,7 @@ 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; @@ -527,7 +529,7 @@ static void glob_expand_one(char *s, char ***argv_ptr, int *argc_ptr, 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; } @@ -547,6 +549,8 @@ static void glob_expand_one(char *s, char ***argv_ptr, int *argc_ptr, s = sanitize_path(NULL, s, "", 0, NULL); else s = strdup(s); + if (!s) + out_of_memory("glob_expand"); memset(&globbuf, 0, sizeof globbuf); if (!filter_server_path(s)) @@ -554,7 +558,7 @@ static void glob_expand_one(char *s, char ***argv_ptr, int *argc_ptr, 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_one"); + out_of_memory("glob_expand"); *argv_ptr = argv; *maxargs_ptr = maxargs; } @@ -565,7 +569,7 @@ static void glob_expand_one(char *s, char ***argv_ptr, int *argc_ptr, free(s); for (i = 0; i < (int)globbuf.gl_pathc; i++) { if (!(argv[argc++] = strdup(globbuf.gl_pathv[i]))) - out_of_memory("glob_expand_one"); + out_of_memory("glob_expand"); } } globfree(&globbuf); @@ -574,35 +578,34 @@ static void glob_expand_one(char *s, char ***argv_ptr, int *argc_ptr, } /* 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); } @@ -612,8 +615,8 @@ void glob_expand(char *base1, char ***argv_ptr, int *argc_ptr, int *maxargs_ptr) void strlower(char *s) { while (*s) { - if (isupper(*(unsigned char *)s)) - *s = tolower(*(unsigned char *)s); + if (isUpper(s)) + *s = toLower(s); s++; } } @@ -748,7 +751,7 @@ 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 @@ -777,7 +780,7 @@ char *sanitize_path(char *dest, const char *p, const char *rootdir, int depth, int plen = strlen(p); if (*p == '/') { if (!rootdir) - rootdir = lp_path(module_id); + rootdir = module_dir; rlen = strlen(rootdir); depth = 0; p++; @@ -861,89 +864,10 @@ char *sanitize_path(char *dest, const char *p, const char *rootdir, int depth, return dest; } -/* If sanitize_paths is not set, this works exactly the same as do_stat(). - * Otherwise, we verify that no symlink takes us outside the module path. - * If we encounter an escape attempt, we return a symlink's stat info! */ -int safe_stat(const char *fname, STRUCT_STAT *stp) -{ -#ifdef SUPPORT_LINKS - char tmpbuf[MAXPATHLEN], linkbuf[MAXPATHLEN], *mod_path; - int i, llen, mod_path_len; - - if (!sanitize_paths) - return do_stat(fname, stp); - - mod_path = lp_path(module_id); - mod_path_len = strlen(mod_path); - - for (i = 0; i < 16; i++) { -#ifdef DEBUG - if (*fname == '/') - assert(strncmp(fname, mod_path, mod_path_len) == 0 && fname[mod_path_len] == '/'); -#endif - if (do_lstat(fname, stp) < 0) - return -1; - if (!S_ISLNK(stp->st_mode)) - return 0; - if ((llen = readlink(fname, linkbuf, sizeof linkbuf - 1)) < 0) - return -1; - linkbuf[llen] = '\0'; - if (*fname == '/') - fname += mod_path_len; - if (!(fname = sanitize_path(tmpbuf, fname, mod_path, curr_dir_depth, linkbuf))) - break; - } - - return 0; /* Leave *stp set to the last symlink. */ -#else - return do_stat(fname, stp); -#endif -} - -void die_on_unsafe_path(char *path, int strip_filename) -{ -#ifdef SUPPORT_LINKS - char *final_slash, *p; - STRUCT_STAT st; - - if (!path) - return; - if (strip_filename) { - if (!(final_slash = strrchr(path, '/'))) - return; - *final_slash = '\0'; - } else - final_slash = NULL; - - p = path; - if (*p == '/') - p += module_dirlen + 1; - while (*p) { - if ((p = strchr(p, '/')) != NULL) - *p = '\0'; - if (safe_stat(path, &st) < 0) { - *p++ = '/'; - goto done; - } - if (S_ISLNK(st.st_mode)) { - rprintf(FERROR, "Unsafe path: %s\n", path); - exit_cleanup(RERR_SYNTAX); - } - if (!p) - break; - *p++ = '/'; - } - - done: - if (final_slash) - *final_slash = '/'; -#endif -} - /* Like chdir(), but it keeps track of the current directory (in the * global "curr_dir"), and ensures that the path size doesn't overflow. * Also cleans the path using the clean_fname() function. */ -int push_dir(char *dir) +int push_dir(const char *dir, int set_path_only) { static int initialised; unsigned int len; @@ -961,10 +885,12 @@ int push_dir(char *dir) 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 (chdir(dir)) + if (!set_path_only && chdir(dir)) return 0; if (*dir == '/') { @@ -990,7 +916,7 @@ int push_dir(char *dir) * Reverse a push_dir() call. You must pass in an absolute path * that was copied from a prior value of "curr_dir". **/ -int pop_dir(char *dir) +int pop_dir(const char *dir) { if (chdir(dir)) return 0; @@ -1090,8 +1016,6 @@ int handle_partial_dir(const char *fname, int create) if (create) { STRUCT_STAT st; int statret = do_lstat(dir, &st); - if (sanitize_paths && *partial_dir != '/') - die_on_unsafe_path(dir, 1); /* lstat handles last element */ if (statret == 0 && !S_ISDIR(st.st_mode)) { if (do_unlink(dir) < 0) return 0; @@ -1196,7 +1120,7 @@ char *human_num(int64 num) units = 'K'; } if (units) { - sprintf(bufs[n], "%.2f%c", dnum, units); + snprintf(bufs[n], sizeof bufs[0], "%.2f%c", dnum, units); return bufs[n]; } } @@ -1207,7 +1131,7 @@ char *human_num(int64 num) if (!num) *--s = '0'; while (num) { - *--s = (num % 10) + '0'; + *--s = (char)(num % 10) + '0'; num /= 10; } return s; @@ -1220,7 +1144,7 @@ char *human_dnum(double dnum, int decimal_digits) { char *buf = human_num(dnum); int len = strlen(buf); - if (isdigit(*(uchar*)(buf+len-1))) { + if (isDigit(buf + len - 1)) { /* There's extra room in buf prior to the start of the num. */ buf -= decimal_digits + 1; snprintf(buf, len + decimal_digits + 2, "%.*f", decimal_digits, dnum); @@ -1228,9 +1152,7 @@ char *human_dnum(double dnum, int decimal_digits) return buf; } -/** - * Return the date and time as a string - **/ +/* Return the date and time as a string. Some callers tweak returned buf. */ char *timestring(time_t t) { static char TimeBuf[200]; @@ -1334,18 +1256,17 @@ 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) { if (num >= MALLOC_MAX/size) return NULL; - /* No realloc should need this, but just in case... */ if (!ptr) return malloc(size * num); return realloc(ptr, size * num); @@ -1388,7 +1309,7 @@ const char *find_filename_suffix(const char *fn, int fn_len, int *len_ptr) if (strcmp(s+1, "orig") == 0) continue; } else if (s_len > 2 && had_tilde - && s[1] == '~' && isdigit(*(uchar*)(s+2))) + && s[1] == '~' && isDigit(s + 2)) continue; *len_ptr = s_len; suf = s; @@ -1396,7 +1317,7 @@ const char *find_filename_suffix(const char *fn, int fn_len, int *len_ptr) break; /* Determine if the suffix is all digits. */ for (s++, s_len--; s_len > 0; s++, s_len--) { - if (!isdigit(*(uchar*)s)) + if (!isDigit(s)) return suf; } /* An all-digit suffix may not be that signficant. */ @@ -1549,3 +1470,31 @@ int bitbag_next_bit(struct bitbag *bb, int after) return -1; } + +void *expand_item_list(item_list *lp, size_t item_size, + const char *desc, int incr) +{ + /* First time through, 0 <= 0, so list is expanded. */ + if (lp->malloced <= lp->count) { + void *new_ptr; + size_t new_size = lp->malloced; + if (incr < 0) + new_size += -incr; /* increase slowly */ + else if (new_size < (size_t)incr) + new_size += incr; + else + new_size *= 2; + new_ptr = realloc_array(lp->items, char, new_size * item_size); + if (verbose >= 4) { + rprintf(FINFO, "[%s] expand %s to %.0f bytes, did%s move\n", + who_am_i(), desc, (double)new_size * item_size, + new_ptr == lp->items ? " not" : ""); + } + if (!new_ptr) + out_of_memory("expand_item_list"); + + lp->items = new_ptr; + lp->malloced = new_size; + } + return (char*)lp->items + (lp->count++ * item_size); +}