X-Git-Url: https://mattmccutchen.net/rsync/rsync.git/blobdiff_plain/37f9805dabba76b7a00bd2a0227762f6f239b598..c7c11a0d4c315a052dddf97cc6d3d63a54b4a831:/util.c diff --git a/util.c b/util.c index c5bb4a70..86ee3f0c 100644 --- a/util.c +++ b/util.c @@ -24,6 +24,8 @@ */ #include "rsync.h" +extern int verbose; + /**************************************************************************** Set a fd into nonblocking mode. Uses POSIX O_NONBLOCK if available, else @@ -287,7 +289,7 @@ int copy_file(char *source, char *dest, mode_t mode) return -1; } - if (do_unlink(dest) && errno != ENOENT) { + if (robust_unlink(dest) && errno != ENOENT) { rprintf(FERROR,"unlink %s: %s\n", dest,strerror(errno)); return -1; @@ -323,6 +325,81 @@ int copy_file(char *source, char *dest, mode_t mode) return 0; } +/* + Robust unlink: some OS'es (HPUX) refuse to unlink busy files, so + rename to /.rsyncNNN instead. Note that successive rsync runs + will shuffle the filenames around a bit as long as the file is still + busy; this is because this function does not know if the unlink call + is due to a new file coming in, or --delete trying to remove old + .rsyncNNN files, hence it renames it each time. +*/ +/* MAX_RENAMES should be 10**MAX_RENAMES_DIGITS */ +#define MAX_RENAMES_DIGITS 3 +#define MAX_RENAMES 1000 + +int robust_unlink(char *fname) +{ +#ifndef ETXTBSY + return do_unlink(fname); +#else + static int counter = 1; + int rc, pos, start; + char path[MAXPATHLEN]; + + rc = do_unlink(fname); + if ((rc == 0) || (errno != ETXTBSY)) + return rc; + + strlcpy(path, fname, MAXPATHLEN); + + pos = strlen(path); + while((path[--pos] != '/') && (pos >= 0)) + ; + ++pos; + strlcpy(&path[pos], ".rsync", MAXPATHLEN-pos); + pos += sizeof(".rsync")-1; + + if (pos > (MAXPATHLEN-MAX_RENAMES_DIGITS-1)) { + errno = ETXTBSY; + return -1; + } + + /* start where the last one left off to reduce chance of clashes */ + start = counter; + do { + sprintf(&path[pos], "%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", + fname, path); + + /* maybe we should return rename()'s exit status? Nah. */ + if (do_rename(fname, path) != 0) { + errno = ETXTBSY; + return -1; + } + return 0; +#endif +} + +int robust_rename(char *from, char *to) +{ +#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); +#endif + } + + /* sleep for a while via select */ void u_sleep(int usec) { @@ -358,37 +435,6 @@ void kill_all(int sig) } } -/* like strncpy but does not 0 fill the buffer and always null - terminates. bufsize is the size of the destination buffer */ -size_t strlcpy(char *d, const char *s, size_t bufsize) -{ - size_t len = strlen(s); - size_t ret = len; - if (len >= bufsize) len = bufsize-1; - memcpy(d, s, len); - d[len] = 0; - return ret; -} - -/* like strncat but does not 0 fill the buffer and always null - terminates. bufsize is the length of the buffer, which should - be one more than the maximum resulting string length */ -size_t strlcat(char *d, const char *s, size_t bufsize) -{ - size_t len1 = strlen(d); - size_t len2 = strlen(s); - size_t ret = len1 + len2; - - if (len1+len2 >= bufsize) { - len2 = bufsize - (len1+1); - } - if (len2 > 0) { - memcpy(d+len1, s, len2); - d[len1+len2] = 0; - } - return ret; -} - /* turn a user name into a uid */ int name_to_uid(char *name, uid_t *uid) { @@ -431,7 +477,7 @@ int lock_range(int fd, int offset, int len) } -static void glob_expand_one(char *s, char **argv, int *argc, int maxargs) +static void glob_expand_one(char *s, char **argv, int *argc, int maxargs, int sanitize_paths) { #if !(defined(HAVE_GLOB) && defined(HAVE_GLOB_H)) if (!*s) s = "."; @@ -444,7 +490,9 @@ static void glob_expand_one(char *s, char **argv, int *argc, int maxargs) if (!*s) s = "."; - argv[*argc] = strdup(s); + s = strdup(s); + sanitize_path(s); + argv[*argc] = s; memset(&globbuf, 0, sizeof(globbuf)); glob(argv[*argc], 0, NULL, &globbuf); @@ -463,7 +511,7 @@ static void glob_expand_one(char *s, char **argv, int *argc, int maxargs) #endif } -void glob_expand(char *base1, char **argv, int *argc, int maxargs) +void glob_expand(char *base1, char **argv, int *argc, int maxargs, int sanitize_paths) { char *s = argv[*argc]; char *p, *q; @@ -487,11 +535,11 @@ void glob_expand(char *base1, char **argv, int *argc, int maxargs) while ((p = strstr(q,base)) && ((*argc) < maxargs)) { /* split it at this point */ *p = 0; - glob_expand_one(q, argv, argc, maxargs); + glob_expand_one(q, argv, argc, maxargs, sanitize_paths); q = p+strlen(base); } - if (*q && (*argc < maxargs)) glob_expand_one(q, argv, argc, maxargs); + if (*q && (*argc < maxargs)) glob_expand_one(q, argv, argc, maxargs, sanitize_paths); free(s); free(base); @@ -592,23 +640,23 @@ void clean_fname(char *name) * While we're at it, remove double slashes and "." components like * clean_fname does(), but DON'T remove a trailing slash because that * is sometimes significant on command line arguments. - * Return a malloc'ed copy. + * Can only shrink paths, so sanitizes in place. * Contributed by Dave Dykstra */ -char *sanitize_path(char *p) +void sanitize_path(char *p) { - char *copy, *copyp; + char *start, *sanp; - copy = (char *) malloc(strlen(p)+1); - copyp = copy; + start = p; + sanp = p; while (*p == '/') { /* remove leading slashes */ p++; } while (*p != '\0') { /* this loop iterates once per filename component in p. - * both p (and copyp if the original had a slash) should + * 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'))) { @@ -623,18 +671,18 @@ char *sanitize_path(char *p) p += 2; if (*p == '/') p++; - if (copyp != copy) { - /* back up the copy one level */ - --copyp; /* now pointing at slash */ - while ((copyp > copy) && (*(copyp - 1) != '/')) { + if (sanp != start) { + /* back up sanp one level */ + --sanp; /* now pointing at slash */ + while ((sanp > start) && (*(sanp - 1) != '/')) { /* skip back up to slash */ - copyp--; + sanp--; } } } else { while (1) { /* copy one component through next slash */ - *copyp++ = *p++; + *sanp++ = *p++; if ((*p == '\0') || (*(p-1) == '/')) { while (*p == '/') { /* skip multiple slashes */ @@ -645,12 +693,11 @@ char *sanitize_path(char *p) } } } - if (copyp == copy) { + if (sanp == start) { /* ended up with nothing, so put in "." component */ - *copyp++ = '.'; + *sanp++ = '.'; } - *copyp = '\0'; - return(copy); + *sanp = '\0'; } @@ -668,6 +715,8 @@ char *push_dir(char *dir, int save) getcwd(curr_dir, sizeof(curr_dir)-1); } + if (!dir) return NULL; /* this call was probably just to initialize */ + if (chdir(dir)) return NULL; if (save) { @@ -708,8 +757,8 @@ int pop_dir(char *dir) to ensure that signed/unsigned usage is consistent between machines. */ int u_strcmp(const char *cs1, const char *cs2) { - const uchar *s1 = (uchar *)cs1; - const uchar *s2 = (uchar *)cs2; + const uchar *s1 = (const uchar *)cs1; + const uchar *s2 = (const uchar *)cs2; while (*s1 && *s2 && (*s1 == *s2)) { s1++; s2++;