--- embedaddon/rsync/util.c 2016/11/01 09:54:32 1.1.1.3 +++ 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-2015 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 @@ -27,12 +27,14 @@ 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 preserve_xattrs; extern int preallocate_files; +extern int force_change; extern char *module_dir; extern unsigned int module_dirlen; extern char *partial_dir; @@ -115,56 +117,102 @@ void print_child_argv(const char *prefix, char **cmd) rprintf(FCLIENT, " (%d args)\n", cnt); } +#ifdef SUPPORT_FORCE_CHANGE +static int try_a_force_change(const char *fname, STRUCT_STAT *stp) +{ + 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; + + 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, uint32 mod_nsec, mode_t mode) +int set_times(const char *fname, STRUCT_STAT *stp) { static int switch_step = 0; if (DEBUG_GTE(TIME, 1)) { - rprintf(FINFO, "set modtime of %s to (%ld) %s", - fname, (long)modtime, - asctime(localtime(&modtime))); + 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, mod_nsec) == 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, mod_nsec) == 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, mod_nsec) == 0) + if (do_utimes(fname, stp) == 0) break; #else - if (do_utime(fname, modtime, mod_nsec) == 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; } @@ -175,7 +223,7 @@ int set_modtime(const char *fname, time_t modtime, uin /* Create any necessary directories in fname. Any missing directories are * created with default permissions. Returns < 0 on error, or the number * of directories created. */ -int make_path(char *fname, int flags) +int make_path(char *fname, mode_t mode, int flags) { char *end, *p; int ret = 0; @@ -206,7 +254,7 @@ int make_path(char *fname, int flags) else errno = ENOTDIR; } - } else if (do_mkdir(fname, ACCESSPERMS) == 0) { + } else if (do_mkdir(fname, mode) == 0) { ret++; break; } @@ -245,7 +293,7 @@ int make_path(char *fname, int flags) p += strlen(p); if (ret < 0) /* Skip mkdir on error, but keep restoring the path. */ continue; - if (do_mkdir(fname, ACCESSPERMS) < 0) + if (do_mkdir(fname, mode) < 0) ret = -ret - 1; else ret++; @@ -323,9 +371,7 @@ int copy_file(const char *source, const char *dest, in int ifd; char buf[1024 * 8]; int len; /* Number of bytes read into `buf'. */ -#ifdef PREALLOCATE_NEEDS_TRUNCATE - OFF_T preallocated_len = 0, offset = 0; -#endif + OFF_T prealloc_len = 0, offset = 0; if ((ifd = do_open(source, O_RDONLY, 0)) < 0) { int save_errno = errno; @@ -338,6 +384,7 @@ 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; } @@ -365,11 +412,8 @@ int copy_file(const char *source, const char *dest, in if (do_fstat(ifd, &srcst) < 0) rsyserr(FWARNING, errno, "fstat %s", full_fname(source)); else if (srcst.st_size > 0) { - if (do_fallocate(ofd, 0, srcst.st_size) == 0) { -#ifdef PREALLOCATE_NEEDS_TRUNCATE - preallocated_len = srcst.st_size; -#endif - } else + prealloc_len = do_fallocate(ofd, 0, srcst.st_size); + if (prealloc_len < 0) rsyserr(FWARNING, errno, "do_fallocate %s", full_fname(dest)); } } @@ -384,9 +428,7 @@ int copy_file(const char *source, const char *dest, in errno = save_errno; return -1; } -#ifdef PREALLOCATE_NEEDS_TRUNCATE offset += len; -#endif } if (len < 0) { @@ -403,16 +445,21 @@ int copy_file(const char *source, const char *dest, in full_fname(source)); } -#ifdef PREALLOCATE_NEEDS_TRUNCATE /* Source file might have shrunk since we fstatted it. * Cut off any extra preallocated zeros from dest file. */ - if (offset < preallocated_len && do_ftruncate(ofd, offset) < 0) { + 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)); } -#endif + 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", @@ -499,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; @@ -588,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); @@ -691,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); } } @@ -716,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); @@ -769,8 +815,7 @@ void glob_expand_module(char *base1, char *arg, char * return; } - if (!(arg = strdup(arg))) - out_of_memory("glob_expand_module"); + arg = strdup(arg); if (asprintf(&base," %s/", base1) < 0) out_of_memory("glob_expand_module"); @@ -800,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 @@ -852,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; @@ -966,14 +1065,13 @@ 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; @@ -981,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++] = '/'; } } @@ -1077,6 +1174,7 @@ int change_dir(const char *dir, int set_path_only) 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; @@ -1086,6 +1184,7 @@ 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; } @@ -1117,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); @@ -1133,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 @@ -1226,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; } @@ -1299,43 +1421,31 @@ int unsafe_symlink(const char *dest, const char *src) /* 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; } /* 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) { - /* The final comparison makes sure that modify_window doesn't overflow a - * time_t, which would mean that file2 must be in the equality window. */ - if (!modify_window || (file2 > file1 + modify_window && file1 + modify_window > file1)) - return -1; - } else if (file1 > file2) { - if (!modify_window || (file1 > file2 + modify_window && file2 + modify_window > file2)) - return 1; - } - return 0; + 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 @@ -1409,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; @@ -1421,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; } @@ -1485,8 +1594,8 @@ uint32 fuzzy_distance(const char *s1, unsigned len1, c #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) @@ -1494,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; } @@ -1505,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); } @@ -1576,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) @@ -1613,35 +1718,40 @@ int flist_ndx_pop(flist_ndx_list *lp) * 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) +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; - else if (new_size) - new_size *= 2; + 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 = 1; - 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); + 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(new_size * item_size), + 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'; }