X-Git-Url: https://mattmccutchen.net/rsync/rsync.git/blobdiff_plain/44e2e57837bcdce0d12a429c308211763ab030db..cb13abfed024d0320c0aa865ad652c946321df3c:/util.c diff --git a/util.c b/util.c index cadf3ebf..974e8651 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) { @@ -400,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, int sanitize_paths) +static void glob_expand_one(char *s, char **argv, int *argc, int maxargs) { #if !(defined(HAVE_GLOB) && defined(HAVE_GLOB_H)) if (!*s) s = "."; @@ -408,14 +485,16 @@ static void glob_expand_one(char *s, char **argv, int *argc, int maxargs, int sa (*argc)++; return; #else + extern int sanitize_paths; glob_t globbuf; int i; if (!*s) s = "."; - s = strdup(s); - sanitize_path(s); - argv[*argc] = s; + argv[*argc] = strdup(s); + if (sanitize_paths) { + sanitize_path(argv[*argc], NULL); + } memset(&globbuf, 0, sizeof(globbuf)); glob(argv[*argc], 0, NULL, &globbuf); @@ -434,7 +513,7 @@ static void glob_expand_one(char *s, char **argv, int *argc, int maxargs, int sa #endif } -void glob_expand(char *base1, char **argv, int *argc, int maxargs, int sanitize_paths) +void glob_expand(char *base1, char **argv, int *argc, int maxargs) { char *s = argv[*argc]; char *p, *q; @@ -458,11 +537,11 @@ void glob_expand(char *base1, char **argv, int *argc, int maxargs, int sanitize_ while ((p = strstr(q,base)) && ((*argc) < maxargs)) { /* split it at this point */ *p = 0; - glob_expand_one(q, argv, argc, maxargs, sanitize_paths); + glob_expand_one(q, argv, argc, maxargs); q = p+strlen(base); } - if (*q && (*argc < maxargs)) glob_expand_one(q, argv, argc, maxargs, sanitize_paths); + if (*q && (*argc < maxargs)) glob_expand_one(q, argv, argc, maxargs); free(s); free(base); @@ -558,8 +637,11 @@ void clean_fname(char *name) /* * Make path appear as if a chroot had occurred: * 1. remove leading "/" (or replace with "." if at end) - * 2. remove leading ".." components + * 2. remove leading ".." components (except those allowed by "reldir") * 3. delete any other "/.." (recursively) + * If "reldir" is non-null, it is a sanitized directory that the path will be + * relative to, so allow as many ".." at the beginning of the path as + * there are components in reldir. * 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. @@ -567,10 +649,20 @@ void clean_fname(char *name) * Contributed by Dave Dykstra */ -void sanitize_path(char *p) +void sanitize_path(char *p, char *reldir) { char *start, *sanp; + int depth = 0; + int allowdotdot = 0; + if (reldir) { + depth++; + while (*reldir) { + if (*reldir++ == '/') { + depth++; + } + } + } start = p; sanp = p; while (*p == '/') { @@ -588,35 +680,48 @@ void sanitize_path(char *p) /* skip following slashes */ ; } - } else if ((*p == '.') && (*(p+1) == '.') && + continue; + } + allowdotdot = 0; + if ((*p == '.') && (*(p+1) == '.') && ((*(p+2) == '/') || (*(p+2) == '\0'))) { - /* skip ".." component followed by slash or end */ - p += 2; - if (*p == '/') - p++; - if (sanp != start) { - /* back up sanp one level */ - --sanp; /* now pointing at slash */ - while ((sanp > start) && (*(sanp - 1) != '/')) { - /* skip back up to slash */ - sanp--; + /* ".." component followed by slash or end */ + if ((depth > 0) && (sanp == start)) { + /* allow depth levels of .. at the beginning */ + --depth; + allowdotdot = 1; + } else { + p += 2; + if (*p == '/') + p++; + if (sanp != start) { + /* back up sanp one level */ + --sanp; /* now pointing at slash */ + while ((sanp > start) && (*(sanp - 1) != '/')) { + /* skip back up to slash */ + sanp--; + } } + continue; } - } else { - while (1) { - /* copy one component through next slash */ - *sanp++ = *p++; - if ((*p == '\0') || (*(p-1) == '/')) { - while (*p == '/') { - /* skip multiple slashes */ - p++; - } - break; + } + while (1) { + /* copy one component through next slash */ + *sanp++ = *p++; + if ((*p == '\0') || (*(p-1) == '/')) { + while (*p == '/') { + /* skip multiple slashes */ + p++; } + break; } } + if (allowdotdot) { + /* move the virtual beginning to leave the .. alone */ + start = sanp; + } } - if (sanp == start) { + if ((sanp == start) && !allowdotdot) { /* ended up with nothing, so put in "." component */ *sanp++ = '.'; } @@ -638,6 +743,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) {