loader: Improve Linux manifest search
authorBenjamin Saunders <ben.e.saunders@gmail.com>
Sun, 29 Jan 2017 22:49:54 +0000 (14:49 -0800)
committerMark Young <marky@lunarg.com>
Thu, 2 Feb 2017 17:16:28 +0000 (10:16 -0700)
Searching XDG_CONFIG_DIRS and XDG_DATA_DIRS instead of hard-coding
specific paths improves portability, particularly on platforms which
don't implement the Filesystem Hierarchy Standard. For example, this
allows the loader to function correctly out of the box on NixOS.

CMakeLists.txt
loader/loader.c
loader/vk_loader_platform.h

index d3037f8..494dd59 100644 (file)
@@ -286,17 +286,10 @@ if(NOT WIN32)
     include(GNUInstallDirs)
 
     add_definitions(-DSYSCONFDIR="${CMAKE_INSTALL_FULL_SYSCONFDIR}")
-    add_definitions(-DDATADIR="${CMAKE_INSTALL_FULL_DATADIR}")
-
     # Make sure /etc is searched by the loader
-    if (NOT (CMAKE_INSTALL_FULL_SYSCONFDIR STREQUAL "/etc"))
+    if(NOT (CMAKE_INSTALL_FULL_SYSCONFDIR STREQUAL "/etc"))
         add_definitions(-DEXTRASYSCONFDIR="/etc")
     endif()
-
-    # Make sure /usr/share is searched by the loader
-    if (NOT (CMAKE_INSTALL_FULL_DATADIR STREQUAL "/usr/share"))
-        add_definitions(-DEXTRADATADIR="/usr/share")
-    endif()
 endif()
 
 if(UNIX)
index 2875af1..81c37c4 100644 (file)
@@ -30,6 +30,7 @@
 #include <stdarg.h>
 #include <stdbool.h>
 #include <string.h>
+#include <stddef.h>
 
 #include <sys/types.h>
 #if defined(_WIN32)
