X-Git-Url: https://mattmccutchen.net/rsync/rsync.git/blobdiff_plain/c284f34a498a4b7c85666df867024be63a511d55..9f27cd8ca6ccbf1ab4cd8b0b66445535e2573d59:/util.c diff --git a/util.c b/util.c index d0d1c2b1..5129c35b 100644 --- a/util.c +++ b/util.c @@ -28,6 +28,7 @@ #include "rsync.h" extern int verbose; +extern struct exclude_list_struct server_exclude_list; int sanitize_paths = 0; @@ -252,20 +253,20 @@ int copy_file(char *source, char *dest, mode_t mode) ifd = do_open(source, O_RDONLY, 0); if (ifd == -1) { rprintf(FERROR,"open %s: %s\n", - source,strerror(errno)); + full_fname(source), strerror(errno)); return -1; } if (robust_unlink(dest) && errno != ENOENT) { rprintf(FERROR,"unlink %s: %s\n", - dest,strerror(errno)); + full_fname(dest), strerror(errno)); return -1; } ofd = do_open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, mode); if (ofd == -1) { rprintf(FERROR,"open %s: %s\n", - dest,strerror(errno)); + full_fname(dest), strerror(errno)); close(ifd); return -1; } @@ -273,19 +274,27 @@ int copy_file(char *source, char *dest, mode_t mode) while ((len = safe_read(ifd, buf, sizeof buf)) > 0) { if (full_write(ofd, buf, len) < 0) { rprintf(FERROR,"write %s: %s\n", - dest,strerror(errno)); + full_fname(dest), strerror(errno)); close(ifd); close(ofd); return -1; } } - close(ifd); - close(ofd); + if (close(ifd) < 0) { + rprintf(FINFO, "close failed on %s: %s\n", + full_fname(source), strerror(errno)); + } + + if (close(ofd) < 0) { + rprintf(FERROR, "close failed on %s: %s\n", + full_fname(dest), strerror(errno)); + return -1; + } if (len < 0) { rprintf(FERROR,"read %s: %s\n", - source,strerror(errno)); + full_fname(source), strerror(errno)); return -1; } @@ -353,18 +362,33 @@ int robust_unlink(char *fname) #endif } -int robust_rename(char *from, char *to) +/* Returns 0 on success, -1 on most errors, and -2 if we got an error + * trying to copy the file across file systems. */ +int robust_rename(char *from, char *to, int mode) { -#ifndef ETXTBSY - return do_rename(from, to); -#else - int rc = do_rename(from, to); - if (rc == 0 || errno != ETXTBSY) - return rc; - if (robust_unlink(to) != 0) - return -1; - return do_rename(from, to); + int tries = 4; + + while (tries--) { + if (do_rename(from, to) == 0) + return 0; + + switch (errno) { +#ifdef ETXTBSY + case ETXTBSY: + if (robust_unlink(to) != 0) + return -1; + break; #endif + case EXDEV: + if (copy_file(from, to, mode) != 0) + return -2; + do_unlink(from); + return 0; + default: + return -1; + } + } + return -1; } @@ -456,12 +480,11 @@ int lock_range(int fd, int offset, int len) static int exclude_server_path(char *arg) { char *s; - extern struct exclude_struct **server_exclude_list; - if (server_exclude_list) { + if (server_exclude_list.head) { for (s = arg; (s = strchr(s, '/')) != NULL; ) { *s = '\0'; - if (check_exclude(server_exclude_list, arg, 1)) { + if (check_exclude(&server_exclude_list, arg, 1) < 0) { /* We must leave arg truncated! */ return 1; } @@ -558,8 +581,8 @@ void strlower(char *s) /* 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 whole thing - * fits into destsize (including the terminating '\0'). */ + * strings + 1 (if '/' was inserted), regardless of whether the null-terminated + * string fits into destsize. */ size_t pathjoin(char *dest, size_t destsize, const char *p1, const char *p2) { size_t len = strlcpy(dest, p1, destsize); @@ -579,9 +602,9 @@ size_t pathjoin(char *dest, size_t destsize, const char *p1, const char *p2) } /* Join any number of strings together, putting them in "dest". The return - * value is the length of all the strings, regardless of whether they fit in - * destsize (including the terminating '\0'). Your list of string pointers - * should end with a NULL to indicate the end of the list. */ + * value is the length of all the strings, regardless of whether the null- + * terminated whole fits in destsize. Your list of string pointers must end + * with a NULL to indicate the end of the list. */ size_t stringjoin(char *dest, size_t destsize, ...) { va_list ap; @@ -734,7 +757,7 @@ void sanitize_path(char *p, char *reldir) while (1) { /* copy one component through next slash */ *sanp++ = *p++; - if (*p == '\0' || p[1] == '/') { + if (*p == '\0' || p[-1] == '/') { while (*p == '/') { /* skip multiple slashes */ p++; @@ -760,6 +783,39 @@ void sanitize_path(char *p, char *reldir) *sanp = '\0'; } +/* Works much like sanitize_path(), with these differences: (1) a new buffer + * is allocated for the sanitized path rather than modifying it in-place; (2) + * a leading slash gets transformed into the rootdir value (which can be empty + * or NULL if you just want the slash to get dropped); (3) no "reldir" can be + * specified. */ +char *alloc_sanitize_path(const char *path, const char *rootdir) +{ + char *buf; + int rlen, plen = strlen(path); + + if (*path == '/' && rootdir) { + rlen = strlen(rootdir); + if (rlen == 1) + path++; + } else + rlen = 0; + if (!(buf = new_array(char, rlen + plen + 1))) + out_of_memory("alloc_sanitize_path"); + if (rlen) + memcpy(buf, rootdir, rlen); + memcpy(buf + rlen, path, plen + 1); + + if (rlen > 1) + rlen++; + sanitize_path(buf + rlen, NULL); + if (rlen && buf[rlen] == '.' && buf[rlen+1] == '\0') { + if (rlen > 1) + rlen--; + buf[rlen] = '\0'; + } + + return buf; +} char curr_dir[MAXPATHLEN]; unsigned int curr_dir_len;