An improved clean_fname() routine that is more efficient and will also
authorWayne Davison <wayned@samba.org>
Fri, 6 Aug 2004 21:24:14 +0000 (21:24 +0000)
committerWayne Davison <wayned@samba.org>
Fri, 6 Aug 2004 21:24:14 +0000 (21:24 +0000)
collapse ".." dirs that aren't at the start of the path.  Care was taken
to ensure that the cleaning of a name that goes over the socket is done
in the same way as the old code (because both sides call clean_fname()
on those file-list names).  This ensures compatibility with older rsync
versions.

util.c

diff --git a/util.c b/util.c
index 43613e7..96b7ac4 100644 (file)
--- a/util.c
+++ b/util.c
@@ -652,45 +652,52 @@ size_t stringjoin(char *dest, size_t destsize, ...)
 
 void clean_fname(char *name)
 {
-       char *p;
-       int l;
-       int modified = 1;
+       char *limit = name, *t = name, *f = name;
+       int anchored;
 
        if (!name)
                return;
 
-       while (modified) {
-               modified = 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++; /* not += 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 == '/') {
+                                       t = s + 1;
+                                       f += 2;
+                                       continue;
+                               }
+                               *t++ = *f++;
+                               *t++ = *f++;
+                               limit = t;
                        }
                }
-
-               if (strncmp(p = name, "./", 2) == 0) {
-                       modified = 1;
-                       do {
-                               p[0] = p[2];
-                       } while (*p++);
-               }
-
-               l = strlen(p = name);
-               if (l > 1 && p[l-1] == '/') {
-                       modified = 1;
-                       p[l-1] = 0;
-               }
+               while (*f && (*t++ = *f++) != '/') {}
        }
+
+       if (t > name+anchored && t[-1] == '/')
+               t--;
+       if (t == name)
+               *t++ = '.';
+       *t = '\0';
 }
 
 /* Make path appear as if a chroot had occurred.  This handles a leading