X-Git-Url: https://mattmccutchen.net/rsync/rsync.git/blobdiff_plain/4d30f17671bdcc0f17facb133ea283eea6f0dc12..f1ca7c4429f2a8e9de72f91d95218bb324df6a9e:/util.c diff --git a/util.c b/util.c index ad29be3d..c4375175 100644 --- a/util.c +++ b/util.c @@ -275,38 +275,54 @@ 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) { + int save_errno = errno; rsyserr(FERROR_XFER, errno, "open %s", full_fname(source)); + errno = save_errno; return -1; } if (ofd < 0) { if (robust_unlink(dest) && errno != ENOENT) { + int save_errno = errno; rsyserr(FERROR_XFER, errno, "unlink %s", full_fname(dest)); + errno = save_errno; 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; + if ((ofd = do_open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, mode)) < 0) { + int save_errno = errno ? errno : EINVAL; /* 0 paranoia */ + if (create_bak_dir && errno == ENOENT && make_bak_dir(dest) == 0) { + if ((ofd = do_open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, mode)) < 0) + save_errno = errno ? errno : save_errno; + else + save_errno = 0; + } + if (save_errno) { + rsyserr(FERROR_XFER, save_errno, "open %s", full_fname(dest)); + close(ifd); + errno = save_errno; + return -1; + } } } while ((len = safe_read(ifd, buf, sizeof buf)) > 0) { if (full_write(ofd, buf, len) < 0) { + int save_errno = errno; rsyserr(FERROR_XFER, errno, "write %s", full_fname(dest)); close(ifd); close(ofd); + errno = save_errno; return -1; } } if (len < 0) { + int save_errno = errno; rsyserr(FERROR_XFER, errno, "read %s", full_fname(source)); close(ifd); close(ofd); + errno = save_errno; return -1; } @@ -316,8 +332,10 @@ int copy_file(const char *source, const char *dest, int ofd, } if (close(ofd) < 0) { + int save_errno = errno; rsyserr(FERROR_XFER, errno, "close failed on %s", full_fname(dest)); + errno = save_errno; return -1; } @@ -401,8 +419,11 @@ int robust_rename(const char *from, const char *to, const char *partialptr, switch (errno) { #ifdef ETXTBSY case ETXTBSY: - if (robust_unlink(to) != 0) + if (robust_unlink(to) != 0) { + errno = ETXTBSY; return -1; + } + errno = ETXTBSY; break; #endif case EXDEV: @@ -504,14 +525,16 @@ int lock_range(int fd, int offset, int len) } #define ENSURE_MEMSPACE(buf, type, sz, req) \ - if ((req) >= sz && !(buf = realloc_array(buf, type, sz *= 2))) \ - out_of_memory("ENSURE_MEMSPACE") + if ((req) > sz && !(buf = realloc_array(buf, type, sz = MAX(sz * 2, req)))) \ + out_of_memory("glob_expand") 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 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) { @@ -520,15 +543,14 @@ static void glob_match(char *arg, int abpos, int fbpos) 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); + ENSURE_MEMSPACE(glob.filt_buf, char, glob.fbsize, glob.absize); + memcpy(glob.filt_buf, glob.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'; + ENSURE_MEMSPACE(glob.arg_buf, char, glob.absize, abpos + 3); + glob.arg_buf[abpos++] = *arg++; + glob.arg_buf[abpos++] = *arg++; + glob.arg_buf[abpos] = '\0'; } if ((slash = strchr(arg, '/')) != NULL) { *slash = '\0'; @@ -539,7 +561,7 @@ static void glob_match(char *arg, int abpos, int fbpos) struct dirent *di; DIR *d; - if (!(d = opendir(abpos ? arg_buf : "."))) + if (!(d = opendir(abpos ? glob.arg_buf : "."))) return; while ((di = readdir(d)) != NULL) { char *dname = d_name(di); @@ -567,63 +589,65 @@ static inline void call_glob_match(const char *name, int len, int from_glob, { char *use_buf; - ENSURE_MEMSPACE(arg_buf, char, absize, abpos + len + 2); - memcpy(arg_buf + abpos, name, len); + ENSURE_MEMSPACE(glob.arg_buf, char, glob.absize, abpos + len + 2); + memcpy(glob.arg_buf + abpos, name, len); abpos += len; - arg_buf[abpos] = '\0'; + glob.arg_buf[abpos] = '\0'; if (fbpos >= 0) { - ENSURE_MEMSPACE(filt_buf, char, fbsize, fbpos + len + 2); - memcpy(filt_buf + fbpos, name, len); + ENSURE_MEMSPACE(glob.filt_buf, char, glob.fbsize, fbpos + len + 2); + memcpy(glob.filt_buf + fbpos, name, len); fbpos += len; - filt_buf[fbpos] = '\0'; - use_buf = filt_buf; + glob.filt_buf[fbpos] = '\0'; + use_buf = glob.filt_buf; } else - use_buf = arg_buf; + use_buf = glob.arg_buf; - if (from_glob || arg) { + if (from_glob || (arg && len)) { 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 (do_stat(glob.arg_buf, &st) != 0) + return; + 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; - } + && check_filter(&daemon_filter_list, FLOG, use_buf, is_dir) < 0) + return; } if (arg) { - arg_buf[abpos++] = '/'; - arg_buf[abpos] = '\0'; + glob.arg_buf[abpos++] = '/'; + glob.arg_buf[abpos] = '\0'; if (fbpos >= 0) { - filt_buf[fbpos++] = '/'; - filt_buf[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(arg_buf))) + ENSURE_MEMSPACE(glob.argv, char *, glob.maxargs, glob.argc + 1); + if (!(glob.argv[glob.argc++] = strdup(glob.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) + * 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; - int save_argc = *argc_p; + + if (!arg) { + if (glob.filt_buf) + free(glob.filt_buf); + free(glob.arg_buf); + memset(&glob, 0, sizeof glob); + return -1; + } if (sanitize_paths) s = sanitize_path(NULL, arg, "", 0, SP_KEEP_DOT_DIRS); @@ -636,36 +660,32 @@ void glob_expand(const char *arg, char ***argv_p, int *argc_p, int *maxargs_p) | CFN_COLLAPSE_DOT_DOT_DIRS); } - if (!(arg_buf = new_array(char, absize = MAXPATHLEN))) - out_of_memory("glob_expand"); - *arg_buf = '\0'; - filt_buf = NULL; - fbsize = 0; + ENSURE_MEMSPACE(glob.arg_buf, char, glob.absize, MAXPATHLEN); + *glob.arg_buf = '\0'; - glob_argc = *argc_p; - glob_argv = *argv_p; - glob_maxargs = *maxargs_p; + glob.argc = save_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"); + ENSURE_MEMSPACE(glob.argv, char *, glob.maxargs, 100); 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 + 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); + ret = 1; + } - *maxargs_p = glob_maxargs; - *argv_p = glob_argv; - *argc_p = glob_argc; + *maxargs_p = glob.maxargs; + *argv_p = glob.argv; + *argc_p = glob.argc; - if (filt_buf) - free(filt_buf); - free(arg_buf); + return ret; } /* This routine is only used in daemon mode. */ @@ -944,7 +964,7 @@ char *sanitize_path(char *dest, const char *p, const char *rootdir, int depth, /* 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(const char *dir, int set_path_only) +int change_dir(const char *dir, int set_path_only) { static int initialised; unsigned int len; @@ -962,21 +982,26 @@ 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) { - errno = ENAMETOOLONG; - return 0; - } - - if (!set_path_only && chdir(dir)) - return 0; - if (*dir == '/') { + if (len >= sizeof curr_dir) { + errno = ENAMETOOLONG; + return 0; + } + if (!set_path_only && chdir(dir)) + return 0; memcpy(curr_dir, dir, len + 1); - curr_dir_len = len; } else { - curr_dir[curr_dir_len++] = '/'; - memcpy(curr_dir + curr_dir_len, dir, len + 1); - curr_dir_len += len; + if (curr_dir_len + 1 + len >= sizeof curr_dir) { + errno = ENAMETOOLONG; + return 0; + } + curr_dir[curr_dir_len] = '/'; + memcpy(curr_dir + curr_dir_len + 1, dir, len + 1); + + if (!set_path_only && chdir(curr_dir)) { + curr_dir[curr_dir_len] = '\0'; + return 0; + } } curr_dir_len = clean_fname(curr_dir, CFN_COLLAPSE_DOT_DOT_DIRS); @@ -987,28 +1012,7 @@ int push_dir(const char *dir, int set_path_only) } if (verbose >= 5 && !set_path_only) - rprintf(FINFO, "[%s] push_dir(%s)\n", who_am_i(), curr_dir); - - return 1; -} - -/** - * 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(const char *dir) -{ - if (chdir(dir)) - return 0; - - curr_dir_len = strlcpy(curr_dir, dir, sizeof curr_dir); - if (curr_dir_len >= sizeof curr_dir) - curr_dir_len = sizeof curr_dir - 1; - 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); + rprintf(FINFO, "[%s] change_dir(%s)\n", who_am_i(), curr_dir); return 1; } @@ -1071,10 +1075,10 @@ char *partial_dir_fname(const char *fname) if (daemon_filter_list.head) { t = strrchr(partial_fname, '/'); *t = '\0'; - if (check_filter(&daemon_filter_list, partial_fname, 1) < 0) + if (check_filter(&daemon_filter_list, FLOG, partial_fname, 1) < 0) return NULL; *t = '/'; - if (check_filter(&daemon_filter_list, partial_fname, 0) < 0) + if (check_filter(&daemon_filter_list, FLOG, partial_fname, 0) < 0) return NULL; } @@ -1346,7 +1350,7 @@ void *_new_array(unsigned long num, unsigned int size, int use_calloc) return use_calloc ? calloc(num, size) : malloc(num * size); } -void *_realloc_array(void *ptr, unsigned int size, unsigned long num) +void *_realloc_array(void *ptr, unsigned int size, size_t num) { if (num >= MALLOC_MAX/size) return NULL; @@ -1567,7 +1571,10 @@ void *expand_item_list(item_list *lp, size_t item_size, new_size += incr; else new_size *= 2; - new_ptr = realloc_array(lp->items, char, new_size * item_size); + if (new_size < lp->malloced) + overflow_exit("expand_item_list"); + /* Using _realloc_array() lets us pass the size, not a type. */ + new_ptr = _realloc_array(lp->items, item_size, new_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,