summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source3/lib/util_path.c133
-rw-r--r--source3/lib/util_path.h1
2 files changed, 134 insertions, 0 deletions
diff --git a/source3/lib/util_path.c b/source3/lib/util_path.c
index 509ba5ff727..cbad2e15d48 100644
--- a/source3/lib/util_path.c
+++ b/source3/lib/util_path.c
@@ -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;
+}
diff --git a/source3/lib/util_path.h b/source3/lib/util_path.h
index 118a4bed524..16e27926084 100644
--- a/source3/lib/util_path.h
+++ b/source3/lib/util_path.h
@@ -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