]> mj.ucw.cz Git - pciutils.git/blobdiff - lib/names-cache.c
libpci: Do not build physmem-posix.c when not needed
[pciutils.git] / lib / names-cache.c
index c97ea302ebe9cb6649d4834c1ad34695c3b0ed07..16e9e9af2faf0cfa10c447e458ff7c70cd0a3f74 100644 (file)
@@ -3,7 +3,9 @@
  *
  *     Copyright (c) 2008--2009 Martin Mares <mj@ucw.cz>
  *
- *     Can be freely distributed and used under the terms of the GNU GPL.
+ *     Can be freely distributed and used under the terms of the GNU GPL v2+.
+ *
+ *     SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #include "internal.h"
@@ -16,6 +18,7 @@
 #include <string.h>
 #include <errno.h>
 #include <sys/types.h>
+#include <sys/stat.h>
 #include <pwd.h>
 #include <unistd.h>
 
@@ -23,24 +26,75 @@ static const char cache_version[] = "#PCI-CACHE-1.0";
 
 static char *get_cache_name(struct pci_access *a)
 {
-  char *name, *buf;
-
-  name = pci_get_param(a, "net.cache_name");
-  if (!name || !name[0])
-    return NULL;
-  if (strncmp(name, "~/", 2))
-    return name;
-
-  uid_t uid = getuid();
-  struct passwd *pw = getpwuid(uid);
-  if (!pw)
-    return name;
-
-  buf = pci_malloc(a, strlen(pw->pw_dir) + strlen(name+1) + 1);
-  sprintf(buf, "%s%s", pw->pw_dir, name+1);
-  pci_set_param_internal(a, "net.cache_name", buf, 0);
-  pci_mfree(buf);
-  return pci_get_param(a, "net.cache_name");
+  if (!a->id_cache_name)
+    {
+      char *name = pci_get_param(a, "net.cache_name");
+      if (!name || !name[0])
+       return NULL;
+
+      if (strncmp(name, "~/", 2))
+       a->id_cache_name = pci_strdup(a, name);
+      else
+       {
+         uid_t uid = getuid();
+         struct passwd *pw = getpwuid(uid);
+         if (!pw)
+           return name;
+
+         a->id_cache_name = pci_malloc(a, strlen(pw->pw_dir) + strlen(name+1) + 1);
+         sprintf(a->id_cache_name, "%s%s", pw->pw_dir, name+1);
+       }
+    }
+
+  return a->id_cache_name;
+}
+
+static void create_parent_dirs(struct pci_access *a, char *name)
+{
+  // Assumes that we have a private copy of the name we can modify
+
+  char *p = name + strlen(name);
+  while (p > name && *p != '/')
+    p--;
+  if (p == name)
+    return;
+
+  while (p > name)
+    {
+      // We stand at a slash. Check if the current prefix exists.
+      *p = 0;
+      struct stat st;
+      int res = stat(name, &st);
+      *p = '/';
+      if (res >= 0)
+       break;
+
+      // Does not exist yet, move up one directory
+      p--;
+      while (p > name && *p != '/')
+       p--;
+    }
+
+  // We now stand at the end of the longest existing prefix.
+  // Create all directories to the right of it.
+  for (;;)
+    {
+      p++;
+      while (*p && *p != '/')
+       p++;
+      if (!*p)
+       break;
+
+      *p = 0;
+      int res = mkdir(name, 0777);
+      if (res < 0)
+       {
+         a->warning("Cannot create directory %s: %s", name, strerror(errno));
+         *p = '/';
+         break;
+       }
+      *p = '/';
+    }
 }
 
 int
@@ -51,11 +105,15 @@ pci_id_cache_load(struct pci_access *a, int flags)
   FILE *f;
   int lino;
 
+  if (a->id_cache_status > 0)
+    return 0;
   a->id_cache_status = 1;
+
   name = get_cache_name(a);
   if (!name)
     return 0;
   a->debug("Using cache %s\n", name);
+
   if (flags & PCI_LOOKUP_REFRESH_CACHE)
     {
       a->debug("Not loading cache, will refresh everything\n");
@@ -128,6 +186,8 @@ pci_id_cache_flush(struct pci_access *a)
   if (!name)
     return;
 
+  create_parent_dirs(a, name);
+
   this_pid = getpid();
   if (gethostname(hostname, sizeof(hostname)) < 0)
     hostname[0] = 0;
@@ -192,6 +252,8 @@ int pci_id_cache_load(struct pci_access *a UNUSED, int flags UNUSED)
 void pci_id_cache_flush(struct pci_access *a)
 {
   a->id_cache_status = 0;
+  pci_mfree(a->id_cache_name);
+  a->id_cache_name = NULL;
 }
 
 #endif