Call sanitize_path() with updated args.
[rsync/rsync.git] / util.c
diff --git a/util.c b/util.c
index 43613e7..449b101 100644 (file)
--- a/util.c
+++ b/util.c
@@ -362,8 +362,8 @@ int robust_unlink(char *fname)
 #endif
 }
 
-/* Returns 0 on success, -1 on most errors, and -2 if we got an error
- * trying to copy the file across file systems. */
+/* Returns 0 on successful rename, 1 if we successfully copied the file
+ * across filesystems, -2 if copy_file() failed, and -1 on other errors. */
 int robust_rename(char *from, char *to, int mode)
 {
        int tries = 4;
@@ -383,7 +383,7 @@ int robust_rename(char *from, char *to, int mode)
                        if (copy_file(from, to, mode) != 0)
                                return -2;
                        do_unlink(from);
-                       return 0;
+                       return 1;
                default:
                        return -1;
                }
@@ -650,47 +650,56 @@ size_t stringjoin(char *dest, size_t destsize, ...)
        return ret;
 }
 
-void clean_fname(char *name)
+unsigned int clean_fname(char *name)
 {
-       char *p;
-       int l;
-       int modified = 1;
+       char *limit = name - 1, *t = name, *f = name;
+       int anchored;
 
        if (!name)
-               return;
-
-       while (modified) {
-               modified = 0;
+               return 0;
 
-               if ((p = strstr(name,"/./")) != NULL) {
-                       modified = 1;
-                       while (*p) {
-                               p[0] = p[2];
-                               p++;
-                       }
+       if ((anchored = *f == '/') != 0)
+               *t++ = *f++;
+       while (*f) {
+               /* discard extra slashes */
+               if (*f == '/') {
+                       f++;
+                       continue;
                }
-
-               if ((p = strstr(name,"//")) != NULL) {
-                       modified = 1;
-                       while (*p) {
-                               p[0] = p[1];
-                               p++;
+               if (*f == '.') {
+                       /* discard "." dirs (but NOT a trailing '.'!) */
+                       if (f[1] == '/') {
+                               f += 2;
+                               continue;
+                       }
+                       /* collapse ".." dirs */
+                       if (f[1] == '.' && (f[2] == '/' || !f[2])) {
+                               char *s = t - 1;
+                               if (s == name && anchored) {
+                                       f += 2;
+                                       continue;
+                               }
+                               while (s > limit && *--s != '/') {}
+                               if (s != t - 1 && (s < name || *s == '/')) {
+                                       t = s + 1;
+                                       f += 2;
+                                       continue;
+                               }
+                               *t++ = *f++;
+                               *t++ = *f++;
+                               limit = t;
                        }
                }
+               while (*f && (*t++ = *f++) != '/') {}
+       }
 
-               if (strncmp(p = name, "./", 2) == 0) {
-                       modified = 1;
-                       do {
-                               p[0] = p[2];
-                       } while (*p++);
-               }
+       if (t > name+anchored && t[-1] == '/')
+               t--;
+       if (t == name)
+               *t++ = '.';
+       *t = '\0';
 
-               l = strlen(p = name);
-               if (l > 1 && p[l-1] == '/') {
-                       modified = 1;
-                       p[l-1] = 0;
-               }
-       }
+       return t - name;
 }
 
 /* Make path appear as if a chroot had occurred.  This handles a leading
@@ -860,7 +869,7 @@ int push_dir(char *dir)
                curr_dir_len += len;
        }
 
-       clean_fname(curr_dir);
+       curr_dir_len = clean_fname(curr_dir);
 
        return 1;
 }