X-Git-Url: https://mattmccutchen.net/rsync/rsync.git/blobdiff_plain/1b42f628f495ff0cdaa8a7c219d8ce33192281fe..e107b6b122812d88d531f3826e1a510abe916006:/util.c diff --git a/util.c b/util.c index 7a320258..74341930 100644 --- a/util.c +++ b/util.c @@ -152,7 +152,7 @@ int set_modtime(const char *fname, time_t modtime, mode_t mode) } # endif return utimes(fname, t); -#elif defined HAVE_UTIMBUF +#elif defined HAVE_STRUCT_UTIMBUF struct utimbuf tbuf; tbuf.actime = time(NULL); tbuf.modtime = modtime; @@ -261,38 +261,41 @@ 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. * * 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) { + rsyserr(FERROR_XFER, errno, "open %s", full_fname(source)); 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) { + rsyserr(FERROR_XFER, errno, "unlink %s", full_fname(dest)); + 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 + && (!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; + } } while ((len = safe_read(ifd, buf, sizeof buf)) > 0) { if (full_write(ofd, buf, len) < 0) { - rsyserr(FERROR, errno, "write %s", full_fname(dest)); + rsyserr(FERROR_XFER, errno, "write %s", full_fname(dest)); close(ifd); close(ofd); return -1; @@ -300,19 +303,19 @@ int copy_file(const char *source, const char *dest, mode_t mode) } if (len < 0) { - rsyserr(FERROR, errno, "read %s", full_fname(source)); + rsyserr(FERROR_XFER, errno, "read %s", full_fname(source)); close(ifd); close(ofd); 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", + rsyserr(FERROR_XFER, errno, "close failed on %s", full_fname(dest)); return -1; } @@ -368,7 +371,7 @@ int robust_unlink(const char *fname) } 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); } @@ -407,7 +410,7 @@ int robust_rename(const char *from, const char *to, const 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; @@ -546,7 +549,7 @@ void glob_expand(char *s, char ***argv_ptr, int *argc_ptr, int *maxargs_ptr) s = "."; if (sanitize_paths) - s = sanitize_path(NULL, s, "", 0, NULL); + s = sanitize_path(NULL, s, "", 0); else s = strdup(s); if (!s) @@ -687,11 +690,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 interior "." + * elements, drops an intial "./" unless CFN_KEEP_LEADING_DOT_DIR is flagged, + * will even 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, will even collapse ".." + * elements (except at the start of the string) if CFN_COLLAPSE_DOT_DOT_DIRS + * is flagged. If the resulting name would be empty, we return ".". */ +unsigned int clean_fname(char *name, int flags) { char *limit = name - 1, *t = name, *f = name; int anchored; @@ -701,6 +707,10 @@ unsigned int clean_fname(char *name, BOOL collapse_dot_dot) if ((anchored = *f == '/') != 0) *t++ = *f++; + else if (flags & CFN_KEEP_LEADING_DOT_DIR && *f == '.' && f[1] == '/') { + *t++ = *f++; + *t++ = *f++; + } while (*f) { /* discard extra slashes */ if (*f == '/') { @@ -708,14 +718,16 @@ unsigned int clean_fname(char *name, BOOL collapse_dot_dot) continue; } if (*f == '.') { - /* discard "." dirs (but NOT a trailing '.'!) */ + /* discard interior "." dirs */ if (f[1] == '/') { 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; @@ -733,7 +745,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++ = '.'; @@ -754,8 +766,7 @@ unsigned int clean_fname(char *name, BOOL collapse_dot_dot) * 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 - * the target path, and **return NULL if any '..'s try to escape**. + * path. * * We also clean the path in a manner similar to clean_fname() but with a * few differences: @@ -765,17 +776,11 @@ 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, - const char *symlink) +char *sanitize_path(char *dest, const char *p, const char *rootdir, int depth) { - char *start, *sanp, *save_dest = dest; + char *start, *sanp; int rlen = 0, leave_one_dotdir = relative_paths; - if (symlink && *symlink == '/') { - p = symlink; - symlink = ""; - } - if (dest != p) { int plen = strlen(p); if (*p == '/') { @@ -798,18 +803,7 @@ char *sanitize_path(char *dest, const char *p, const char *rootdir, int depth, } start = sanp = dest + rlen; - while (1) { - if (*p == '\0') { - if (!symlink || !*symlink) - break; - while (sanp != start && sanp[-1] != '/') { - /* strip last element */ - sanp--; - } - /* Append a relative symlink */ - p = symlink; - symlink = ""; - } + while (*p) { /* discard leading or extra slashes */ if (*p == '/') { p++; @@ -831,11 +825,6 @@ char *sanitize_path(char *dest, const char *p, const char *rootdir, int depth, if (*p == '.' && p[1] == '.' && (p[2] == '/' || p[2] == '\0')) { /* ".." component followed by slash or end */ if (depth <= 0 || sanp != start) { - if (symlink && sanp == start) { - if (!save_dest) - free(dest); - return NULL; - } p += 2; if (sanp != start) { /* back up sanp one level */ @@ -902,7 +891,7 @@ int push_dir(const char *dir, int set_path_only) curr_dir_len += len; } - curr_dir_len = clean_fname(curr_dir, 1); + 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;