--- embedaddon/rsync/util.c 2012/02/17 15:09:30 1.1.1.1 +++ embedaddon/rsync/util.c 2021/03/17 00:32:36 1.1.1.4 @@ -4,7 +4,7 @@ * Copyright (C) 1996-2000 Andrew Tridgell * Copyright (C) 1996 Paul Mackerras * Copyright (C) 2001, 2002 Martin Pool - * Copyright (C) 2003-2009 Wayne Davison + * Copyright (C) 2003-2020 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 @@ -22,19 +22,23 @@ #include "rsync.h" #include "ifuncs.h" +#include "itypes.h" +#include "inums.h" -extern int verbose; +extern int dry_run; extern int module_id; +extern int do_fsync; +extern int protect_args; extern int modify_window; extern int relative_paths; extern int preserve_times; -extern int human_readable; extern int preserve_xattrs; +extern int preallocate_files; +extern int force_change; extern char *module_dir; extern unsigned int module_dirlen; -extern mode_t orig_umask; extern char *partial_dir; -extern struct filter_list_struct daemon_filter_list; +extern filter_rule_list daemon_filter_list; int sanitize_paths = 0; @@ -94,6 +98,7 @@ int fd_pair(int fd[2]) void print_child_argv(const char *prefix, char **cmd) { + int cnt = 0; rprintf(FCLIENT, "%s ", prefix); for (; *cmd; cmd++) { /* Look for characters that ought to be quoted. This @@ -107,72 +112,107 @@ void print_child_argv(const char *prefix, char **cmd) } else { rprintf(FCLIENT, "%s ", *cmd); } + cnt++; } - rprintf(FCLIENT, "\n"); + rprintf(FCLIENT, " (%d args)\n", cnt); } -NORETURN void out_of_memory(const char *str) +#ifdef SUPPORT_FORCE_CHANGE +static int try_a_force_change(const char *fname, STRUCT_STAT *stp) { - rprintf(FERROR, "ERROR: out of memory in %s [%s]\n", str, who_am_i()); - exit_cleanup(RERR_MALLOC); -} + uint32 fileflags = ST_FLAGS(*stp); + if (fileflags == NO_FFLAGS) { + STRUCT_STAT st; + if (x_lstat(fname, &st, NULL) == 0) + fileflags = st.st_flags; + } + if (fileflags != NO_FFLAGS && make_mutable(fname, stp->st_mode, fileflags, force_change) > 0) { + int ret, save_force_change = force_change; -NORETURN void overflow_exit(const char *str) -{ - rprintf(FERROR, "ERROR: buffer overflow in %s [%s]\n", str, who_am_i()); - exit_cleanup(RERR_MALLOC); + force_change = 0; /* Make certain we can't come back here. */ + ret = set_times(fname, stp); + force_change = save_force_change; + + undo_make_mutable(fname, fileflags); + + return ret; + } + + errno = EPERM; + + return -1; } +#endif /* This returns 0 for success, 1 for a symlink if symlink time-setting * is not possible, or -1 for any other error. */ -int set_modtime(const char *fname, time_t modtime, mode_t mode) +int set_times(const char *fname, STRUCT_STAT *stp) { static int switch_step = 0; - if (verbose > 2) { - rprintf(FINFO, "set modtime of %s to (%ld) %s", - fname, (long)modtime, - asctime(localtime(&modtime))); + if (DEBUG_GTE(TIME, 1)) { + rprintf(FINFO, + "set modtime, atime of %s to (%ld) %s, (%ld) %s\n", + fname, (long)stp->st_mtime, + timestring(stp->st_mtime), (long)stp->st_atime, timestring(stp->st_atime)); } switch (switch_step) { +#ifdef HAVE_SETATTRLIST +#include "case_N.h" + if (do_setattrlist_times(fname, stp) == 0) + break; + if (errno != ENOSYS) + return -1; + switch_step++; +#endif + #ifdef HAVE_UTIMENSAT #include "case_N.h" - if (do_utimensat(fname, modtime, 0) == 0) + if (do_utimensat(fname, stp) == 0) break; +#ifdef SUPPORT_FORCE_CHANGE + if (force_change && errno == EPERM && try_a_force_change(fname, stp) == 0) + break; +#endif if (errno != ENOSYS) return -1; switch_step++; - /* FALLTHROUGH */ #endif #ifdef HAVE_LUTIMES #include "case_N.h" - if (do_lutimes(fname, modtime, 0) == 0) + if (do_lutimes(fname, stp) == 0) break; +#ifdef SUPPORT_FORCE_CHANGE + if (force_change && errno == EPERM && try_a_force_change(fname, stp) == 0) + break; +#endif if (errno != ENOSYS) return -1; switch_step++; - /* FALLTHROUGH */ #endif #include "case_N.h" switch_step++; if (preserve_times & PRESERVE_LINK_TIMES) { preserve_times &= ~PRESERVE_LINK_TIMES; - if (S_ISLNK(mode)) + if (S_ISLNK(stp->st_mode)) return 1; } - /* FALLTHROUGH */ #include "case_N.h" #ifdef HAVE_UTIMES - if (do_utimes(fname, modtime, 0) == 0) + if (do_utimes(fname, stp) == 0) break; #else - if (do_utime(fname, modtime, 0) == 0) + if (do_utime(fname, stp) == 0) break; #endif +#ifdef SUPPORT_FORCE_CHANGE + if (force_change && errno == EPERM && try_a_force_change(fname, stp) == 0) + break; +#endif return -1; } @@ -180,42 +220,88 @@ int set_modtime(const char *fname, time_t modtime, mod return 0; } -/* This creates a new directory with default permissions. Since there - * might be some directory-default permissions affecting this, we can't - * force the permissions directly using the original umask and mkdir(). */ -int mkdir_defmode(char *fname) -{ - int ret; - - umask(orig_umask); - ret = do_mkdir(fname, ACCESSPERMS); - umask(0); - - return ret; -} - /* Create any necessary directories in fname. Any missing directories are - * created with default permissions. */ -int create_directory_path(char *fname) + * created with default permissions. Returns < 0 on error, or the number + * of directories created. */ +int make_path(char *fname, mode_t mode, int flags) { - char *p; + char *end, *p; int ret = 0; - while (*fname == '/') - fname++; - while (strncmp(fname, "./", 2) == 0) + if (flags & MKP_SKIP_SLASH) { + while (*fname == '/') + fname++; + } + + while (*fname == '.' && fname[1] == '/') fname += 2; - umask(orig_umask); - p = fname; - while ((p = strchr(p,'/')) != NULL) { - *p = '\0'; - if (do_mkdir(fname, ACCESSPERMS) < 0 && errno != EEXIST) - ret = -1; - *p++ = '/'; + if (flags & MKP_DROP_NAME) { + end = strrchr(fname, '/'); + if (!end || end == fname) + return 0; + *end = '\0'; + } else + end = fname + strlen(fname); + + /* Try to find an existing dir, starting from the deepest dir. */ + for (p = end; ; ) { + if (dry_run) { + STRUCT_STAT st; + if (do_stat(fname, &st) == 0) { + if (S_ISDIR(st.st_mode)) + errno = EEXIST; + else + errno = ENOTDIR; + } + } else if (do_mkdir(fname, mode) == 0) { + ret++; + break; + } + + if (errno != ENOENT) { + STRUCT_STAT st; + if (errno != EEXIST || (do_stat(fname, &st) == 0 && !S_ISDIR(st.st_mode))) + ret = -ret - 1; + break; + } + while (1) { + if (p == fname) { + /* We got a relative path that doesn't exist, so assume that '.' + * is there and just break out and create the whole thing. */ + p = NULL; + goto double_break; + } + if (*--p == '/') { + if (p == fname) { + /* We reached the "/" dir, which we assume is there. */ + goto double_break; + } + *p = '\0'; + break; + } + } } - umask(0); + double_break: + /* Make all the dirs that we didn't find on the way here. */ + while (p != end) { + if (p) + *p = '/'; + else + p = fname; + p += strlen(p); + if (ret < 0) /* Skip mkdir on error, but keep restoring the path. */ + continue; + if (do_mkdir(fname, mode) < 0) + ret = -ret - 1; + else + ret++; + } + + if (flags & MKP_DROP_NAME) + *end = '/'; + return ret; } @@ -280,12 +366,12 @@ static int safe_read(int desc, char *ptr, size_t len) * * This is used in conjunction with the --temp-dir, --backup, and * --copy-dest options. */ -int copy_file(const char *source, const char *dest, int ofd, - mode_t mode, int create_bak_dir) +int copy_file(const char *source, const char *dest, int ofd, mode_t mode) { int ifd; char buf[1024 * 8]; int len; /* Number of bytes read into `buf'. */ + OFF_T prealloc_len = 0, offset = 0; if ((ifd = do_open(source, O_RDONLY, 0)) < 0) { int save_errno = errno; @@ -298,27 +384,41 @@ int copy_file(const char *source, const char *dest, in if (robust_unlink(dest) && errno != ENOENT) { int save_errno = errno; rsyserr(FERROR_XFER, errno, "unlink %s", full_fname(dest)); + close(ifd); errno = save_errno; return -1; } +#ifdef SUPPORT_XATTRS + if (preserve_xattrs) + mode |= S_IWUSR; +#endif + mode &= INITACCESSPERMS; 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; - } + int save_errno = errno; + rsyserr(FERROR_XFER, save_errno, "open %s", full_fname(dest)); + close(ifd); + errno = save_errno; + return -1; } } +#ifdef SUPPORT_PREALLOCATION + if (preallocate_files) { + STRUCT_STAT srcst; + + /* Try to preallocate enough space for file's eventual length. Can + * reduce fragmentation on filesystems like ext4, xfs, and NTFS. */ + if (do_fstat(ifd, &srcst) < 0) + rsyserr(FWARNING, errno, "fstat %s", full_fname(source)); + else if (srcst.st_size > 0) { + prealloc_len = do_fallocate(ofd, 0, srcst.st_size); + if (prealloc_len < 0) + rsyserr(FWARNING, errno, "do_fallocate %s", full_fname(dest)); + } + } +#endif + while ((len = safe_read(ifd, buf, sizeof buf)) > 0) { if (full_write(ofd, buf, len) < 0) { int save_errno = errno; @@ -328,6 +428,7 @@ int copy_file(const char *source, const char *dest, in errno = save_errno; return -1; } + offset += len; } if (len < 0) { @@ -344,6 +445,21 @@ int copy_file(const char *source, const char *dest, in full_fname(source)); } + /* Source file might have shrunk since we fstatted it. + * Cut off any extra preallocated zeros from dest file. */ + if (offset < prealloc_len && do_ftruncate(ofd, offset) < 0) { + /* If we fail to truncate, the dest file may be wrong, so we + * must trigger the "partial transfer" error. */ + rsyserr(FERROR_XFER, errno, "ftruncate %s", full_fname(dest)); + } + + if (do_fsync && fsync(ofd) < 0) { + rsyserr(FERROR, errno, "fsync failed on %s", + full_fname(dest)); + close(ofd); + return -1; + } + if (close(ofd) < 0) { int save_errno = errno; rsyserr(FERROR_XFER, errno, "close failed on %s", @@ -407,7 +523,7 @@ int robust_unlink(const char *fname) counter = 1; } while ((rc = access(path, 0)) == 0 && counter != start); - if (verbose > 0) { + if (INFO_GTE(MISC, 1)) { rprintf(FWARNING, "renaming %s to %s because of text busy\n", fname, path); } @@ -430,6 +546,11 @@ int robust_rename(const char *from, const char *to, co { int tries = 4; + /* A resumed in-place partial-dir transfer might call us with from and + * to pointing to the same buf if the transfer failed yet again. */ + if (from == to) + return 0; + while (tries--) { if (do_rename(from, to) == 0) return 0; @@ -450,7 +571,7 @@ int robust_rename(const char *from, const char *to, co return -2; to = partialptr; } - if (copy_file(from, to, -1, mode, 0) != 0) + if (copy_file(from, to, -1, mode) != 0) return -2; do_unlink(from); return 1; @@ -504,30 +625,6 @@ void kill_all(int sig) } } -/** Turn a user name into a uid */ -int name_to_uid(const char *name, uid_t *uid_p) -{ - struct passwd *pass; - if (!name || !*name) - 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_p) -{ - struct group *grp; - if (!name || !*name) - return 0; - if (!(grp = getgrnam(name))) - return 0; - *gid_p = grp->gr_gid; - return 1; -} - /** Lock a byte range in a open file */ int lock_range(int fd, int offset, int len) { @@ -543,8 +640,7 @@ 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 = MAX(sz * 2, req)))) \ - out_of_memory("glob_expand") + do { if ((req) > sz) buf = realloc_array(buf, type, sz = MAX(sz * 2, req)); } while(0) static inline void call_glob_match(const char *name, int len, int from_glob, char *arg, int abpos, int fbpos); @@ -646,8 +742,7 @@ static inline void call_glob_match(const char *name, i 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"); + glob.argv[glob.argc++] = strdup(glob.arg_buf); } } @@ -671,11 +766,7 @@ int glob_expand(const char *arg, char ***argv_p, int * s = sanitize_path(NULL, arg, "", 0, SP_KEEP_DOT_DIRS); else { 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); + clean_fname(s, CFN_KEEP_DOT_DIRS | CFN_KEEP_TRAILING_SLASH | CFN_COLLAPSE_DOT_DOT_DIRS); } ENSURE_MEMSPACE(glob.arg_buf, char, glob.absize, MAXPATHLEN); @@ -719,10 +810,14 @@ void glob_expand_module(char *base1, char *arg, char * if (strncmp(arg, base, base_len) == 0) arg += base_len; - if (!(arg = strdup(arg))) - out_of_memory("glob_expand_module"); + if (protect_args) { + glob_expand(arg, argv_p, argc_p, maxargs_p); + return; + } - if (asprintf(&base," %s/", base1) <= 0) + arg = strdup(arg); + + if (asprintf(&base," %s/", base1) < 0) out_of_memory("glob_expand_module"); base_len++; @@ -750,6 +845,41 @@ void strlower(char *s) } } +/** + * Split a string into tokens based (usually) on whitespace & commas. If the + * string starts with a comma (after skipping any leading whitespace), then + * splitting is done only on commas. No empty tokens are ever returned. */ +char *conf_strtok(char *str) +{ + static int commas_only = 0; + + if (str) { + while (isSpace(str)) str++; + if (*str == ',') { + commas_only = 1; + str++; + } else + commas_only = 0; + } + + while (commas_only) { + char *end, *tok = strtok(str, ","); + if (!tok) + return NULL; + /* Trim just leading and trailing whitespace. */ + while (isSpace(tok)) + tok++; + end = tok + strlen(tok); + while (end > tok && isSpace(end-1)) + *--end = '\0'; + if (*tok) + return tok; + str = NULL; + } + + return strtok(str, " ,\t\r\n"); +} + /* Join strings p1 & p2 into "dest" with a guaranteed '/' between them. (If * p1 ends with a '/', no extra '/' is inserted.) Returns the length of both * strings + 1 (if '/' was inserted), regardless of whether the null-terminated @@ -802,6 +932,25 @@ size_t stringjoin(char *dest, size_t destsize, ...) return ret; } +/* Append formatted text at *dest_ptr up to a maximum of sz (like snprintf). + * On success, advance *dest_ptr and return True; on overflow, return False. */ +BOOL snappendf(char **dest_ptr, size_t sz, const char *format, ...) +{ + va_list ap; + size_t len; + + va_start(ap, format); + len = vsnprintf(*dest_ptr, sz, format, ap); + va_end(ap); + + if (len >= sz) + return False; + else { + *dest_ptr += len; + return True; + } +} + int count_dir_elements(const char *p) { int cnt = 0, new_component = 1; @@ -824,7 +973,7 @@ int count_dir_elements(const char *p) * 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) +int clean_fname(char *name, int flags) { char *limit = name - 1, *t = name, *f = name; int anchored; @@ -832,6 +981,8 @@ unsigned int clean_fname(char *name, int flags) if (!name) return 0; +#define DOT_IS_DOT_DOT_DIR(bp) (bp[1] == '.' && (bp[2] == '/' || !bp[2])) + if ((anchored = *f == '/') != 0) { *t++ = *f++; #ifdef __CYGWIN__ @@ -844,7 +995,8 @@ unsigned int clean_fname(char *name, int flags) } else if (flags & CFN_KEEP_DOT_DIRS && *f == '.' && f[1] == '/') { *t++ = *f++; *t++ = *f++; - } + } else if (flags & CFN_REFUSE_DOT_DOT_DIRS && *f == '.' && DOT_IS_DOT_DOT_DIR(f)) + return -1; while (*f) { /* discard extra slashes */ if (*f == '/') { @@ -860,9 +1012,10 @@ unsigned int clean_fname(char *name, int flags) if (f[1] == '\0' && flags & CFN_DROP_TRAILING_DOT_DIR) break; /* collapse ".." dirs */ - if (flags & CFN_COLLAPSE_DOT_DOT_DIRS - && f[1] == '.' && (f[2] == '/' || !f[2])) { + if (flags & (CFN_COLLAPSE_DOT_DOT_DIRS|CFN_REFUSE_DOT_DOT_DIRS) && DOT_IS_DOT_DOT_DIR(f)) { char *s = t - 1; + if (flags & CFN_REFUSE_DOT_DOT_DIRS) + return -1; if (s == name && anchored) { f += 2; continue; @@ -885,6 +1038,8 @@ unsigned int clean_fname(char *name, int flags) *t++ = '.'; *t = '\0'; +#undef DOT_IS_DOT_DOT_DIR + return t - name; } @@ -910,14 +1065,13 @@ unsigned int clean_fname(char *name, int flags) * ALWAYS collapses ".." elements (except for those at the start of the * 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, - int flags) +char *sanitize_path(char *dest, const char *p, const char *rootdir, int depth, int flags) { char *start, *sanp; int rlen = 0, drop_dot_dirs = !relative_paths || !(flags & SP_KEEP_DOT_DIRS); if (dest != p) { - int plen = strlen(p); + int plen = strlen(p); /* the path len INCLUDING any separating slash */ if (*p == '/') { if (!rootdir) rootdir = module_dir; @@ -925,14 +1079,13 @@ char *sanitize_path(char *dest, const char *p, const c depth = 0; p++; } - if (dest) { - if (rlen + plen + 1 >= MAXPATHLEN) - return NULL; - } else if (!(dest = new_array(char, rlen + plen + 1))) - out_of_memory("sanitize_path"); - if (rlen) { + if (!dest) + dest = new_array(char, MAX(rlen + plen + 1, 2)); + else if (rlen + plen + 1 >= MAXPATHLEN) + return NULL; + if (rlen) { /* only true if p previously started with a slash */ memcpy(dest, rootdir, rlen); - if (rlen > 1) + if (rlen > 1) /* a rootdir of len 1 is "/", so this avoids a 2nd slash */ dest[rlen++] = '/'; } } @@ -992,7 +1145,7 @@ char *sanitize_path(char *dest, const char *p, const c * Also cleans the path using the clean_fname() function. */ int change_dir(const char *dir, int set_path_only) { - static int initialised; + static int initialised, skipped_chdir; unsigned int len; if (!initialised) { @@ -1008,7 +1161,7 @@ int change_dir(const char *dir, int set_path_only) return 0; len = strlen(dir); - if (len == 1 && *dir == '.') + if (len == 1 && *dir == '.' && (!skipped_chdir || set_path_only)) return 1; if (*dir == '/') { @@ -1018,8 +1171,10 @@ int change_dir(const char *dir, int set_path_only) } if (!set_path_only && chdir(dir)) return 0; + skipped_chdir = set_path_only; memcpy(curr_dir, dir, len + 1); } else { + unsigned int save_dir_len = curr_dir_len; if (curr_dir_len + 1 + len >= sizeof curr_dir) { errno = ENAMETOOLONG; return 0; @@ -1029,19 +1184,21 @@ int change_dir(const char *dir, int set_path_only) memcpy(curr_dir + curr_dir_len, dir, len + 1); if (!set_path_only && chdir(curr_dir)) { + curr_dir_len = save_dir_len; curr_dir[curr_dir_len] = '\0'; return 0; } + skipped_chdir = set_path_only; } - curr_dir_len = clean_fname(curr_dir, CFN_COLLAPSE_DOT_DOT_DIRS); + curr_dir_len = clean_fname(curr_dir, CFN_COLLAPSE_DOT_DOT_DIRS | CFN_DROP_TRAILING_DOT_DIR); 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) + if (DEBUG_GTE(CHDIR, 1) && !set_path_only) rprintf(FINFO, "[%s] change_dir(%s)\n", who_am_i(), curr_dir); return 1; @@ -1059,13 +1216,10 @@ char *normalize_path(char *path, BOOL force_newbuf, un return NULL; curr_dir[curr_dir_len] = '/'; memcpy(curr_dir + curr_dir_len + 1, path, len + 1); - if (!(path = strdup(curr_dir))) - out_of_memory("normalize_path"); + path = strdup(curr_dir); curr_dir[curr_dir_len] = '\0'; - } else if (force_newbuf) { - if (!(path = strdup(path))) - out_of_memory("normalize_path"); - } + } else if (force_newbuf) + path = strdup(path); len = clean_fname(path, CFN_COLLAPSE_DOT_DOT_DIRS | CFN_DROP_TRAILING_DOT_DIR); @@ -1075,6 +1229,32 @@ char *normalize_path(char *path, BOOL force_newbuf, un return path; } +/* We need to supply our own strcmp function for file list comparisons + * to ensure that signed/unsigned usage is consistent between machines. */ +int u_strcmp(const char *p1, const char *p2) +{ + for ( ; *p1; p1++, p2++) { + if (*p1 != *p2) + break; + } + + return (int)*(uchar*)p1 - (int)*(uchar*)p2; +} + +/* We need a memcmp function compares unsigned-byte values. */ +int u_memcmp(const void *p1, const void *p2, size_t len) +{ + const uchar *u1 = p1; + const uchar *u2 = p2; + + while (len--) { + if (*u1 != *u2) + return (int)*u1 - (int)*u2; + } + + return 0; +} + /** * Return a quoted string with the full pathname of the indicated filename. * The string " (in MODNAME)" may also be appended. The returned pointer @@ -1104,7 +1284,7 @@ char *full_fname(const char *fn) } else m1 = m2 = m3 = ""; - if (asprintf(&result, "\"%s%s%s\"%s%s%s", p1, p2, fn, m1, m2, m3) <= 0) + if (asprintf(&result, "\"%s%s%s\"%s%s%s", p1, p2, fn, m1, m2, m3) < 0) out_of_memory("full_fname"); return result; @@ -1168,7 +1348,7 @@ int handle_partial_dir(const char *fname, int create) } statret = -1; } - if (statret < 0 && do_mkdir(dir, 0700) < 0) { + if (statret < 0 && make_path(dir, 0700, 0) < 0) { *fn = '/'; return 0; } @@ -1238,156 +1418,36 @@ int unsafe_symlink(const char *dest, const char *src) return depth < 0; } -#define HUMANIFY(mult) \ - do { \ - if (num >= mult || num <= -mult) { \ - double dnum = (double)num / mult; \ - char units; \ - if (num < 0) \ - dnum = -dnum; \ - if (dnum < mult) \ - units = 'K'; \ - else if ((dnum /= mult) < mult) \ - units = 'M'; \ - else { \ - dnum /= mult; \ - units = 'G'; \ - } \ - if (num < 0) \ - dnum = -dnum; \ - snprintf(bufs[n], sizeof bufs[0], "%.2f%c", dnum, units); \ - return bufs[n]; \ - } \ - } while (0) - -/* Return the int64 number as a string. If the --human-readable option was - * specified, we may output the number in K, M, or G units. We can return - * up to 4 buffers at a time. */ -char *human_num(int64 num) -{ - static char bufs[4][128]; /* more than enough room */ - static unsigned int n; - char *s; - int negated; - - n = (n + 1) % (sizeof bufs / sizeof bufs[0]); - - if (human_readable) { - if (human_readable == 1) - HUMANIFY(1000); - else - HUMANIFY(1024); - } - - s = bufs[n] + sizeof bufs[0] - 1; - *s = '\0'; - - if (!num) - *--s = '0'; - if (num < 0) { - /* A maximum-size negated number can't fit as a positive, - * so do one digit in negated form to start us off. */ - *--s = (char)(-(num % 10)) + '0'; - num = -(num / 10); - negated = 1; - } else - negated = 0; - - while (num) { - *--s = (char)(num % 10) + '0'; - num /= 10; - } - - if (negated) - *--s = '-'; - - return s; -} - -/* Return the double number as a string. If the --human-readable option was - * specified, we may output the number in K, M, or G units. We use a buffer - * from human_num() to return our result. */ -char *human_dnum(double dnum, int decimal_digits) -{ - char *buf = human_num(dnum); - int len = strlen(buf); - if (isDigit(buf + len - 1)) { - /* There's extra room in buf prior to the start of the num. */ - buf -= decimal_digits + 2; - snprintf(buf, len + decimal_digits + 3, "%.*f", decimal_digits, dnum); - } - return buf; -} - /* Return the date and time as a string. Some callers tweak returned buf. */ char *timestring(time_t t) { - static char TimeBuf[200]; + static int ndx = 0; + static char buffers[4][20]; /* We support 4 simultaneous timestring results. */ + char *TimeBuf = buffers[ndx = (ndx + 1) % 4]; struct tm *tm = localtime(&t); - char *p; + int len = snprintf(TimeBuf, sizeof buffers[0], "%4d/%02d/%02d %02d:%02d:%02d", + (int)tm->tm_year + 1900, (int)tm->tm_mon + 1, (int)tm->tm_mday, + (int)tm->tm_hour, (int)tm->tm_min, (int)tm->tm_sec); + assert(len > 0); /* Silence gcc warning */ -#ifdef HAVE_STRFTIME - strftime(TimeBuf, sizeof TimeBuf - 1, "%Y/%m/%d %H:%M:%S", tm); -#else - strlcpy(TimeBuf, asctime(tm), sizeof TimeBuf); -#endif - - if ((p = strchr(TimeBuf, '\n')) != NULL) - *p = '\0'; - return TimeBuf; } -/** - * Sleep for a specified number of milliseconds. - * - * Always returns TRUE. (In the future it might return FALSE if - * interrupted.) - **/ -int msleep(int t) -{ - int tdiff = 0; - struct timeval tval, t1, t2; - - gettimeofday(&t1, NULL); - - while (tdiff < t) { - tval.tv_sec = (t-tdiff)/1000; - tval.tv_usec = 1000*((t-tdiff)%1000); - - errno = 0; - select(0,NULL,NULL, NULL, &tval); - - gettimeofday(&t2, NULL); - tdiff = (t2.tv_sec - t1.tv_sec)*1000 + - (t2.tv_usec - t1.tv_usec)/1000; - } - - return True; -} - /* Determine if two time_t values are equivalent (either exact, or in * the modification timestamp window established by --modify-window). - * - * @retval 0 if the times should be treated as the same - * - * @retval +1 if the first is later - * - * @retval -1 if the 2nd is later - **/ -int cmp_time(time_t file1, time_t file2) + * Returns 1 if the times the "same", or 0 if they are different. */ +int same_time(time_t f1_sec, unsigned long f1_nsec, time_t f2_sec, unsigned long f2_nsec) { - if (file2 > file1) { - if (file2 - file1 <= modify_window) - return 0; - return -1; - } - if (file1 - file2 <= modify_window) - return 0; - return 1; + if (modify_window == 0) + return f1_sec == f2_sec; + if (modify_window < 0) + return f1_sec == f2_sec && f1_nsec == f2_nsec; + /* The nano seconds doesn't figure into these checks -- time windows don't care about that. */ + if (f2_sec > f1_sec) + return f2_sec - f1_sec <= modify_window; + return f1_sec - f2_sec <= modify_window; } - #ifdef __INSURE__XX #include @@ -1399,11 +1459,13 @@ int cmp_time(time_t file1, time_t file2) int _Insure_trap_error(int a1, int a2, int a3, int a4, int a5, int a6) { static int (*fn)(); - int ret; + int ret, pid_int = getpid(); char *cmd; - asprintf(&cmd, "/usr/X11R6/bin/xterm -display :0 -T Panic -n Panic -e /bin/sh -c 'cat /tmp/ierrs.*.%d ; gdb /proc/%d/exe %d'", - getpid(), getpid(), getpid()); + if (asprintf(&cmd, + "/usr/X11R6/bin/xterm -display :0 -T Panic -n Panic -e /bin/sh -c 'cat /tmp/ierrs.*.%d ; " + "gdb /proc/%d/exe %d'", pid_int, pid_int, pid_int) < 0) + return -1; if (!fn) { static void *h; @@ -1421,24 +1483,6 @@ int _Insure_trap_error(int a1, int a2, int a3, int a4, } #endif -#define MALLOC_MAX 0x40000000 - -void *_new_array(unsigned long num, unsigned int size, int use_calloc) -{ - if (num >= MALLOC_MAX/size) - return NULL; - return use_calloc ? calloc(num, size) : malloc(num * size); -} - -void *_realloc_array(void *ptr, unsigned int size, size_t num) -{ - if (num >= MALLOC_MAX/size) - return NULL; - if (!ptr) - return malloc(size * num); - return realloc(ptr, size * num); -} - /* Take a filename and filename length and return the most significant * filename suffix we can find. This ignores suffixes such as "~", * ".bak", ".orig", ".~1~", etc. */ @@ -1475,8 +1519,7 @@ const char *find_filename_suffix(const char *fn, int f } else if (s_len == 5) { if (strcmp(s+1, "orig") == 0) continue; - } else if (s_len > 2 && had_tilde - && s[1] == '~' && isDigit(s + 2)) + } else if (s_len > 2 && had_tilde && s[1] == '~' && isDigit(s + 2)) continue; *len_ptr = s_len; suf = s; @@ -1487,7 +1530,7 @@ const char *find_filename_suffix(const char *fn, int f if (!isDigit(s)) return suf; } - /* An all-digit suffix may not be that signficant. */ + /* An all-digit suffix may not be that significant. */ s = suf; } @@ -1503,11 +1546,11 @@ const char *find_filename_suffix(const char *fn, int f #define UNIT (1 << 16) -uint32 fuzzy_distance(const char *s1, int len1, const char *s2, int len2) +uint32 fuzzy_distance(const char *s1, unsigned len1, const char *s2, unsigned len2) { uint32 a[MAXPATHLEN], diag, above, left, diag_inc, above_inc, left_inc; int32 cost; - int i1, i2; + unsigned i1, i2; if (!len1 || !len2) { if (!len1) { @@ -1551,8 +1594,8 @@ uint32 fuzzy_distance(const char *s1, int len1, const #define BB_PER_SLOT_INTS (BB_SLOT_SIZE / 4) /* Number of int32s per slot */ struct bitbag { - uint32 **bits; - int slot_cnt; + uint32 **bits; + int slot_cnt; }; struct bitbag *bitbag_create(int max_ndx) @@ -1560,8 +1603,7 @@ struct bitbag *bitbag_create(int max_ndx) struct bitbag *bb = new(struct bitbag); bb->slot_cnt = (max_ndx + BB_PER_SLOT_BITS - 1) / BB_PER_SLOT_BITS; - if (!(bb->bits = (uint32**)calloc(bb->slot_cnt, sizeof (uint32*)))) - out_of_memory("bitbag_create"); + bb->bits = new_array0(uint32*, bb->slot_cnt); return bb; } @@ -1571,10 +1613,8 @@ void bitbag_set_bit(struct bitbag *bb, int ndx) int slot = ndx / BB_PER_SLOT_BITS; ndx %= BB_PER_SLOT_BITS; - if (!bb->bits[slot]) { - if (!(bb->bits[slot] = (uint32*)calloc(BB_PER_SLOT_INTS, 4))) - out_of_memory("bitbag_set_bit"); - } + if (!bb->bits[slot]) + bb->bits[slot] = new_array0(uint32, BB_PER_SLOT_INTS); bb->bits[slot][ndx/32] |= 1u << (ndx % 32); } @@ -1642,8 +1682,7 @@ void flist_ndx_push(flist_ndx_list *lp, int ndx) { struct flist_ndx_item *item; - if (!(item = new(struct flist_ndx_item))) - out_of_memory("flist_ndx_push"); + item = new(struct flist_ndx_item); item->next = NULL; item->ndx = ndx; if (lp->tail) @@ -1671,33 +1710,48 @@ int flist_ndx_pop(flist_ndx_list *lp) return ndx; } -void *expand_item_list(item_list *lp, size_t item_size, - const char *desc, int incr) +/* Make sure there is room for one more item in the item list. If there + * is not, expand the list as indicated by the value of "incr": + * - if incr < 0 then increase the malloced size by -1 * incr + * - if incr >= 0 then either make the malloced size equal to "incr" + * or (if that's not large enough) double the malloced size + * After the size check, the list's count is incremented by 1 and a pointer + * to the "new" list item is returned. + */ +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; + size_t expand_size; if (incr < 0) - new_size += -incr; /* increase slowly */ - else if (new_size < (size_t)incr) - new_size += incr; + expand_size = -incr; /* increase slowly */ + else if (lp->malloced < (size_t)incr) + expand_size = incr - lp->malloced; + else if (lp->malloced) + expand_size = lp->malloced; /* double in size */ else - new_size *= 2; - if (new_size < lp->malloced) + expand_size = 1; + if (SIZE_MAX/item_size - expand_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, + expand_size += lp->malloced; + new_ptr = realloc_buf(lp->items, expand_size * item_size); + if (DEBUG_GTE(FLIST, 3)) { + rprintf(FINFO, "[%s] expand %s to %s bytes, did%s move\n", + who_am_i(), desc, big_num(expand_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; + lp->malloced = expand_size; } return (char*)lp->items + (lp->count++ * item_size); +} + +/* This zeroing of memory won't be optimized away by the compiler. */ +void force_memzero(void *buf, size_t len) +{ + volatile uchar *z = buf; + while (len-- > 0) + *z++ = '\0'; }