diff options
author | John Dennis <jdennis@jdennis.(none)> | 2009-07-14 11:25:37 -0400 |
---|---|---|
committer | Stephen Gallagher <sgallagh@redhat.com> | 2009-07-15 08:56:51 -0400 |
commit | df8b16d7d55d97001ed71f629fc21511ec9f8d3b (patch) | |
tree | 601fc66ee7526f2035306e256265b5d9a0fab038 /common | |
parent | 7f5e857a11f932c247b5a96d0d86768226ba2e96 (diff) | |
download | sssd-df8b16d7d55d97001ed71f629fc21511ec9f8d3b.tar.gz sssd-df8b16d7d55d97001ed71f629fc21511ec9f8d3b.tar.bz2 sssd-df8b16d7d55d97001ed71f629fc21511ec9f8d3b.zip |
add path_utils filesystem path manipulation utility functions
remove trace macro, not needed
modifications to satisfy Stephen's checkin review
correct spelling of PATH_UTILS_ERROR_NOT_FULLY_NORMALIZED
add checks for truncation, return error
add checks for getcwd errors
modify (dst >= dst_end) test to be (dst > dst_end)
remove all use of tabs
remove all trailing whitespace
add missing truncation check after strncpy()
Fix path_utils.pc
Also correct a minor typo.
Diffstat (limited to 'common')
-rw-r--r-- | common/Makefile.am | 2 | ||||
-rw-r--r-- | common/configure.ac | 2 | ||||
-rw-r--r-- | common/path_utils/Makefile.am | 17 | ||||
-rw-r--r-- | common/path_utils/configure.ac | 18 | ||||
-rw-r--r-- | common/path_utils/m4/.dir | 0 | ||||
-rw-r--r-- | common/path_utils/path_utils.c | 535 | ||||
-rw-r--r-- | common/path_utils/path_utils.h | 257 | ||||
-rw-r--r-- | common/path_utils/path_utils.pc.in | 11 |
8 files changed, 840 insertions, 2 deletions
diff --git a/common/Makefile.am b/common/Makefile.am index 42bd483a..d3329c9b 100644 --- a/common/Makefile.am +++ b/common/Makefile.am @@ -1,5 +1,5 @@ ACLOCAL_AMFLAGS = -I m4 -SUBDIRS = trace collection ini dhash +SUBDIRS = trace collection ini dhash path_utils if SINGLELIB # Build all components as a single shared library lib_LTLIBRARIES = libsssd_util.la diff --git a/common/configure.ac b/common/configure.ac index 70d21717..89b3309e 100644 --- a/common/configure.ac +++ b/common/configure.ac @@ -21,7 +21,7 @@ AS_IF([test ["$trace_level" -gt "0"] -a ["$trace_level" -lt "8"] ],[AC_SUBST([TR AC_CONFIG_FILES([Makefile trace/Makefile]) -AC_CONFIG_SUBDIRS([collection dhash ini]) +AC_CONFIG_SUBDIRS([collection dhash ini path_utils]) AC_DEFUN([WITH_SINGLELIB], [ AC_ARG_WITH([singlelib], diff --git a/common/path_utils/Makefile.am b/common/path_utils/Makefile.am new file mode 100644 index 00000000..fdda791c --- /dev/null +++ b/common/path_utils/Makefile.am @@ -0,0 +1,17 @@ +AM_CFLAGS = +if HAVE_GCC + AM_CFLAGS += \ + -Wall -Wshadow -Wstrict-prototypes -Wpointer-arith -Wcast-qual \ + -Wcast-align -Wwrite-strings +endif + +ACLOCAL_AMFLAGS = -I m4 + +pkgconfigdir = $(libdir)/pkgconfig +dist_noinst_DATA = path_utils.pc + +noinst_LTLIBRARIES = libpath_utils.la +libpath_utils_la_SOURCES = \ + path_utils.c \ + path_utils.h + diff --git a/common/path_utils/configure.ac b/common/path_utils/configure.ac new file mode 100644 index 00000000..9207a075 --- /dev/null +++ b/common/path_utils/configure.ac @@ -0,0 +1,18 @@ +AC_INIT([path_utils], [0.1], [jdennis@redhat.com]) +AC_CONFIG_SRCDIR([path_utils.c]) +AC_CONFIG_AUX_DIR([build]) +AM_INIT_AUTOMAKE([-Wall -Werror foreign]) +AC_PROG_CC +AC_PROG_LIBTOOL +AC_CONFIG_MACRO_DIR([m4]) +AC_PROG_INSTALL + +AM_CONDITIONAL([HAVE_GCC], [test "$ac_cv_prog_gcc" = yes]) + +m4_pattern_allow([AM_SILENT_RULES]) +AM_SILENT_RULES + +AC_CONFIG_HEADERS([config.h]) + +AC_CONFIG_FILES([Makefile path_utils.pc]) +AC_OUTPUT diff --git a/common/path_utils/m4/.dir b/common/path_utils/m4/.dir new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/common/path_utils/m4/.dir diff --git a/common/path_utils/path_utils.c b/common/path_utils/path_utils.c new file mode 100644 index 00000000..b957392b --- /dev/null +++ b/common/path_utils/path_utils.c @@ -0,0 +1,535 @@ +/* gcc -O0 -g -o path-utils path-utils.c */ + +/*****************************************************************************/ +/******************************** Documentation ******************************/ +/*****************************************************************************/ + +/*****************************************************************************/ +/******************************* Include Files *******************************/ +/*****************************************************************************/ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <dirent.h> +#include <sys/errno.h> +#include <sys/stat.h> + +#include <libgen.h> + +#include "path_utils.h" + +/*****************************************************************************/ +/****************************** Internal Defines *****************************/ +/*****************************************************************************/ + +/*****************************************************************************/ +/************************** Internal Type Definitions ************************/ +/*****************************************************************************/ + +/*****************************************************************************/ +/********************** External Function Declarations *********************/ +/*****************************************************************************/ + +/*****************************************************************************/ +/********************** Internal Function Declarations *********************/ +/*****************************************************************************/ + +/*****************************************************************************/ +/************************* External Global Variables ***********************/ +/*****************************************************************************/ + +/*****************************************************************************/ +/************************* Internal Global Variables ***********************/ +/*****************************************************************************/ + +/*****************************************************************************/ +/**************************** Inline Functions *****************************/ +/*****************************************************************************/ + +/*****************************************************************************/ +/*************************** Internal Functions ****************************/ +/*****************************************************************************/ + +/*****************************************************************************/ +/**************************** Exported Functions ***************************/ +/*****************************************************************************/ + +const char *path_utils_error_string(int error) +{ + switch(error) { + case SUCCESS: return _("Success"); + case PATH_UTILS_ERROR_NOT_FULLY_NORMALIZED: return _("Path could not be fully normalized"); + } + return NULL; +} + +int get_basename(char *base_name, size_t base_name_size, const char *path) +{ + char tmp_path[PATH_MAX]; + + if (!base_name || base_name_size < 1) return ENOBUFS; + + strncpy(tmp_path, path, sizeof(tmp_path)); + if (tmp_path[sizeof(tmp_path)-1] != 0) return ENOBUFS; + strncpy(base_name, basename(tmp_path), base_name_size); + if (base_name[base_name_size-1] != 0) return ENOBUFS; + return SUCCESS; +} + +int get_dirname(char *dir_path, size_t dir_path_size, const char *path) +{ + char tmp_path[PATH_MAX]; + + if (!dir_path || dir_path_size < 1) return ENOBUFS; + + strncpy(tmp_path, path, sizeof(tmp_path)); + if (tmp_path[sizeof(tmp_path)-1] != 0) return ENOBUFS; + strncpy(dir_path, dirname(tmp_path), dir_path_size); + if (dir_path[dir_path_size-1] != 0) return ENOBUFS; + if (strcmp(dir_path, ".") == 0) { + if (getcwd(dir_path, dir_path_size) == NULL) { + if (errno == ERANGE) + return ENOBUFS; + else + return errno; + } + } else if (strcmp(dir_path, "..") == 0) { + if (getcwd(tmp_path, sizeof(tmp_path)) == NULL) { + if (errno == ERANGE) + return ENOBUFS; + else + return errno; + } + strncpy(dir_path, dirname(tmp_path), dir_path_size); + if (dir_path[dir_path_size-1] != 0) return ENOBUFS; + } + return SUCCESS; +} + +int get_directory_and_base_name(char *dir_path, size_t dir_path_size, char *base_name, size_t base_name_size, const char *path) +{ + char tmp_path[PATH_MAX]; + + if (!dir_path || dir_path_size < 1) return ENOBUFS; + if (!base_name || base_name_size < 1) return ENOBUFS; + + strncpy(tmp_path, path, sizeof(tmp_path)); + if (tmp_path[sizeof(tmp_path)-1] != 0) return ENOBUFS; + strncpy(base_name, basename(tmp_path), base_name_size); + if (base_name[base_name_size-1] != 0) return ENOBUFS; + + strncpy(tmp_path, path, sizeof(tmp_path)); + if (tmp_path[sizeof(tmp_path)-1] != 0) return ENOBUFS; + strncpy(dir_path, dirname(tmp_path), dir_path_size); + if (dir_path[dir_path_size-1] != 0) return ENOBUFS; + + if (strcmp(dir_path, ".") == 0) { + if (getcwd(dir_path, dir_path_size) == NULL) { + if (errno == ERANGE) + return ENOBUFS; + else + return errno; + } + } else if (strcmp(dir_path, "..") == 0) { + if (getcwd(tmp_path, sizeof(tmp_path)) == NULL) { + if (errno == ERANGE) + return ENOBUFS; + else + return errno; + } + strncpy(dir_path, dirname(tmp_path), dir_path_size); + if (dir_path[dir_path_size-1] != 0) return ENOBUFS; + } + return SUCCESS; +} + +bool is_absolute_path(const char *path) +{ + if (!path) return false; + return path[0] == '/'; +} + +int path_concat(char *path, size_t path_size, const char *head, const char *tail) +{ + const char *p, *src; + char *dst, *dst_end; + + if (!path || path_size < 1) return ENOBUFS; + + dst = path; + dst_end = path + path_size - 1; /* -1 allows for NULL terminator */ + + if (head && *head) { + for (p = head; *p; p++); /* walk to end of head */ + for (p--; p >= head && *p == '/'; p--); /* skip any trailing slashes in head */ + for (src = head; src <= p && dst < dst_end;) *dst++ = *src++; /* copy head */ + } + if (tail && *tail) { + for (p = tail; *p && *p == '/'; p++); /* skip any leading slashes in tail */ + if (dst > path) + if (dst < dst_end) *dst++ = '/'; /* insert single slash between head & tail */ + for (src = p; *src && dst < dst_end;) *dst++ = *src++; /* copy tail */ + } + *dst = 0; + if (dst > dst_end) { + return ENOBUFS; + } + return SUCCESS; + +} + +int make_path_absolute(char *absolute_path, size_t absolute_path_size, const char *path) +{ + int result = SUCCESS; + const char *src; + char *dst, *dst_end; + + if (!absolute_path || absolute_path_size < 1) return ENOBUFS; + + dst = absolute_path; + dst_end = absolute_path + absolute_path_size - 1; /* -1 allows for NULL terminator */ + + if (is_absolute_path(path)) { + for (src = path; *src && dst < dst_end;) *dst++ = *src++; + *dst = 0; + if (dst > dst_end) result = ENOBUFS; + return result; + } + + if ((getcwd(absolute_path, absolute_path_size) == NULL)) { + if (errno == ERANGE) + return ENOBUFS; + else + return errno; + } + for (dst = absolute_path; *dst && dst < dst_end; dst++); + if (dst > dst_end) { + *dst = 0; + result = ENOBUFS; + return result; + } + if (!(path && *path)) return result; + *dst++ = '/'; + if (dst > dst_end) { + *dst = 0; + result = ENOBUFS; + return result; + } + for (src = path; *src && dst < dst_end;) *dst++ = *src++; + *dst = 0; + if (dst > dst_end) { + result = ENOBUFS; + } + return result; +} + +char **split_path(const char *path, int *count) +{ + int n_components, component_len, total_component_len, alloc_len; + const char *start, *end; + char *mem_block, **array_ptr, *component_ptr; + + /* If path is absolute add in special "/" root component */ + if (*path == '/') { + n_components = 1; + total_component_len = 2; + } else { + n_components = 0; + total_component_len = 0; + } + + /* Scan for components, keep several counts */ + for (start = end = path; *start; start = end) { + for (start = end; *start && *start == '/'; start++); + for (end = start; *end && *end != '/'; end++); + if ((component_len = end - start) == 0) break; + n_components++; + total_component_len += component_len + 1; + } + + /* + * Allocate a block big enough for component array (with trailing NULL + * entry, hence n_components+1) and enough room for a copy of each NULL + * terminated component. We'll copy the components into the same allocation + * block after the end of the pointer array. + */ + alloc_len = ((n_components+1) * sizeof(char *)) + total_component_len; + + if ((mem_block = malloc(alloc_len)) == NULL) { + if (count) *count = -1; + return NULL; + } + + /* component array */ + array_ptr = (char **)mem_block; + /* components copied after end of array */ + component_ptr = mem_block + ((n_components+1)*sizeof(char *)); + + /* If path is absolute add in special "/" root component */ + if (*path == '/') { + *array_ptr++ = component_ptr; + *component_ptr++ = '/'; + *component_ptr++ = 0; + } + + for (start = end = path; *start; start = end) { + for (start = end; *start && *start == '/'; start++); + for (end = start; *end && *end != '/'; end++); + if ((component_len = end - start) == 0) break; + + *array_ptr++ = component_ptr; + while (start < end) *component_ptr++ = *start++; + *component_ptr++ = 0; + } + *array_ptr++ = NULL; + if (count) *count = n_components; + return (char **)mem_block; +} + +int normalize_path(char *normalized_path, size_t normalized_path_size, const char *path) +{ + int result = SUCCESS; + int component_len; + bool is_absolute, can_backup; + const char *start, *end; + char *dst, *dst_end, *p, *limit; + + if (!normalized_path || normalized_path_size < 1) return ENOBUFS; + + dst = normalized_path; + dst_end = normalized_path + normalized_path_size - 1; /* -1 allows for NULL terminator */ + can_backup = true; + + if (!path || !*path) { + if (dst > dst_end) { + *dst = 0; + return ENOBUFS; + } + *dst++ = '.'; + *dst = 0; + return result; + } + + if ((is_absolute = *path == '/')) { + if (dst < dst_end) { + *dst++ = '/'; + } else { + *dst = 0; + return ENOBUFS; + } + } + + for (start = end = path; *start; start = end) { + for (start = end; *start && *start == '/'; start++); + for (end = start; *end && *end != '/'; end++); + if ((component_len = end - start) == 0) break; + if (component_len == 1 && start[0] == '.') continue; + if (component_len == 2 && start[0] == '.' && start[1] == '.' && can_backup) { + /* back up one level */ + if ((is_absolute && dst == normalized_path+1) || (!is_absolute && dst == normalized_path)) { + if (is_absolute) continue; + can_backup = false; + result = PATH_UTILS_ERROR_NOT_FULLY_NORMALIZED; + } else { + if (is_absolute) + limit = normalized_path+1; + else + limit = normalized_path; + for (p = dst-1; p >= limit && *p != '/'; p--); + if (p < limit) + dst = limit; + else + dst = p; + continue; + } + } + + if ((dst > normalized_path) && (dst < dst_end) && (dst[-1] != '/')) *dst++ = '/'; + while ((start < end) && (dst < dst_end)) *dst++ = *start++; + if (dst > dst_end) { + *dst = 0; + return ENOBUFS; + } + } + + if (dst == normalized_path) { + if (is_absolute) + *dst++ = '/'; + else + *dst++ = '.'; + } + *dst = 0; + return result; +} + +int common_path_prefix(char *common_path, size_t common_path_size, int *common_count, const char *path1, const char *path2) +{ + int count1, count2, min_count, i, n_common, result; + char **split1, **split2; + char *dst, *dst_end, *src; + + if (!common_path || common_path_size < 1) return ENOBUFS; + + result = SUCCESS; + n_common = 0; + split1 = split_path(path1, &count1); + split2 = split_path(path2, &count2); + + if (count1 <= count2) + min_count = count1; + else + min_count = count2; + + if (min_count <= 0) { + result = 0; + *common_path = 0; + goto done; + } + + for (n_common = 0; n_common < min_count; n_common++) { + if (strcmp(split1[n_common], split2[n_common]) != 0) break; + } + + if (n_common < 0) { + result = 0; + *common_path = 0; + goto done; + } + + dst = common_path; + dst_end = common_path + common_path_size - 1; /* -1 allows for NULL terminator */ + for (i = 0; i < n_common; i++) { + for (src = split1[i]; *src && dst < dst_end;) *dst++ = *src++; + if (dst == dst_end && *src) { + *dst = 0; + result = ENOBUFS; + goto done; + } + if (dst[-1] != '/' && i < n_common-1) { /* insert path separator */ + if (dst == dst_end) { + *dst = 0; + result = ENOBUFS; + goto done; + } + *dst++ = '/'; + } + } + *dst = 0; + + done: + free(split1); + free(split2); + if (common_count) *common_count = n_common; + return result; +} + +int make_normalized_absolute_path(char *result_path, size_t result_path_size, const char *path) +{ + int error; + char absolute_path[PATH_MAX]; + + if (!result_path || result_path_size < 1) return ENOBUFS; + *result_path = 0; + if ((error = make_path_absolute(absolute_path, sizeof(absolute_path), path)) != SUCCESS) return error; + if ((error = normalize_path(result_path, result_path_size, absolute_path)) != SUCCESS) return error; + return SUCCESS; +} + +int find_existing_directory_ancestor(char *ancestor, size_t ancestor_size, const char *path) +{ + int error; + char base_name[PATH_MAX]; + char dir_path[PATH_MAX]; + struct stat info; + + if (!ancestor || ancestor_size < 1) return ENOBUFS; + *ancestor = 0; + strncpy(dir_path, path, sizeof(dir_path)); + if (dir_path[sizeof(dir_path)-1] != 0) return ENOBUFS; + + while (strcmp(dir_path, "/") != 0) { + if (lstat(dir_path, &info) < 0) { + error = errno; + if (error != ENOENT) return error; + } else { + if (S_ISDIR(info.st_mode)) break; + } + get_directory_and_base_name(dir_path, sizeof(dir_path), base_name, sizeof(base_name), dir_path); + } + + strncpy(ancestor, dir_path, ancestor_size); + if (ancestor[ancestor_size-1] != 0) return ENOBUFS; + return SUCCESS; +} + +int directory_list(const char *path, bool recursive, directory_list_callback_t callback, void *user_data) +{ + DIR * dir; + struct dirent *entry; + struct stat info; + int error = 0; + char entry_path[PATH_MAX]; + bool prune = false; + + if (!(dir = opendir(path))) { + error = errno; + return error; + } + + for (entry = readdir(dir); entry; entry = readdir(dir)) { + prune = false; + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue; + + path_concat(entry_path, sizeof(entry_path), path, entry->d_name); + + if (lstat(entry_path, &info) < 0) { + continue; + } + + prune = !callback(path, entry->d_name, entry_path, &info, user_data); + if (S_ISDIR(info.st_mode)) { + if (recursive && !prune) directory_list(entry_path, recursive, callback, user_data); + + } + } + closedir(dir); + return SUCCESS; +} + +bool is_ancestor_path(const char *ancestor, const char *path) +{ + char **path_components, **ancestor_components; + int i, path_count, ancestor_count; + bool result; + + result = false; + path_components = split_path(path, &path_count); + ancestor_components = split_path(ancestor, &ancestor_count); + + if (!path_components || !ancestor_components) { + result = false; + goto exit; + } + + if (ancestor_count >= path_count) { + result = false; + goto exit; + } + + for (i = 0; i < ancestor_count; i++) { + if (strcmp(path_components[i], ancestor_components[i]) != 0) { + result = false; + goto exit; + } + } + + result = true; + + exit: + free(path_components); + free(ancestor_components); + return result; +} + diff --git a/common/path_utils/path_utils.h b/common/path_utils/path_utils.h new file mode 100644 index 00000000..9c1598ef --- /dev/null +++ b/common/path_utils/path_utils.h @@ -0,0 +1,257 @@ +#ifndef PATH_UTILS_H +#define PATH_UTILS_H + +/*****************************************************************************/ +/******************************** Documentation ******************************/ +/*****************************************************************************/ + +/*****************************************************************************/ +/******************************* Include Files *******************************/ +/*****************************************************************************/ + +#include <stdbool.h> +#include <libintl.h> +#include <sys/param.h> +#include <sys/stat.h> + +/*****************************************************************************/ +/*********************************** Defines *********************************/ +/*****************************************************************************/ + +#ifndef _ +#define _(String) gettext(String) +#endif + +#ifndef SUCCESS +#define SUCCESS 0 +#endif + +#define PATH_UTILS_ERROR_BASE -3000 +#define PATH_UTILS_ERROR_LIMIT (PATH_UTILS_ERROR_BASE+20) +#define IS_PATH_UTILS_ERROR(error) (((error) >= PATH_UTILS_ERROR_BASE) && ((error) < PATH_UTILS_ERROR_LIMIT)) + +#define PATH_UTILS_ERROR_NOT_FULLY_NORMALIZED (PATH_UTILS_ERROR_BASE + 1) + +/*****************************************************************************/ +/******************************* Type Definitions ****************************/ +/*****************************************************************************/ + +/*****************************************************************************/ +/************************* External Global Variables ***********************/ +/*****************************************************************************/ + +/*****************************************************************************/ +/**************************** Exported Functions ***************************/ +/*****************************************************************************/ + +/** + * Given an error code return the string description. + * If error code is not recognized NULL is returned. + */ +const char *path_utils_error_string(int error); + +/** + * Given a path, copy the basename component into the buffer base_name whose + * length is base_name_size. + * + * Returns SUCCESS (0) if successful, non-zero error code otherwise. Possible errors: + * ENOBUFS if the buffer space is too small + */ +int get_basename(char *base_name, size_t base_name_size, const char *path); + +/** + * Given a path, copy the directory components into the buffer dir_path whose + * length is dir_path_size. + * + * Returns SUCCESS (0) if successful, non-zero error code otherwise. Possible errors: + * ENOBUFS If the buffer space is too small + * EACCES Permission to read or search a component of the filename was denied. + * ENAMETOOLONG The size of the null-terminated pathname exceeds PATH_MAX bytes. + * ENOENT The current working directory has been unlinked. + */ +int get_dirname(char *dir_path, size_t dir_path_size, const char *path); + +/** + * Given a path, copy the directory components into the buffer dir_path whose + * length is dir_path_size and copy the basename component into the buffer + * base_name whose length is base_name_size. + * + * Returns SUCCESS (0) if successful, non-zero error code otherwise. Possible errors: + * ENOBUFS If the buffer space is too small + * EACCES Permission to read or search a component of the filename was denied. + * ENAMETOOLONG The size of the null-terminated pathname exceeds PATH_MAX bytes. + * ENOENT The current working directory has been unlinked. + */ +int get_directory_and_base_name(char *dir_path, size_t dir_path_size, char *base_name, size_t base_name_size, const char *path); + +/** + * Return true if the path is absolute, false otherwise. + */ +bool is_absolute_path(const char *path); + +/** + * Given two paths, head & tail, copy their concatenation into the buffer path + * whose length is path_size. + * + * Returns SUCCESS (0) if successful, non-zero error code otherwise. Possible errors: + * ENOBUFS If the buffer space is too small + */ +int path_concat(char *path, size_t path_size, const char *head, const char *tail); + +/** + * Given a path make it absolute storing the absolute path in into the buffer + * absolute_path whose length is absolute_path_size. + * + * Returns SUCCESS (0) if successful, non-zero error code otherwise. Possible errors: + * ENOBUFS If the buffer space is too small + * ENOMEM If user memory cannot be mapped + * ENOENT If directory does not exist (i.e. it has been deleted) + * EFAULT If memory access violation occurs while copying + */ +int make_path_absolute(char *absolute_path, size_t absolute_path_size, const char *path); + +/** + * Split a file system path into individual components. Return a pointer to an + * array of char pointers, each array entry is a pointer to a copy of the + * component. As a special case if the path begins with / then the first + * component is "/" so the caller can identify the pah as absolute with the + * first component being the root. The last entry in the array is NULL serving + * as a termination sentinel. An optional integer count parameter can be + * provided, which if non-NULL will have the number of components written into + * it. Thus the caller can iterate on the array until it sees a NULL pointer or + * iterate count times indexing the array. + * + * The caller is responsible for calling free() on the returned array. This + * frees both the array of component pointers and the copies of each component + * in one operation because the copy of each component is stored in the same + * allocation block. + * + * The original path parameter is not modified. + * + * In the event of an error NULL is returned and count (if specified) will be -1. + * + * Examples: + * + * char **components, **component; + * int i; + * + * components = split_path(path, NULL); + * for (component = components; *component; component++) + * printf("\"%s\" ", *component); + * free(components); + * + * -OR- + * + * components = split_path(path, &count); + * for (i = 0; i < count; i++) + * printf("\"%s\" ", components[i]); + * free(components); + * + */ +char **split_path(const char *path, int *count); + +/** + * Normalizes a path copying the resulting normalized path into the buffer + * normalized_path whose length is normalized_size. + * + * A path is normalized when: + * only 1 slash separates all path components + * there are no . path components (except if . is the only component) + * there are no .. path components + * + * The input path may either be an absolute path or a path fragment. + * + * As a special case if the input path is NULL, the empty string "", or "." the + * returned normalized path will be ".". + * + * .. path components point to the parent directory which effectively means + * poping the parent off the path. But what happens when there are more .. path + * components than ancestors in the path? The answer depends on whether the path + * is an absolute path or a path fragment. If the path is absolute then the + * extra .. components which would move above the root (/) are simply + * ignored. This effectively limits the path to the root. However if the path is + * not absolute, rather it is a path fragment, and there are more .. components + * than ancestors which can be "popped off" then as many .. components will be + * popped off the fragement as possible without changing the meaning of the path + * fragment. In this case some extra .. components will be left in the path and + * the function will return the error + * ERROR_COULD_NOT_NORMALIZE_PATH_FULLY. However the function will still + * normalize as much of the path fragment as is possible. The behavior of + * .. components when the input path is a fragment is adopted because after + * normalizing a path fragment then the normalized path fragment if made + * absolute should reference the same file system name as if the unnormalized + * fragment were made absolute. Note this also means + * ERROR_COULD_NOT_NORMALIZE_PATH_FULLY will never be returned if the input path + * is absolute. + * + * Returns SUCCESS (0) if successful, non-zero error code otherwise. Possible errors: + * ENOBUFS If the buffer space is too small + * ERROR_COULD_NOT_NORMALIZE_PATH_FULLY If not all .. path components could be removed + */ +int normalize_path(char *normalized_path, size_t normalized_path_size, const char *path); + +/** + * Finds the common prefix between two paths, returns the common prefix and + * optionally the count of how many path components were common between the two + * paths (if common_count is non-NULL). + * + * Returns SUCCESS (0) if successful, non-zero error code otherwise. Possible errors: + * ENOBUFS if the buffer space is too small + */ +int common_path_prefix(char *common_path, size_t common_path_size, int *common_count, const char *path1, const char *path2); + + +/** + * Make the input path absolute if it's not already, then normalize it. + * + * Returns SUCCESS (0) if successful, non-zero error code otherwise. Possible errors: + * ENOBUFS if the buffer space is too small + */ +int make_normalized_absolute_path(char *result_path, size_t result_path_size, const char *path); + +/** + * Find the first path component which is an existing directory by walking from + * the tail of the path to it's head, return the path of the existing directory. + * + * Returns SUCCESS (0) if successful, non-zero error code otherwise. Possible errors: + * ENOBUFS if the buffer space is too small + * EACCES Search permission is denied for one of the directories. + * ELOOP Too many symbolic links encountered while traversing the path. + * ENAMETOOLONG File name too long. + * ENOMEM Out of memory (i.e., kernel memory). + */ +int find_existing_directory_ancestor(char *ancestor, size_t ancestor_size, const char *path); + +/** + * Walk a directory. If recursive is true child directories will be descended + * into. The supplied callback is invoked for each entry in the directory. + * + * The callback is provided with the directory name, basename the full pathname + * (i.e. directory name + basename) a stat sturcture for the path item and a + * pointer to any user supplied data specified in the user_data parameter. If + * the callback returns false for a directory the recursive descent into that + * directory does not occur thus effectively "pruning" the tree. + */ +typedef bool (*directory_list_callback_t)(const char *directory, const char *basename, const char *path, + struct stat *info, void *user_data); +int directory_list(const char *path, bool recursive, directory_list_callback_t callback, void *user_data); + +/** + * Test to see if the path passed in the ancestor parameter is an ancestor of + * the path passed in the path parameter returning true if it is, false otherwise. + * + * The test "static" as such it is performed on the string components in each + * path. Live symbolic links in the file system are not taken into + * consideration. The test operates by splitting each path into it's individual + * components and then comparing each component pairwise for string + * equality. Both paths mush share a common root component for the test to be + * meaningful (e.g. don't attempt to compare an absolute path with a relative + * path). + * + * Example: + * is_ancestor_path("/a/b/c" "/a/b/c/d") => true + * is_ancestor_path("/a/b/c/d" "/a/b/c/d") => false // equal, not ancestor + * is_ancestor_path("/a/x/c" "/a/b/c/d") => false + */ +bool is_ancestor_path(const char *ancestor, const char *path); +#endif diff --git a/common/path_utils/path_utils.pc.in b/common/path_utils/path_utils.pc.in new file mode 100644 index 00000000..aaf7531b --- /dev/null +++ b/common/path_utils/path_utils.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: @PACKAGE_NAME@ +Description: Utility functions to manipulate filesystem pathnames +Version: @PACKAGE_VERSION@ +Libs: -L${libdir} -lpath_utils +Cflags: -I${includedir} +URL: http://fedorahosted.org/sssd/ |