s3: lib: Add canonicalize_absolute_path().
authorJeremy Allison <jra@samba.org>
Tue, 17 Jan 2017 19:33:18 +0000 (11:33 -0800)
committerJeremy Allison <jra@samba.org>
Mon, 30 Jan 2017 17:39:18 +0000 (18:39 +0100)
Resolves any invalid path components (.) (..)
in an absolute POSIX path.

We will be re-using this in several places.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12531

Signed-off-by: Jeremy Allison <jra@samba.org>
Reviewed-by: Uri Simchoni <uri@samba.org>
source3/lib/util_path.c
source3/lib/util_path.h

index 509ba5ff7271628be5c36582efb505dcac4a2f6e..cbad2e15d48ad925923fc6ff041b4a2d8552d790 100644 (file)
@@ -93,3 +93,136 @@ char *cache_path(const char *name)
 {
        return xx_path(name, lp_cache_directory());
 }
+
+/**
+ * @brief Removes any invalid path components in an absolute POSIX path.
+ *
+ * @param ctx Talloc context to return string.
+ *
+ * @param abs_path Absolute path string to process.
+ *
+ * @retval Pointer to a talloc'ed string containing the absolute full path.
+ **/
+
+char *canonicalize_absolute_path(TALLOC_CTX *ctx, const char *abs_path)
+{
+       char *destname;
+       char *d;
+       const char *s = abs_path;
+       bool start_of_name_component = true;
+
+       /* Allocate for strlen + '\0' + possible leading '/' */
+       destname = (char *)talloc_size(ctx, strlen(abs_path) + 2);
+       if (destname == NULL) {
+               return NULL;
+        }
+       d = destname;
+
+       *d++ = '/'; /* Always start with root. */
+
+       while (*s) {
+               if (*s == '/') {
+                       /* Eat multiple '/' */
+                       while (*s == '/') {
+                               s++;
+                       }
+                       if ((d > destname + 1) && (*s != '\0')) {
+                               *d++ = '/';
+                       }
+                       start_of_name_component = true;
+                       continue;
+               }
+
+               if (start_of_name_component) {
+                       if ((s[0] == '.') && (s[1] == '.') &&
+                                       (s[2] == '/' || s[2] == '\0')) {
+                               /* Uh oh - "/../" or "/..\0" ! */
+
+                               /* Go past the ../ or .. */
+                               if (s[2] == '/') {
+                                       s += 3;
+                               } else {
+                                       s += 2; /* Go past the .. */
+                               }
+
+                               /* If  we just added a '/' - delete it */
+                               if ((d > destname) && (*(d-1) == '/')) {
+                                       *(d-1) = '\0';
+                                       d--;
+                               }
+
+                               /*
+                                * Are we at the start ?
+                                * Can't go back further if so.
+                                */
+                               if (d <= destname) {
+                                       *d++ = '/'; /* Can't delete root */
+                                       continue;
+                               }
+                               /* Go back one level... */
+                               /*
+                                * Decrement d first as d points to
+                                * the *next* char to write into.
+                                */
+                               for (d--; d > destname; d--) {
+                                       if (*d == '/') {
+                                               break;
+                                       }
+                               }
+                               /*
+                                * We're still at the start of a name
+                                * component, just the previous one.
+                                */
+                               continue;
+                       } else if ((s[0] == '.') &&
+                                       ((s[1] == '\0') || s[1] == '/')) {
+                               /*
+                                * Component of pathname can't be "." only.
+                                * Skip the '.' .
+                                */
+                               if (s[1] == '/') {
+                                       s += 2;
+                               } else {
+                                       s++;
+                               }
+                               continue;
+                       }
+               }
+
+               if (!(*s & 0x80)) {
+                       *d++ = *s++;
+               } else {
+                       size_t siz;
+                       /* Get the size of the next MB character. */
+                       next_codepoint(s,&siz);
+                       switch(siz) {
+                               case 5:
+                                       *d++ = *s++;
+                                       /*fall through*/
+                               case 4:
+                                       *d++ = *s++;
+                                       /*fall through*/
+                               case 3:
+                                       *d++ = *s++;
+                                       /*fall through*/
+                               case 2:
+                                       *d++ = *s++;
+                                       /*fall through*/
+                               case 1:
+                                       *d++ = *s++;
+                                       break;
+                               default:
+                                       break;
+                       }
+               }
+               start_of_name_component = false;
+       }
+       *d = '\0';
+
+       /* And must not end in '/' */
+       if (d > destname + 1 && (*(d-1) == '/')) {
+               *(d-1) = '\0';
+       }
+
+       return destname;
+}
index 118a4bed5247e35ae714fd15eb25ffc28b6d28e5..16e279260842d0e648cdd8725e0393cefa5de02c 100644 (file)
@@ -27,5 +27,6 @@
 char *lock_path(const char *name);
 char *state_path(const char *name);
 char *cache_path(const char *name);
+char *canonicalize_absolute_path(TALLOC_CTX *ctx, const char *abs_path);
 
 #endif