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.
void clean_fname(char *name)
{
void clean_fname(char *name)
{
- char *p;
- int l;
- int modified = 1;
+ char *limit = name, *t = name, *f = name;
+ int anchored;
- 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
}
/* Make path appear as if a chroot had occurred. This handles a leading