From: Michael Tremer Date: Fri, 6 Oct 2023 13:22:46 +0000 (+0000) Subject: path: Add a simple path library to normalize paths X-Git-Tag: 0.9.30~1537 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=397a600e0b47530d8a7a05789b3e71acd8e5ad69;p=pakfire.git path: Add a simple path library to normalize paths Signed-off-by: Michael Tremer --- diff --git a/.gitignore b/.gitignore index 11f11b3e6..b0c38bbaf 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,7 @@ /tests/libpakfire/package /tests/libpakfire/packager /tests/libpakfire/parser +/tests/libpakfire/path /tests/libpakfire/progressbar /tests/libpakfire/repo /tests/libpakfire/snapshot diff --git a/Makefile.am b/Makefile.am index 3205afa93..c16414320 100644 --- a/Makefile.am +++ b/Makefile.am @@ -235,6 +235,7 @@ libpakfire_la_SOURCES = \ src/libpakfire/packagelist.c \ src/libpakfire/pakfire.c \ src/libpakfire/parser.c \ + src/libpakfire/path.c \ src/libpakfire/problem.c \ src/libpakfire/progress.c \ src/libpakfire/pwd.c \ @@ -274,6 +275,7 @@ pkginclude_HEADERS += \ src/libpakfire/include/pakfire/packagelist.h \ src/libpakfire/include/pakfire/pakfire.h \ src/libpakfire/include/pakfire/parser.h \ + src/libpakfire/include/pakfire/path.h \ src/libpakfire/include/pakfire/private.h \ src/libpakfire/include/pakfire/problem.h \ src/libpakfire/include/pakfire/progress.h \ @@ -507,6 +509,7 @@ check_PROGRAMS += \ tests/libpakfire/package \ tests/libpakfire/packager \ tests/libpakfire/parser \ + tests/libpakfire/path \ tests/libpakfire/repo \ tests/libpakfire/snapshot \ tests/libpakfire/string \ @@ -728,6 +731,18 @@ tests_libpakfire_parser_CFLAGS = \ tests_libpakfire_parser_LDADD = \ $(TESTSUITE_LDADD) +dist_tests_libpakfire_path_SOURCES = \ + tests/libpakfire/path.c + +tests_libpakfire_path_CPPFLAGS = \ + $(TESTSUITE_CPPFLAGS) + +tests_libpakfire_path_CFLAGS = \ + $(TESTSUITE_CFLAGS) + +tests_libpakfire_path_LDADD = \ + $(TESTSUITE_LDADD) + dist_tests_libpakfire_repo_SOURCES = \ tests/libpakfire/repo.c diff --git a/src/libpakfire/include/pakfire/path.h b/src/libpakfire/include/pakfire/path.h new file mode 100644 index 000000000..4390becbe --- /dev/null +++ b/src/libpakfire/include/pakfire/path.h @@ -0,0 +1,30 @@ +/*############################################################################# +# # +# Pakfire - The IPFire package management system # +# Copyright (C) 2013 Pakfire development team # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see . # +# # +#############################################################################*/ + +#ifndef PAKFIRE_PATH_H +#define PAKFIRE_PATH_H + +#include + +#define pakfire_path_normalize(path) \ + __pakfire_path_normalize(path, sizeof(path)) +int __pakfire_path_normalize(char* p, const size_t length); + +#endif /* PAKFIRE_PATH_H */ diff --git a/src/libpakfire/path.c b/src/libpakfire/path.c new file mode 100644 index 000000000..2fa08cf28 --- /dev/null +++ b/src/libpakfire/path.c @@ -0,0 +1,246 @@ +/*############################################################################# +# # +# Pakfire - The IPFire package management system # +# Copyright (C) 2019 Pakfire development team # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see . # +# # +#############################################################################*/ + +#include +#include +#include +#include +#include +#include + +#include +#include + +struct pakfire_path { + int is_absolute; + + unsigned int num_segments; + char** segments; +}; + +static void pakfire_path_free(struct pakfire_path* path) { + if (path->segments) { + for (unsigned int i = 0; i < path->num_segments; i++) + free(path->segments[i]); + + free(path->segments); + } + + free(path); +} + +static int pakfire_path_append(struct pakfire_path* path, const char* segment) { + char* s = NULL; + + // Make space + path->segments = reallocarray(path->segments, sizeof(*path->segments), + path->num_segments + 2); + + // Fail if we could not make space + if (!path->segments) + return -errno; + + // Copy the segment to the heap + s = strdup(segment); + if (!s) + return -errno; + + // Store the segment in the array + path->segments[path->num_segments++] = s; + + return 0; +} + +static int pakfire_path_remove_segment(struct pakfire_path* path, unsigned int idx) { + if (idx >= path->num_segments) + return -EINVAL; + + // Free the value + if (path->segments[idx]) + free(path->segments[idx]); + + // Move the other segments + for (unsigned int i = idx + 1; i < path->num_segments; i++) + path->segments[i - 1] = path->segments[i]; + + // Decrement the number of segments + path->num_segments--; + + return 0; +} + +static int pakfire_path_import_segments(struct pakfire_path* path, const char* s) { + char buffer[PATH_MAX]; + char* p = NULL; + int r; + + // Is the path absolute? + if (*s == '/') + path->is_absolute = 1; + + // Copy path into buffer + r = pakfire_string_set(buffer, s); + if (r) + return r; + + const char* segment = strtok_r(buffer, "/", &p); + + // Append all segments + while (segment) { + r = pakfire_path_append(path, segment); + if (r) + break; + + segment = strtok_r(NULL, "/", &p); + } + + return r; +} + +static int pakfire_path_parse(struct pakfire_path** path, const char* s) { + struct pakfire_path* p = NULL; + int r; + + // Check input + if (!s || !*s) + return -EINVAL; + + // Allocate object + p = calloc(1, sizeof(*p)); + if (!p) + return -errno; + + // Import the path + r = pakfire_path_import_segments(p, s); + if (r) + goto ERROR; + + // Success + *path = p; + return 0; + +ERROR: + if (p) + pakfire_path_free(p); + + return r; +} + +static size_t pakfire_path_length(struct pakfire_path* path) { + size_t length = path->num_segments; + + for (unsigned int i = 0; i < path->num_segments; i++) + length += strlen(path->segments[i]); + + return length; +} + +static int pakfire_path_to_string(struct pakfire_path* path, char* buffer, const size_t length) { + char* p = buffer; + char* s = NULL; + + const size_t required_length = pakfire_path_length(path) + 1; + + // Check if we have enough space + if (length < required_length) + return -ENOBUFS; + + // Return / for empty paths + if (!path->num_segments) + return __pakfire_string_set(buffer, length, "/"); + + for (unsigned int i = 0; i < path->num_segments; i++) { + s = path->segments[i]; + + // Add separator + *p++ = '/'; + + // Add the segment + while (*s) + *p++ = *s++; + } + + // Terminate the string + *p = '\0'; + + return 0; +} + +static int pakfire_path_do_normalize(struct pakfire_path* path) { + int r; + + for (unsigned int i = 0; i < path->num_segments; i++) { + // Simply remove single dots + if (strcmp(path->segments[i], ".") == 0) { + r = pakfire_path_remove_segment(path, i); + if (r) + return r; + + // Go back one step + i--; + + // Remove double dots + } else if (strcmp(path->segments[i], "..") == 0) { + r = pakfire_path_remove_segment(path, i); + if (r) + return r; + + // Remove the previous element if there is one + if (i > 0) { + r = pakfire_path_remove_segment(path, i - 1); + if (r) + return r; + + i--; + } + + // Go back one step + i--; + } + } + + return 0; +} + +int __pakfire_path_normalize(char* p, const size_t length) { + struct pakfire_path* path = NULL; + int r; + + // Parse the path + r = pakfire_path_parse(&path, p); + if (r) + goto ERROR; + + // Normalize the path + r = pakfire_path_do_normalize(path); + if (r) + goto ERROR; + + // Write back the path + r = pakfire_path_to_string(path, p, length); + if (r) + goto ERROR; + +ERROR: + if (path) + pakfire_path_free(path); + + return r; +} diff --git a/tests/libpakfire/path.c b/tests/libpakfire/path.c new file mode 100644 index 000000000..4c9218d63 --- /dev/null +++ b/tests/libpakfire/path.c @@ -0,0 +1,65 @@ +/*############################################################################# +# # +# Pakfire - The IPFire package management system # +# Copyright (C) 2019 Pakfire development team # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see . # +# # +#############################################################################*/ + +#include + +#include +#include + +#include "../testsuite.h" + +static int test_path_normalize(const struct test* t) { + char path[PATH_MAX]; + + ASSERT_SUCCESS(pakfire_string_set(path, "/usr/bin/bash")); + ASSERT_SUCCESS(pakfire_path_normalize(path)); + ASSERT_STRING_EQUALS(path, "/usr/bin/bash"); + + ASSERT_SUCCESS(pakfire_string_set(path, "/")); + ASSERT_SUCCESS(pakfire_path_normalize(path)); + ASSERT_STRING_EQUALS(path, "/"); + + ASSERT_SUCCESS(pakfire_string_set(path, "/..")); + ASSERT_SUCCESS(pakfire_path_normalize(path)); + ASSERT_STRING_EQUALS(path, "/"); + + ASSERT_SUCCESS(pakfire_string_set(path, "/../..")); + ASSERT_SUCCESS(pakfire_path_normalize(path)); + ASSERT_STRING_EQUALS(path, "/"); + + ASSERT_SUCCESS(pakfire_string_set(path, "/usr/bin/bash")); + ASSERT_SUCCESS(pakfire_path_normalize(path)); + ASSERT_STRING_EQUALS(path, "/usr/bin/bash"); + + ASSERT_SUCCESS(pakfire_string_set(path, "/usr/bin/sh/../bash")); + ASSERT_SUCCESS(pakfire_path_normalize(path)); + ASSERT_STRING_EQUALS(path, "/usr/bin/bash"); + + return EXIT_SUCCESS; + +FAIL: + return EXIT_FAILURE; +} + +int main(int argc, const char* argv[]) { + testsuite_add_test(test_path_normalize); + + return testsuite_run(argc, argv); +}