From: Jeremy Allison Date: Tue, 21 Apr 2020 20:24:44 +0000 (-0700) Subject: s3: util: Replace the old (hard to understand) canonicalize_absolute_path() with... X-Git-Tag: ldb-2.2.0~894 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=594c3712c885447c096df9892da8f6f28f1ce8d9;p=thirdparty%2Fsamba.git s3: util: Replace the old (hard to understand) canonicalize_absolute_path() with a version created from resolve_realpath_name() in vfs_widelinks.c This code is *much* more comprehensible and passes the stricter test set than the original (unfixed) canonicalize_absolute_path() out of the gate. Signed-off-by: Jeremy Allison Reviewed-by: Ralph Boehme --- diff --git a/source3/lib/util_path.c b/source3/lib/util_path.c index fed0cc34d1c..5053d91935e 100644 --- a/source3/lib/util_path.c +++ b/source3/lib/util_path.c @@ -107,6 +107,7 @@ char *cache_path(TALLOC_CTX *mem_ctx, const char *name) * @retval Pointer to a talloc'ed string containing the absolute full path. **/ +#if 0 char *canonicalize_absolute_path(TALLOC_CTX *ctx, const char *abs_path) { char *destname; @@ -251,3 +252,115 @@ char *canonicalize_absolute_path(TALLOC_CTX *ctx, const char *abs_path) return destname; } +#endif + +/* + * Canonicalizes an absolute path, removing '.' and ".." components + * and consolitating multiple '/' characters. As this can only be + * called once widelinks_chdir with an absolute path has been called, + * we can assert that the start of path must be '/'. + */ + +char *canonicalize_absolute_path(TALLOC_CTX *ctx, const char *pathname_in) +{ + /* + * Note we use +2 here so if pathname_in=="" then we + * have space to return "/". + */ + char *pathname = talloc_array(ctx, char, strlen(pathname_in)+2); + const char *s = pathname_in; + char *p = pathname; + bool wrote_slash = false; + + if (pathname == NULL) { + return NULL; + } + + /* Always start with a '/'. */ + *p++ = '/'; + wrote_slash = true; + + while (*s) { + /* Deal with '/' or multiples of '/'. */ + if (s[0] == '/') { + while (s[0] == '/') { + /* Eat trailing '/' */ + s++; + } + /* Update target with one '/' */ + if (!wrote_slash) { + *p++ = '/'; + wrote_slash = true; + } + continue; + } + if (wrote_slash) { + /* Deal with "./" or ".\0" */ + if (s[0] == '.' && + (s[1] == '/' || s[1] == '\0')) { + /* Eat the dot. */ + s++; + while (s[0] == '/') { + /* Eat any trailing '/' */ + s++; + } + /* Don't write anything to target. */ + /* wrote_slash is still true. */ + continue; + } + /* Deal with "../" or "..\0" */ + if (s[0] == '.' && s[1] == '.' && + (s[2] == '/' || s[2] == '\0')) { + /* Eat the dot dot. */ + s += 2; + while (s[0] == '/') { + /* Eat any trailing '/' */ + s++; + } + /* + * As wrote_slash is true, we go back + * one character to point p at the slash + * we just saw. + */ + if (p > pathname) { + p--; + } + /* + * Now go back to the slash + * before the one that p currently points to. + */ + while (p > pathname) { + p--; + if (p[0] == '/') { + break; + } + } + /* + * Step forward one to leave the + * last written '/' alone. + */ + p++; + + /* Don't write anything to target. */ + /* wrote_slash is still true. */ + continue; + } + } + /* Non-separator character, just copy. */ + *p++ = *s++; + wrote_slash = false; + } + if (wrote_slash) { + /* + * We finished on a '/'. + * Remove the trailing '/', but not if it's + * the sole character in the path. + */ + if (p > pathname + 1) { + p--; + } + } + /* Terminate and we're done ! */ + *p++ = '\0'; + return pathname; +}