@@ -2585,7 +2586,7 @@ static void loader_add_layer_properties(const struct loader_instance *inst, stru
  * Linux Layer| dirs     | dirs
  */
 static VkResult loader_get_manifest_files(const struct loader_instance *inst, const char *env_override, const char *source_override,
-                                          bool is_layer, bool warn_if_not_present, const char *location, const char *home_location,
+                                          bool is_layer, bool warn_if_not_present, const char *location, const char *relative_location,
                                           struct loader_manifest_files *out_files) {
     const char * override = NULL;
     char *override_getenv = NULL;
@@ -2617,9 +2618,9 @@ static VkResult loader_get_manifest_files(const struct loader_instance *inst, co
     }
 
 #if !defined(_WIN32)
-    if (location == NULL && home_location == NULL) {
+    if (relative_location == NULL) {
 #else
-    home_location = NULL;
+    relative_location = NULL;
     if (location == NULL) {
 #endif
         loader_log(inst, VK_DEBUG_REPORT_ERROR_BIT_EXT, 0,
@@ -2638,16 +2639,92 @@ static VkResult loader_get_manifest_files(const struct loader_instance *inst, co
     // Make a copy of the input we are using so it is not modified
     // Also handle getting the location(s) from registry on Windows
     if (override == NULL) {
-        loc = loader_stack_alloc(strlen(location) + 1);
+        size_t loc_size = 0;
+#if !defined(_WIN32)
+        const char *xdgconfdirs = secure_getenv("XDG_CONFIG_DIRS");
+        const char *xdgdatadirs = secure_getenv("XDG_DATA_DIRS");
+        if (xdgconfdirs == NULL || xdgconfdirs[0] == '\0')
+            xdgconfdirs = "/etc/xdg";
+        if (xdgdatadirs == NULL || xdgdatadirs[0] == '\0')
+            xdgdatadirs = "/usr/local/share:/usr/share";
+        const size_t rel_size = strlen(relative_location);
+        // Leave space for trailing separators
+        loc_size += strlen(xdgconfdirs) + strlen(xdgdatadirs) + 2*rel_size + 2;
+        for (const char *x = xdgconfdirs; *x; ++x)
+            if (*x == PATH_SEPARATOR) loc_size += rel_size;
+        for (const char *x = xdgdatadirs; *x; ++x)
+            if (*x == PATH_SEPARATOR) loc_size += rel_size;
+        loc_size += strlen(SYSCONFDIR) + rel_size + 1;
+#if defined(EXTRASYSCONFDIR)
+        loc_size += strlen(EXTRASYSCONFDIR) + rel_size + 1;
+#endif
+#else
+        loc_size += strlen(location) + 1;
+#endif
+        loc = loader_stack_alloc(loc_size);
         if (loc == NULL) {
             loader_log(inst, VK_DEBUG_REPORT_ERROR_BIT_EXT, 0,
                        "loader_get_manifest_files: Failed to allocate "
                        "%d bytes for manifest file location.",
-                       strlen(location));
+                       loc_size);
             res = VK_ERROR_OUT_OF_HOST_MEMORY;
             goto out;
         }
-        strcpy(loc, location);
+        char *loc_write = loc;
+#if !defined(_WIN32)
+        const char *loc_read;
+
+        loc_read = &xdgconfdirs[0];
+        for (const char *x = loc_read;; ++x) {
+            if (*x == PATH_SEPARATOR || *x == '\0') {
+                const size_t s = x - loc_read;
+                memcpy(loc_write, loc_read, s);
+                loc_write += s;
+                memcpy(loc_write, relative_location, rel_size);
+                loc_write += rel_size;
+                *loc_write++ = PATH_SEPARATOR;
+                if (*x == 0)
+                    break;
+                loc_read = ++x;
+            }
+        }
+
+        memcpy(loc_write, SYSCONFDIR, strlen(SYSCONFDIR));
+        loc_write += strlen(SYSCONFDIR);
+        memcpy(loc_write, relative_location, rel_size);
+        loc_write += rel_size;
+        *loc_write++ = PATH_SEPARATOR;
+
+#if defined(EXTRASYSCONFDIR)
+        memcpy(loc_write, EXTRASYSCONFDIR, strlen(EXTRASYSCONFDIR));
+        loc_write += strlen(EXTRASYSCONFDIR);
+        memcpy(loc_write, relative_location, rel_size);
+        loc_write += rel_size;
+        *loc_write++ = PATH_SEPARATOR;
+#endif
+
+        loc_read = &xdgdatadirs[0];
+        for (const char *x = loc_read;; ++x) {
+            if (*x == PATH_SEPARATOR || *x == '\0') {
+                const size_t s = x - loc_read;
+                memcpy(loc_write, loc_read, s);
+                loc_write += s;
+                memcpy(loc_write, relative_location, rel_size);
+                loc_write += rel_size;
+                *loc_write++ = PATH_SEPARATOR;
+                if (*x == 0)
+                    break;
+                loc_read = ++x;
+            }
+        }
+        --loc_write;
+#else
+        memcpy(loc_write, location, strlen(location));
+        loc_write += strlen(location);
+#endif
+        assert(loc_write - loc < (ptrdiff_t)loc_size);
+        *loc_write = '\0';
+
 #if defined(_WIN32)
         VkResult reg_result = loaderGetRegistryFiles(inst, loc, &reg);
         if (VK_SUCCESS != reg_result || NULL == reg) {
@@ -2786,11 +2863,11 @@ static VkResult loader_get_manifest_files(const struct loader_instance *inst, co
         }
         file = next_file;
 #if !defined(_WIN32)
-        if (home_location != NULL && (next_file == NULL || *next_file == '\0') && override == NULL) {
+        if (relative_location != NULL && (next_file == NULL || *next_file == '\0') && override == NULL) {
             char *xdgdatahome = secure_getenv("XDG_DATA_HOME");
             size_t len;
             if (xdgdatahome != NULL) {
-                char *home_loc = loader_stack_alloc(strlen(xdgdatahome) + 2 + strlen(home_location));
+                char *home_loc = loader_stack_alloc(strlen(xdgdatahome) + 2 + strlen(relative_location));
                 if (home_loc == NULL) {
                     loader_log(inst, VK_DEBUG_REPORT_ERROR_BIT_EXT, 0,
                                "loader_get_manifest_files: Failed to allocate "
@@ -2800,15 +2877,15 @@ static VkResult loader_get_manifest_files(const struct loader_instance *inst, co
                 }
                 strcpy(home_loc, xdgdatahome);
                 // Add directory separator if needed
-                if (home_location[0] != DIRECTORY_SYMBOL) {
+                if (relative_location[0] != DIRECTORY_SYMBOL) {
                     len = strlen(home_loc);
                     home_loc[len] = DIRECTORY_SYMBOL;
                     home_loc[len + 1] = '\0';
                 }
-                strcat(home_loc, home_location);
+                strcat(home_loc, relative_location);
                 file = home_loc;
                 next_file = loader_get_next_path(file);
-                home_location = NULL;
+                relative_location = NULL;
 
                 loader_log(inst, VK_DEBUG_REPORT_DEBUG_BIT_EXT, 0, "Searching the following path for manifest files: %s\n",
                            home_loc);
@@ -2817,7 +2894,7 @@ static VkResult loader_get_manifest_files(const struct loader_instance *inst, co
             } else {
                 char *home = secure_getenv("HOME");
                 if (home != NULL) {
-                    char *home_loc = loader_stack_alloc(strlen(home) + 16 + strlen(home_location));
+                    char *home_loc = loader_stack_alloc(strlen(home) + 16 + strlen(relative_location));
                     if (home_loc == NULL) {
                         loader_log(inst, VK_DEBUG_REPORT_ERROR_BIT_EXT, 0,
                                    "loader_get_manifest_files: Failed to allocate "
@@ -2834,15 +2911,15 @@ static VkResult loader_get_manifest_files(const struct loader_instance *inst, co
                     }
                     strcat(home_loc, ".local/share");
 
-                    if (home_location[0] != DIRECTORY_SYMBOL) {
+                    if (relative_location[0] != DIRECTORY_SYMBOL) {
                         len = strlen(home_loc);
                         home_loc[len] = DIRECTORY_SYMBOL;
                         home_loc[len + 1] = '\0';
                     }
-                    strcat(home_loc, home_location);
+                    strcat(home_loc, relative_location);
                     file = home_loc;
                     next_file = loader_get_next_path(file);
-                    home_location = NULL;
+                    relative_location = NULL;
 
                     loader_log(inst, VK_DEBUG_REPORT_DEBUG_BIT_EXT, 0, "Searching the following path for manifest files: %s\n",
                                home_loc);
@@ -2914,7 +2991,7 @@ VkResult loader_icd_scan(const struct loader_instance *inst, struct loader_icd_t
     }
 
     // Get a list of manifest files for ICDs
-    res = loader_get_manifest_files(inst, "VK_ICD_FILENAMES", NULL, false, true, DEFAULT_VK_DRIVERS_INFO, HOME_VK_DRIVERS_INFO,
+    res = loader_get_manifest_files(inst, "VK_ICD_FILENAMES", NULL, false, true, DEFAULT_VK_DRIVERS_INFO, RELATIVE_VK_DRIVERS_INFO,
                                     &manifest_files);
     if (VK_SUCCESS != res || manifest_files.count == 0) {
         goto out;
@@ -3128,14 +3205,14 @@ void loader_layer_scan(const struct loader_instance *inst, struct loader_layer_l
 
     // Get a list of manifest files for explicit layers
     if (VK_SUCCESS != loader_get_manifest_files(inst, LAYERS_PATH_ENV, LAYERS_SOURCE_PATH, true, true, DEFAULT_VK_ELAYERS_INFO,
-                                                HOME_VK_ELAYERS_INFO, &manifest_files[0])) {
+                                                RELATIVE_VK_ELAYERS_INFO, &manifest_files[0])) {
         goto out;
     }
 
     // Get a list of manifest files for any implicit layers
     // Pass NULL for environment variable override - implicit layers are not
     // overridden by LAYERS_PATH_ENV
-    if (VK_SUCCESS != loader_get_manifest_files(inst, NULL, NULL, true, false, DEFAULT_VK_ILAYERS_INFO, HOME_VK_ILAYERS_INFO,
+    if (VK_SUCCESS != loader_get_manifest_files(inst, NULL, NULL, true, false, DEFAULT_VK_ILAYERS_INFO, RELATIVE_VK_ILAYERS_INFO,
                                                 &manifest_files[1])) {
         goto out;
     }
@@ -3198,7 +3275,7 @@ void loader_implicit_layer_scan(const struct loader_instance *inst, struct loade
     // Pass NULL for environment variable override - implicit layers are not
     // overridden by LAYERS_PATH_ENV
     VkResult res =
-        loader_get_manifest_files(inst, NULL, NULL, true, false, DEFAULT_VK_ILAYERS_INFO, HOME_VK_ILAYERS_INFO, &manifest_files);
+        loader_get_manifest_files(inst, NULL, NULL, true, false, DEFAULT_VK_ILAYERS_INFO, RELATIVE_VK_ILAYERS_INFO, &manifest_files);
     if (VK_SUCCESS != res || manifest_files.count == 0) {
         return;
     }
index 8a70865..9af64a9 100644 (file)
 #define VULKAN_ILAYERCONF_DIR "implicit_layer.d"
 #define VULKAN_LAYER_DIR "layer"
 
-#if defined(EXTRASYSCONFDIR)
-#define EXTRA_DRIVERS_SYSCONFDIR_INFO ":" EXTRASYSCONFDIR VULKAN_DIR VULKAN_ICDCONF_DIR
-#define EXTRA_ELAYERS_SYSCONFDIR_INFO ":" EXTRASYSCONFDIR VULKAN_DIR VULKAN_ELAYERCONF_DIR
-#define EXTRA_ILAYERS_SYSCONFDIR_INFO ":" EXTRASYSCONFDIR VULKAN_DIR VULKAN_ILAYERCONF_DIR
-#else
-#define EXTRA_DRIVERS_SYSCONFDIR_INFO
-#define EXTRA_ELAYERS_SYSCONFDIR_INFO
-#define EXTRA_ILAYERS_SYSCONFDIR_INFO
-#endif
-
-#if defined(EXTRADATADIR)
-#define EXTRA_DRIVERS_DATADIR_INFO ":" EXTRADATADIR VULKAN_DIR VULKAN_ICDCONF_DIR
-#define EXTRA_ELAYERS_DATADIR_INFO ":" EXTRADATADIR VULKAN_DIR VULKAN_ELAYERCONF_DIR
-#define EXTRA_ILAYERS_DATADIR_INFO ":" EXTRADATADIR VULKAN_DIR VULKAN_ILAYERCONF_DIR
-#else
-#define EXTRA_DRIVERS_DATADIR_INFO
-#define EXTRA_ELAYERS_DATADIR_INFO
-#define EXTRA_ILAYERS_DATADIR_INFO
-#endif
-
-#define DEFAULT_VK_DRIVERS_INFO              \
-    SYSCONFDIR VULKAN_DIR VULKAN_ICDCONF_DIR \
-        ":" DATADIR VULKAN_DIR VULKAN_ICDCONF_DIR EXTRA_DRIVERS_SYSCONFDIR_INFO EXTRA_DRIVERS_DATADIR_INFO
-#define DEFAULT_VK_ELAYERS_INFO                 \
-    SYSCONFDIR VULKAN_DIR VULKAN_ELAYERCONF_DIR \
-        ":" DATADIR VULKAN_DIR VULKAN_ELAYERCONF_DIR EXTRA_ELAYERS_SYSCONFDIR_INFO EXTRA_ELAYERS_DATADIR_INFO
-#define DEFAULT_VK_ILAYERS_INFO                 \
-    SYSCONFDIR VULKAN_DIR VULKAN_ILAYERCONF_DIR \
-        ":" DATADIR VULKAN_DIR VULKAN_ILAYERCONF_DIR EXTRA_ILAYERS_SYSCONFDIR_INFO EXTRA_ILAYERS_DATADIR_INFO
+#define DEFAULT_VK_DRIVERS_INFO ""
+#define DEFAULT_VK_ELAYERS_INFO ""
+#define DEFAULT_VK_ILAYERS_INFO ""
 
 #define DEFAULT_VK_DRIVERS_PATH ""
 #if !defined(DEFAULT_VK_LAYERS_PATH)
@@ -97,9 +71,9 @@
 #endif
 #define LAYERS_PATH_ENV "VK_LAYER_PATH"
 
-#define HOME_VK_DRIVERS_INFO VULKAN_DIR VULKAN_ICDCONF_DIR
-#define HOME_VK_ELAYERS_INFO VULKAN_DIR VULKAN_ELAYERCONF_DIR
-#define HOME_VK_ILAYERS_INFO VULKAN_DIR VULKAN_ILAYERCONF_DIR
+#define RELATIVE_VK_DRIVERS_INFO VULKAN_DIR VULKAN_ICDCONF_DIR
+#define RELATIVE_VK_ELAYERS_INFO VULKAN_DIR VULKAN_ELAYERCONF_DIR
+#define RELATIVE_VK_ILAYERS_INFO VULKAN_DIR VULKAN_ILAYERCONF_DIR
 
 // C99:
 #define PRINTF_SIZE_T_SPECIFIER "%zu"
@@ -201,9 +175,9 @@ static inline void loader_platform_thread_cond_broadcast(loader_platform_thread_
 #define LAYERS_SOURCE_PATH NULL
 #endif
 #define LAYERS_PATH_ENV "VK_LAYER_PATH"
-#define HOME_VK_DRIVERS_INFO ""
-#define HOME_VK_ELAYERS_INFO ""
-#define HOME_VK_ILAYERS_INFO ""
+#define RELATIVE_VK_DRIVERS_INFO ""
+#define RELATIVE_VK_ELAYERS_INFO ""
+#define RELATIVE_VK_ILAYERS_INFO ""
 #define PRINTF_SIZE_T_SPECIFIER "%Iu"
 
 // File IO