From 986e4abce4efeccd9418eb8cdc7a710487f093b9 Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Tue, 5 Nov 2013 21:37:28 +0100 Subject: [PATCH] Add new API find_path[s](), find_path_for_path() The new functions are meant to replace many uses of find_directory(): * find_paths() is supposed to be used when the directories of a certain kind in all installation directories are needed (e.g. font directories, add-on directory, etc.). Using this API makes code robust wrt addition or removal of installation locations. * find_path() is supposed to be used when files/directories associated with a loaded program, library, or add-on need to be found (e.g. data files or global settings). * find_path_for_path() is similar to find_path(), but it starts from a given path instead of an image. --- headers/os/storage/FindDirectory.h | 59 ++- .../private/system/find_directory_private.h | 11 + src/system/libroot/os/Jamfile | 1 + src/system/libroot/os/find_paths.cpp | 466 ++++++++++++++++++ 4 files changed, 535 insertions(+), 2 deletions(-) create mode 100644 src/system/libroot/os/find_paths.cpp diff --git a/headers/os/storage/FindDirectory.h b/headers/os/storage/FindDirectory.h index 35b99565fc..761a1eea69 100644 --- a/headers/os/storage/FindDirectory.h +++ b/headers/os/storage/FindDirectory.h @@ -1,12 +1,12 @@ /* - * Copyright 2002-2009, Haiku Inc. All Rights Reserved. + * Copyright 2002-2013, Haiku Inc. All Rights Reserved. * Distributed under the terms of the MIT License. */ #ifndef _FIND_DIRECTORY_H #define _FIND_DIRECTORY_H -#include +#include typedef enum { @@ -120,6 +120,47 @@ typedef enum { B_BEOS_DATA_DIRECTORY, } directory_which; + +/* find_path[s]() flags */ +enum { + B_FIND_PATH_CREATE_DIRECTORY = 0x0001, + B_FIND_PATH_CREATE_PARENT_DIRECTORY = 0x0002, + B_FIND_PATH_EXISTING_ONLY = 0x0004, +}; + + +typedef enum path_base_directory { + B_FIND_PATH_INSTALLATION_LOCATION_DIRECTORY, + B_FIND_PATH_ADD_ONS_DIRECTORY, + B_FIND_PATH_APPS_DIRECTORY, + B_FIND_PATH_BIN_DIRECTORY, + B_FIND_PATH_BOOT_DIRECTORY, + B_FIND_PATH_CACHE_DIRECTORY, + B_FIND_PATH_DATA_DIRECTORY, + B_FIND_PATH_DEVELOP_DIRECTORY, + B_FIND_PATH_DEVELOP_LIB_DIRECTORY, + B_FIND_PATH_DOCUMENTATION_DIRECTORY, + B_FIND_PATH_ETC_DIRECTORY, + B_FIND_PATH_FONTS_DIRECTORY, + B_FIND_PATH_HEADERS_DIRECTORY, + B_FIND_PATH_LIB_DIRECTORY, + B_FIND_PATH_LOG_DIRECTORY, + B_FIND_PATH_MEDIA_NODES_DIRECTORY, + B_FIND_PATH_PACKAGES_DIRECTORY, + B_FIND_PATH_PREFERENCES_DIRECTORY, + B_FIND_PATH_SERVERS_DIRECTORY, + B_FIND_PATH_SETTINGS_DIRECTORY, + B_FIND_PATH_SOUNDS_DIRECTORY, + B_FIND_PATH_SPOOL_DIRECTORY, + B_FIND_PATH_TRANSLATORS_DIRECTORY, + B_FIND_PATH_VAR_DIRECTORY, + + /* find_path() only */ + B_FIND_PATH_IMAGE_PATH = 1000, + B_FIND_PATH_IMAGE_PACKAGE_PATH, +} path_base_directory; + + #ifdef __cplusplus extern "C" { #endif @@ -129,6 +170,18 @@ extern "C" { status_t find_directory(directory_which which, dev_t volume, bool createIt, char* pathString, int32 length); +status_t find_path(const void* codePointer, const char* dependency, + path_base_directory baseDirectory, const char* subPath, uint32 flags, + char* pathBuffer, size_t bufferSize); + +status_t find_path_for_path(const char* path, const char* dependency, + path_base_directory baseDirectory, const char* subPath, uint32 flags, + char* pathBuffer, size_t bufferSize); + +status_t find_paths(path_base_directory baseDirectory, const char* subPath, + uint32 flags, char*** _paths, size_t* _pathCount); + + #ifdef __cplusplus } @@ -137,9 +190,11 @@ status_t find_directory(directory_which which, dev_t volume, bool createIt, class BVolume; class BPath; + status_t find_directory(directory_which which, BPath* path, bool createIt = false, BVolume* volume = NULL); + #endif /* __cplusplus */ #endif /* _FIND_DIRECTORY_H */ diff --git a/headers/private/system/find_directory_private.h b/headers/private/system/find_directory_private.h index 1fed4d298a..3bdd7bf35f 100644 --- a/headers/private/system/find_directory_private.h +++ b/headers/private/system/find_directory_private.h @@ -17,6 +17,17 @@ __BEGIN_DECLS status_t __find_directory(directory_which which, dev_t device, bool createIt, char *returnedPath, int32 pathLength); +status_t __find_path(const void* codePointer, const char* dependency, + path_base_directory baseDirectory, const char* subPath, uint32 flags, + char* pathBuffer, size_t bufferSize); + +status_t __find_path_for_path(const char* path, const char* dependency, + path_base_directory baseDirectory, const char* subPath, uint32 flags, + char* pathBuffer, size_t bufferSize); + +status_t __find_paths(path_base_directory baseDirectory, const char* subPath, + uint32 flags, char*** _paths, size_t* _pathCount); + __END_DECLS diff --git a/src/system/libroot/os/Jamfile b/src/system/libroot/os/Jamfile index ca427584b4..9e5809a871 100644 --- a/src/system/libroot/os/Jamfile +++ b/src/system/libroot/os/Jamfile @@ -20,6 +20,7 @@ for architectureObject in [ MultiArchSubDirSetup ] { driver_settings.cpp extended_system_info.cpp find_directory.cpp + find_paths.cpp fs_attr.cpp fs_index.c fs_info.c diff --git a/src/system/libroot/os/find_paths.cpp b/src/system/libroot/os/find_paths.cpp new file mode 100644 index 0000000000..384f4cd2cb --- /dev/null +++ b/src/system/libroot/os/find_paths.cpp @@ -0,0 +1,466 @@ +/* + * Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include + +#include +#include +#include +#include + +#include + +#include +#include + + +static const char* const kInstallationLocations[] = { + "/boot/home/config/non-packaged", + "/boot/home/config", + "/boot/system/non-packaged", + "/boot/system", +}; + +static size_t kHomeInstallationLocationIndex = 1; +static size_t kInstallationLocationCount + = sizeof(kInstallationLocations) / sizeof(kInstallationLocations[0]); + + +static const char* +get_relative_directory_path(size_t installationLocationIndex, + path_base_directory baseDirectory) +{ + switch (baseDirectory) { + case B_FIND_PATH_INSTALLATION_LOCATION_DIRECTORY: + return ""; + case B_FIND_PATH_ADD_ONS_DIRECTORY: + return "/add-ons"; + case B_FIND_PATH_APPS_DIRECTORY: + return "/apps"; + case B_FIND_PATH_BIN_DIRECTORY: + return "/bin"; + case B_FIND_PATH_BOOT_DIRECTORY: + return "/boot"; + case B_FIND_PATH_CACHE_DIRECTORY: + return "/cache"; + case B_FIND_PATH_DATA_DIRECTORY: + return "/data"; + case B_FIND_PATH_DEVELOP_DIRECTORY: + return "/develop"; + case B_FIND_PATH_DEVELOP_LIB_DIRECTORY: + return "/develop/lib"; + case B_FIND_PATH_DOCUMENTATION_DIRECTORY: + return "/documentation"; + case B_FIND_PATH_ETC_DIRECTORY: + return "/settings/etc"; + case B_FIND_PATH_FONTS_DIRECTORY: + return "/data/fonts"; + case B_FIND_PATH_HEADERS_DIRECTORY: + return "/develop/headers"; + case B_FIND_PATH_LIB_DIRECTORY: + return "/lib"; + case B_FIND_PATH_LOG_DIRECTORY: + return "/log"; + case B_FIND_PATH_MEDIA_NODES_DIRECTORY: + return "/add-ons/media"; + case B_FIND_PATH_PACKAGES_DIRECTORY: + return "/packages"; + case B_FIND_PATH_PREFERENCES_DIRECTORY: + return "/preferences"; + case B_FIND_PATH_SERVERS_DIRECTORY: + return "/servers"; + case B_FIND_PATH_SETTINGS_DIRECTORY: + return installationLocationIndex == kHomeInstallationLocationIndex + ? "/settings/global" : "/settings"; + case B_FIND_PATH_SOUNDS_DIRECTORY: + return "/data/sounds"; + case B_FIND_PATH_SPOOL_DIRECTORY: + return "/var/spool"; + case B_FIND_PATH_TRANSLATORS_DIRECTORY: + return "/add-ons/Translators"; + case B_FIND_PATH_VAR_DIRECTORY: + return "/var"; + + case B_FIND_PATH_IMAGE_PATH: + case B_FIND_PATH_IMAGE_PACKAGE_PATH: + default: + return NULL; + } +} + + +static status_t +create_directory(char* path) +{ + // find the first directory that doesn't exist + char* slash = path; + bool found = false; + while (!found && (slash = strchr(slash + 1, '/')) != NULL) { + *slash = '\0'; + struct stat st; + if (lstat(path, &st) != 0) + break; + *slash = '/'; + } + + if (found) + return B_OK; + + // create directories + while (slash != NULL) { + *slash = '\0'; + bool created = mkdir(path, 0755); + *slash = '/'; + + if (!created) + return errno; + + slash = strchr(slash + 1, '/'); + } + + return B_OK; +} + + +static bool +is_in_range(const void* pointer, const void* base, size_t size) +{ + return pointer >= base && (addr_t)pointer < (addr_t)base + size; +} + + +static status_t +find_image(const void* codePointer, image_info& _info) +{ + int32 cookie = 0; + + while (get_next_image_info(B_CURRENT_TEAM, &cookie, &_info) == B_OK) { + if (codePointer == NULL ? _info.type == B_APP_IMAGE + : (is_in_range(codePointer, _info.text, _info.text_size) + || is_in_range(codePointer, _info.data, _info.data_size))) { + return B_OK; + } + } + + return B_ENTRY_NOT_FOUND; +} + + +static status_t +copy_path(const char* path, char* buffer, size_t bufferSize) +{ + if (strlcpy(buffer, path, bufferSize) >= bufferSize) + return B_BUFFER_OVERFLOW; + return B_OK; +} + + +static status_t +normalize_path(const char* path, char* buffer, size_t bufferSize) +{ + status_t error; + if (bufferSize >= B_PATH_NAME_LENGTH) { + error = _kern_normalize_path(path, true, buffer); + } else { + char normalizedPath[B_PATH_NAME_LENGTH]; + error = _kern_normalize_path(path, true, normalizedPath); + if (error == B_OK) + error = copy_path(path, buffer, bufferSize); + } + + if (error != B_OK) + return error; + + // make sure the path exists + struct stat st; + if (lstat(buffer, &st) != 0) + return errno; + + return B_OK; +} + + +static const char* +get_installation_location(const char* path, size_t& _index) +{ + for (size_t i = 0; i < kInstallationLocationCount; i++) { + size_t length = strlen(kInstallationLocations[i]); + if (strncmp(path, kInstallationLocations[i], length) == 0 + && (path[length] == '/' || path[length] == '\0')) { + _index = i; + return kInstallationLocations[i]; + } + } + + return NULL; +} + + +static status_t +get_file_attribute(const char* path, const char* attribute, char* nameBuffer, + size_t bufferSize) +{ + int fd = fs_open_attr(path, attribute, B_STRING_TYPE, O_RDONLY); + if (fd < 0) + return errno; + + status_t error = B_OK; + ssize_t bytesRead = read(fd, nameBuffer, bufferSize - 1); + if (bytesRead < 0) + error = bytesRead; + else if (bytesRead == 0) + error = B_ENTRY_NOT_FOUND; + else + nameBuffer[bytesRead] = '\0'; + + fs_close_attr(fd); + + return error; +} + + +static status_t +normalize_dependency(const char* dependency, char* buffer, size_t bufferSize) +{ + if (strlcpy(buffer, dependency, bufferSize) >= bufferSize) + return B_NAME_TOO_LONG; + + // replace all ':' with '~' + char* colon = buffer - 1; + while ((colon = strchr(colon + 1, ':')) != NULL) + *colon = '~'; + + return B_OK; +} + + +static ssize_t +process_path(const char* installationLocation, const char* relativePath, + const char* subPath, uint32 flags, char* pathBuffer, size_t bufferSize) +{ + size_t totalLength; + if (subPath != NULL) { + totalLength = snprintf(pathBuffer, bufferSize, "%s%s/%s", + installationLocation, relativePath, subPath); + } else { + totalLength = snprintf(pathBuffer, bufferSize, "%s%s", + installationLocation, relativePath); + } + + if (totalLength >= bufferSize) + return B_BUFFER_OVERFLOW; + + char* path = pathBuffer; + + status_t error = B_OK; + if ((flags & B_FIND_PATH_CREATE_DIRECTORY) != 0) { + // create the directory + error = create_directory(path); + } else if ((flags & B_FIND_PATH_CREATE_PARENT_DIRECTORY) != 0) { + // create the parent directory + char* lastSlash = strrchr(path, '/'); + *lastSlash = '\0'; + error = create_directory(path); + *lastSlash = '/'; + } + + if (error != B_OK) + return error; + + if ((flags & B_FIND_PATH_EXISTING_ONLY) != 0) { + // check if the entry exists + struct stat st; + if (lstat(path, &st) != 0) + return 0; + } + + return totalLength + 1; +} + + +status_t +internal_path_for_path(char* referencePath, size_t referencePathSize, + const char* dependency, path_base_directory baseDirectory, + const char* subPath, uint32 flags, char* pathBuffer, size_t bufferSize) +{ + // normalize + status_t error = normalize_path(referencePath, referencePath, + referencePathSize); + if (error != B_OK) + return error; + + // handle B_FIND_PATH_IMAGE_PATH + if (baseDirectory == B_FIND_PATH_IMAGE_PATH) + return copy_path(referencePath, pathBuffer, bufferSize); + + // get the installation location + size_t installationLocationIndex; + const char* installationLocation = get_installation_location(referencePath, + installationLocationIndex); + if (installationLocation == NULL) + return B_ENTRY_NOT_FOUND; + + // Handle B_FIND_PATH_IMAGE_PACKAGE_PATH: get the package file name and + // simply adjust our arguments to look the package file up in the packages + // directory. + char packageName[B_FILE_NAME_LENGTH]; + if (baseDirectory == B_FIND_PATH_IMAGE_PACKAGE_PATH) { + error = get_file_attribute(referencePath, "SYS:PACKAGE_FILE", + packageName, sizeof(packageName)); + if (error != B_OK) + return error; + + dependency = NULL; + subPath = packageName; + baseDirectory = B_FIND_PATH_PACKAGES_DIRECTORY; + flags = B_FIND_PATH_EXISTING_ONLY; + } + + // resolve dependency + if (dependency != NULL) { + // get the versioned package name + error = get_file_attribute(referencePath, "SYS:PACKAGE", + packageName, sizeof(packageName)); + if (error != B_OK) + return error; + + // normalize the dependency name + char normalizedDependency[B_FILE_NAME_LENGTH]; + error = normalize_dependency(dependency, normalizedDependency, + sizeof(normalizedDependency)); + if (error != B_OK) + return error; + + // Compute the path of the dependency symlink and normalize it. This + // should yield the installation location path. + if (snprintf(referencePath, referencePathSize, "/packages/%s/%s", + packageName, normalizedDependency) + >= (ssize_t)referencePathSize) { + return B_BUFFER_OVERFLOW; + } + + error = normalize_path(referencePath, referencePath, referencePathSize); + if (error != B_OK) + return error; + + // get the installation location + installationLocation = get_installation_location(referencePath, + installationLocationIndex); + if (installationLocation == NULL) + return B_ENTRY_NOT_FOUND; + } + + // get base dir and process the path + const char* relativePath = get_relative_directory_path( + installationLocationIndex, baseDirectory); + if (relativePath == NULL) + return B_BAD_VALUE; + + ssize_t pathSize = process_path(installationLocation, relativePath, subPath, + flags, pathBuffer, bufferSize); + if (pathSize <= 0) + return pathSize == 0 ? B_ENTRY_NOT_FOUND : pathSize; + return B_OK; +} + + +// #pragma mark - + + +status_t +__find_path(const void* codePointer, const char* dependency, + path_base_directory baseDirectory, const char* subPath, uint32 flags, + char* pathBuffer, size_t bufferSize) +{ + if (pathBuffer == NULL) + return B_BAD_VALUE; + + // resolve codePointer to image info + image_info imageInfo; + status_t error = find_image(codePointer, imageInfo); + if (error != B_OK) + return error; + + return internal_path_for_path(imageInfo.name, sizeof(imageInfo.name), + dependency, baseDirectory, subPath, flags, pathBuffer, bufferSize); +} + + +status_t +__find_path_for_path(const char* path, const char* dependency, + path_base_directory baseDirectory, const char* subPath, uint32 flags, + char* pathBuffer, size_t bufferSize) +{ + char referencePath[B_PATH_NAME_LENGTH]; + if (strlcpy(referencePath, path, sizeof(referencePath)) + >= sizeof(referencePath)) { + return B_NAME_TOO_LONG; + } + + return internal_path_for_path(referencePath, sizeof(referencePath), + dependency, baseDirectory, subPath, flags, pathBuffer, bufferSize); +} + + +status_t +__find_paths(path_base_directory baseDirectory, const char* subPath, + uint32 flags, char*** _paths, size_t* _pathCount) +{ + if (_paths == NULL || _pathCount == NULL) + return B_BAD_VALUE; + + size_t subPathLength = subPath != NULL ? strlen(subPath) + 1 : 0; + + // Get the relative paths and compute the total size to allocate. + const char* relativePaths[kInstallationLocationCount]; + size_t totalSize = 0; + + for (size_t i = 0; i < kInstallationLocationCount; i++) { + relativePaths[i] = get_relative_directory_path(i, baseDirectory); + if (relativePaths[i] == NULL) + return B_BAD_VALUE; + + totalSize += strlen(kInstallationLocations[i]) + + strlen(relativePaths[i]) + subPathLength + 1; + } + + // allocate storage + char** paths = (char**)malloc(sizeof(char*) * kInstallationLocationCount + + totalSize); + if (paths == NULL) + return B_NO_MEMORY; + MemoryDeleter pathsDeleter(paths); + + // construct and process the paths + size_t count = 0; + char* pathBuffer = (char*)(paths + kInstallationLocationCount); + const char* pathBufferEnd = pathBuffer + totalSize; + for (size_t i = 0; i < kInstallationLocationCount; i++) { + ssize_t pathSize = process_path(kInstallationLocations[i], + relativePaths[i], subPath, flags, pathBuffer, + pathBufferEnd - pathBuffer); + if (pathSize < 0) + return pathSize; + if (pathSize > 0) { + paths[count++] = pathBuffer; + pathBuffer += pathSize; + } + } + + if (count == 0) + return B_ENTRY_NOT_FOUND; + + *_paths = paths; + *_pathCount = count; + pathsDeleter.Detach(); + + return B_OK; +} + + +B_DEFINE_WEAK_ALIAS(__find_path, find_path); +B_DEFINE_WEAK_ALIAS(__find_path_for_path, find_path_for_path); +B_DEFINE_WEAK_ALIAS(__find_paths, find_paths);