X-Git-Url: https://mattmccutchen.net/rsync/rsync.git/blobdiff_plain/0f78b81511be65d8fe21af1e6ac674f9e80ac29d..e9489cd6cb380b30727bd3d074eacc59abca080e:/util.c diff --git a/util.c b/util.c index 2ec56a7c..c71830bd 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-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 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, @@ -16,12 +16,12 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * You should have received a copy of the GNU General Public License along + * 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,18 +29,25 @@ extern int module_id; extern int modify_window; extern int relative_paths; extern int human_readable; +extern int preserve_xattrs; +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; +char curr_dir[MAXPATHLEN]; +unsigned int curr_dir_len; +int curr_dir_depth; /* This is only set for a sanitizing daemon. */ + /* Set a fd into nonblocking mode. */ 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; @@ -53,7 +60,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; @@ -85,9 +92,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(FINFO, "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 @@ -96,27 +103,27 @@ void print_child_argv(char **cmd) "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789" ",.-_=+@/") != strlen(*cmd)) { - rprintf(FINFO, "\"%s\" ", *cmd); + rprintf(FCLIENT, "\"%s\" ", *cmd); } else { - rprintf(FINFO, "%s ", *cmd); + rprintf(FCLIENT, "%s ", *cmd); } } - rprintf(FINFO, "\n"); + 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\n", 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\n", 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)) @@ -140,11 +147,14 @@ 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)) { + 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; @@ -209,7 +219,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; @@ -253,62 +263,90 @@ 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. + * In either case, if --xattrs are being preserved, the dest file will + * have its xattrs set from the source file. * * 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) { + int save_errno = errno; + rsyserr(FERROR_XFER, errno, "open %s", full_fname(source)); + errno = save_errno; 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) { + int save_errno = errno; + rsyserr(FERROR_XFER, errno, "unlink %s", full_fname(dest)); + errno = save_errno; + 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) { + 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) { - rsyserr(FERROR, errno, "write %s", full_fname(dest)); + 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) { - rsyserr(FERROR, errno, "read %s", full_fname(source)); + int save_errno = errno; + rsyserr(FERROR_XFER, errno, "read %s", full_fname(source)); close(ifd); close(ofd); + errno = save_errno; 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", + int save_errno = errno; + rsyserr(FERROR_XFER, errno, "close failed on %s", full_fname(dest)); + errno = save_errno; return -1; } +#ifdef SUPPORT_XATTRS + if (preserve_xattrs) + copy_xattrs(source, dest); +#endif + return 0; } @@ -354,13 +392,13 @@ 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); 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); } @@ -377,7 +415,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; @@ -389,8 +427,11 @@ int robust_rename(char *from, char *to, 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: @@ -399,7 +440,7 @@ int robust_rename(char *from, char *to, 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; @@ -454,31 +495,27 @@ 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_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(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 */ @@ -495,109 +532,199 @@ int lock_range(int fd, int offset, int len) return fcntl(fd,F_SETLK,&lock) == 0; } -static int filter_server_path(char *arg) -{ - char *s; +#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") - if (server_filter_list.head) { - for (s = arg; (s = strchr(s, '/')) != NULL; ) { - *s = '\0'; - if (check_filter(&server_filter_list, arg, 1) < 0) { - /* We must leave arg truncated! */ - return 1; - } - *s++ = '/'; +static inline void call_glob_match(const char *name, int len, int from_glob, + char *arg, int abpos, int fbpos); + +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) +{ + int len; + char *slash; + + while (*arg == '.' && arg[1] == '/') { + if (fbpos < 0) { + ENSURE_MEMSPACE(glob.filt_buf, char, glob.fbsize, glob.absize); + memcpy(glob.filt_buf, glob.arg_buf, abpos + 1); + fbpos = abpos; } + 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'; } - return 0; + if ((slash = strchr(arg, '/')) != NULL) { + *slash = '\0'; + len = slash - arg; + } else + len = strlen(arg); + if (strpbrk(arg, "*?[")) { + struct dirent *di; + DIR *d; + + if (!(d = opendir(abpos ? glob.arg_buf : "."))) + return; + while ((di = readdir(d)) != NULL) { + char *dname = d_name(di); + if (dname[0] == '.' && (dname[1] == '\0' + || (dname[1] == '.' && dname[2] == '\0'))) + continue; + if (!wildmatch(arg, dname)) + continue; + call_glob_match(dname, strlen(dname), 1, + slash ? arg + len + 1 : NULL, + abpos, fbpos); + } + closedir(d); + } else { + call_glob_match(arg, len, 0, + slash ? arg + len + 1 : NULL, + abpos, fbpos); + } + if (slash) + *slash = '/'; } -static void glob_expand_one(char *s, char ***argv_ptr, int *argc_ptr, - int *maxargs_ptr) +static inline void call_glob_match(const char *name, int len, int from_glob, + char *arg, int abpos, int fbpos) { - char **argv = *argv_ptr; - int argc = *argc_ptr; - int maxargs = *maxargs_ptr; -#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"); - *argv_ptr = argv; - *maxargs_ptr = maxargs; + char *use_buf; + + ENSURE_MEMSPACE(glob.arg_buf, char, glob.absize, abpos + len + 2); + memcpy(glob.arg_buf + abpos, name, len); + abpos += len; + glob.arg_buf[abpos] = '\0'; + + if (fbpos >= 0) { + ENSURE_MEMSPACE(glob.filt_buf, char, glob.fbsize, fbpos + len + 2); + memcpy(glob.filt_buf + fbpos, name, len); + fbpos += len; + glob.filt_buf[fbpos] = '\0'; + use_buf = glob.filt_buf; + } else + use_buf = glob.arg_buf; + + if (from_glob || (arg && len)) { + STRUCT_STAT st; + int is_dir; + + 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, FLOG, use_buf, is_dir) < 0) + return; } - if (!*s) - s = "."; - s = argv[argc++] = strdup(s); - filter_server_path(s); -#else - glob_t globbuf; - if (maxargs <= argc) - return; - if (!*s) - s = "."; + if (arg) { + glob.arg_buf[abpos++] = '/'; + glob.arg_buf[abpos] = '\0'; + if (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(glob.arg_buf))) + out_of_memory("glob_match"); + } +} - if (sanitize_paths) - s = sanitize_path(NULL, s, "", 0); - else - s = strdup(s); - - 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; - if (!(argv = realloc_array(argv, char *, maxargs))) - out_of_memory("glob_expand_one"); - *argv_ptr = argv; - *maxargs_ptr = maxargs; +/* This routine performs wild-card expansion of the pathname in "arg". Any + * 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; + + if (!arg) { + if (glob.filt_buf) + free(glob.filt_buf); + free(glob.arg_buf); + memset(&glob, 0, sizeof glob); + return -1; } - if (globbuf.gl_pathc == 0) - argv[argc++] = s; + + if (sanitize_paths) + s = sanitize_path(NULL, arg, "", 0, SP_KEEP_DOT_DIRS); else { - int i; + 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); + } + + ENSURE_MEMSPACE(glob.arg_buf, char, glob.absize, MAXPATHLEN); + *glob.arg_buf = '\0'; + + glob.argc = save_argc = *argc_p; + glob.argv = *argv_p; + glob.maxargs = *maxargs_p; + + 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; + ret = 0; + } else { 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"); - } + ret = 1; } - globfree(&globbuf); -#endif - *argc_ptr = argc; + + *maxargs_p = glob.maxargs; + *argv_p = glob.argv; + *argc_p = glob.argc; + + return ret; } /* 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_p, int *argc_p, int *maxargs_p) { - 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_p, argc_p, maxargs_p); if (!p) break; } - free(s); + free(arg); free(base); } @@ -607,8 +734,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++; } } @@ -670,7 +797,7 @@ int count_dir_elements(const char *p) int cnt = 0, new_component = 1; while (*p) { if (*p++ == '/') - new_component = 1; + new_component = (*p != '.' || (p[1] != '/' && p[1] != '\0')); else if (new_component) { new_component = 0; cnt++; @@ -679,11 +806,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; @@ -693,6 +823,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 == '/') { @@ -700,14 +834,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; @@ -725,7 +861,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++ = '.'; @@ -743,10 +879,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". * - * If depth is >= 0, it is a count of how many '..'s to allow at the start - * of the path. Use -1 to allow unlimited depth. + * The depth var is a count of how many '..'s to allow at the start of the + * path. * * We also clean the path in a manner similar to clean_fname() but with a * few differences: @@ -756,16 +892,17 @@ unsigned int clean_fname(char *name, BOOL collapse_dot_dot) * 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) +char *sanitize_path(char *dest, const char *p, const char *rootdir, int depth, + int flags) { char *start, *sanp; - int rlen = 0, leave_one_dotdir = relative_paths; + 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++; @@ -782,21 +919,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 (*p != '\0') { + /* 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; @@ -809,10 +947,8 @@ char *sanitize_path(char *dest, const char *p, const char *rootdir, int depth) 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; } @@ -833,15 +969,10 @@ char *sanitize_path(char *dest, const char *p, const char *rootdir, int depth) return dest; } -char curr_dir[MAXPATHLEN]; -unsigned int curr_dir_len; - -/** - * Like chdir(), but it keeps track of the current directory (in the +/* 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) + * Also cleans the path using the clean_fname() function. */ +int change_dir(const char *dir, int set_path_only) { static int initialised; unsigned int len; @@ -859,38 +990,37 @@ int push_dir(char *dir) if (len == 1 && *dir == '.') return 1; - if ((*dir == '/' ? len : curr_dir_len + 1 + len) >= sizeof curr_dir) - return 0; - - if (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; - } - - curr_dir_len = clean_fname(curr_dir, 1); + 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); - return 1; -} + if (!set_path_only && chdir(curr_dir)) { + curr_dir[curr_dir_len] = '\0'; + return 0; + } + } -/** - * 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) -{ - if (chdir(dir)) - return 0; + 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); + } - 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 (verbose >= 5 && !set_path_only) + rprintf(FINFO, "[%s] change_dir(%s)\n", who_am_i(), curr_dir); return 1; } @@ -912,7 +1042,7 @@ char *full_fname(const char *fn) if (*fn == '/') p1 = p2 = ""; else { - p1 = curr_dir; + p1 = curr_dir + module_dirlen; for (p2 = p1; *p2 == '/'; p2++) {} if (*p2) p2 = "/"; @@ -921,17 +1051,11 @@ char *full_fname(const char *fn) m1 = " (in "; m2 = lp_name(module_id); m3 = ")"; - if (p1 == curr_dir) { - if (!lp_use_chroot(module_id)) { - char *p = lp_path(module_id); - if (*p != '/' || p[1]) - p1 += strlen(p); - } - } } else m1 = m2 = m3 = ""; - asprintf(&result, "\"%s%s%s\"%s%s%s", p1, p2, fn, m1, m2, m3); + if (asprintf(&result, "\"%s%s%s\"%s%s%s", p1, p2, fn, m1, m2, m3) <= 0) + out_of_memory("full_fname"); return result; } @@ -956,15 +1080,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) { - static int len; - if (!len) - len = strlen(partial_dir); - t[len] = '\0'; - if (check_filter(&server_filter_list, partial_fname, 1) < 0) + if (daemon_filter_list.head) { + t = strrchr(partial_fname, '/'); + *t = '\0'; + if (check_filter(&daemon_filter_list, FLOG, partial_fname, 1) < 0) return NULL; - t[len] = '/'; - if (check_filter(&server_filter_list, partial_fname, 0) < 0) + *t = '/'; + if (check_filter(&daemon_filter_list, FLOG, partial_fname, 0) < 0) return NULL; } @@ -1093,7 +1215,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]; } } @@ -1104,7 +1226,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; @@ -1117,7 +1239,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); @@ -1125,9 +1247,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]; @@ -1231,18 +1351,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) +void *_realloc_array(void *ptr, unsigned int size, size_t 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); @@ -1285,7 +1404,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; @@ -1293,7 +1412,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. */ @@ -1446,3 +1565,34 @@ 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; + 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, + 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); +}