[Mesa-dev] [PATCH 5/7] glsl: Make cache directory if it does not already exist

Carl Worth cworth at cworth.org
Wed Feb 4 13:52:59 PST 2015


With this patch, there are now three different options for the shader
cache directory, (considered in order until the first variable is
set):

	$MESA_GLSL_CACHE_DIR
	$XDG_CACHE_HOME/mesa
	<user-home-directory>/.cache/mesa

Also with this patch, once the desired path is determined, the
directory is created if it does not exist, (but this code will not
create an arbitrary number of parent directories if they don't exist).
---
 src/glsl/cache.c | 170 +++++++++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 147 insertions(+), 23 deletions(-)

diff --git a/src/glsl/cache.c b/src/glsl/cache.c
index da71868..79e8afb 100644
--- a/src/glsl/cache.c
+++ b/src/glsl/cache.c
@@ -30,76 +30,200 @@
 #include <unistd.h>
 #include <fcntl.h>
 #include <pwd.h>
+#include <errno.h>
 
 #include "util/mesa-sha1.h"
+#include "util/ralloc.h"
+#include "main/errors.h"
 
 #include "cache.h"
 
 #define INDEX_SIZE 1024
 struct program_cache {
    unsigned char *index;
-   char path[256];
+   char *path;
 };
 
+/* Create a directory named 'path' if it does not already exist.
+ *
+ * Returns: 0 if path already exists as a directory or if created.
+ *         -1 in all other cases.
+ */
+static int
+mkdir_if_needed(char *path)
+{
+   struct stat sb;
+
+   /* If the path exists already, then our work is done if it'sa directory,
+    * but it's an error if it is not.
+    */
+   if (stat(path, &sb) == 0) {
+      if (S_ISDIR(sb.st_mode)) {
+         return 0;
+      } else {
+         _mesa_warning(NULL,
+                       "Cannot use %s for shader cache (not a directory)"
+                       "---disabling.\n", path);
+         return -1;
+      }
+   }
+
+   if (mkdir(path, 0755) == 0)
+      return 0;
+
+   _mesa_warning(NULL,
+                 "Failed to create %s for shader cache (%s)---disabling.\n",
+                 path, strerror(errno));
+
+   return -1;
+}
+
+/* Concatenate an existing path and a new name to form a new path.  If the new
+ * path does not exist as a directory, create it then return the resulting
+ * name of the new path (ralloc'ed off of 'ctx').
+ *
+ * Returns NULL on any error, such as:
+ *
+ *	<path> does not exist or is not a directory
+ *	<path>/<name> exists but is not a directory
+ *	<path>/<name> cannot be created as a directory
+ */
+static char *
+concatenate_and_mkdir(void *ctx, char *path, char *name)
+{
+   char *new_path;
+   struct stat sb;
+
+   if (stat(path, &sb) != 0 || ! S_ISDIR(sb.st_mode))
+      return NULL;
+
+   new_path = ralloc_asprintf(ctx, "%s/%s", path, name);
+
+   if (mkdir_if_needed(new_path) == 0)
+      return new_path;
+   else
+      return NULL;
+}
+
 struct program_cache *
 cache_create(void)
 {
-   struct program_cache *cache;
-   char index_file[256], buffer[512];
+   void *ctx = ralloc_context(NULL);
+   struct program_cache *cache = NULL;
+   char *path, *index_file;
    struct stat sb;
    size_t size;
-   int fd;
-   struct passwd pwd, *result;
+   int fd = -1;
 
    /* At user request, disable shader cache entirely. */
    if (getenv("MESA_GLSL_CACHE_DISABLE"))
-      return NULL;
+      goto done;
+
+   /* Determine path for cache based on the first defined name as follows:
+    *
+    *	$MESA_GLSL_CACHE_DIR
+    *   $XDG_CACHE_HOME/mesa
+    *   <pwd.pw_dir>/.cache/mesa
+    */
+   path = getenv("MESA_GLSL_CACHE_DIR");
+   if (path && mkdir_if_needed(path) == -1) {
+      goto done;
+   }
 
-   getpwuid_r(getuid(), &pwd, buffer, sizeof buffer, &result);
-   if (result == NULL)
-      return NULL;
-   snprintf(index_file, sizeof index_file,
-            "%s/.cache/mesa/index", pwd.pw_dir);
+   if (path == NULL) {
+      char *xdg_cache_home = getenv("XDG_CACHE_HOME");
+
+      if (xdg_cache_home) {
+         if (mkdir_if_needed(xdg_cache_home) == -1)
+            goto done;
+
+         path = concatenate_and_mkdir(ctx, xdg_cache_home, "mesa");
+         if (path == NULL)
+            goto done;
+      }
+   }
+
+   if (path == NULL) {
+      char *buf;
+      size_t buf_size;
+      struct passwd pwd, *result;
+
+      buf_size = sysconf(_SC_GETPW_R_SIZE_MAX);
+      if (buf_size == -1)
+         buf_size = 512;
+
+      /* Loop until buf_size is large enough to query the directory */
+      while (1) {
+         buf = ralloc_size(ctx, buf_size);
+
+         getpwuid_r(getuid(), &pwd, buf, buf_size, &result);
+         if (result)
+            break;
+
+         if (errno == ERANGE) {
+            ralloc_free(buf);
+            buf = NULL;
+            buf_size *= 2;
+         } else {
+            goto done;
+         }
+      }
+
+      path = concatenate_and_mkdir(ctx, pwd.pw_dir, ".cache");
+      if (path == NULL)
+         goto done;
+
+      path = concatenate_and_mkdir(ctx, path, "mesa");
+      if (path == NULL)
+         goto done;
+   }
+
+   index_file = ralloc_asprintf(ctx, "%s/%s", path, "index");
 
    fd = open(index_file, O_RDWR | O_CREAT | O_CLOEXEC, 0644);
    if (fd == -1) {
-      /* FIXME: Check for ENOENT and mkdir on demand */
-      return NULL;
+      goto done;
    }
 
    if (fstat(fd, &sb) == -1) {
-      close(fd);
-      return NULL;
+      goto done;
    }
 
    size = INDEX_SIZE * CACHE_KEY_SIZE;
    if (sb.st_size == 0) {
       if (ftruncate(fd, size) == -1) {
-         close(fd);
-         return NULL;
+         goto done;
       }
       fsync(fd);
    }
 
    cache = (struct program_cache *) malloc(sizeof *cache);
    if (cache == NULL) {
-      close(fd);
-      return NULL;
+      goto done;
    }
 
-   snprintf(cache->path, sizeof cache->path,
-            "%s/.cache/mesa", pwd.pw_dir);
+   cache->path = strdup(path);
+   if (cache->path == NULL) {
+      free (cache);
+      cache = NULL;
+      goto done;
+   }
 
    /* FIXME: We map this shared, which is a start, but we need to think about
     * how to make it multi-process safe. */
    cache->index = (unsigned char *)
       mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
-   close(fd);
    if (cache->index == MAP_FAILED) {
       free(cache);
-      return NULL;
+      cache = NULL;
+      goto done;
    }
 
+ done:
+   if (fd != -1)
+      close(fd);
+   ralloc_free(ctx);
+
    return cache;
 }
 
-- 
2.1.4



More information about the mesa-dev mailing list