]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
s3: util: Replace the old (hard to understand) canonicalize_absolute_path() with...
authorJeremy Allison <jra@samba.org>
Tue, 21 Apr 2020 20:24:44 +0000 (13:24 -0700)
committerRalph Boehme <slow@samba.org>
Wed, 22 Apr 2020 08:15:35 +0000 (08:15 +0000)
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 <jra@samba.org>
Reviewed-by: Ralph Boehme <slow@samba.org>
source3/lib/util_path.c

index fed0cc34d1c3c648f397eb8b0a39e2ebba43a162..5053d91935e2105b9f509698ea75b0725dae78eb 100644 (file)
@@ -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;
+}