xref: /aosp_15_r20/external/pciutils/lib/names-cache.c (revision c2e0c6b56a71da9abe8df5c8348fb3eb5c2c9251)
1 /*
2  *	The PCI Library -- ID to Name Cache
3  *
4  *	Copyright (c) 2008--2009 Martin Mares <[email protected]>
5  *
6  *	Can be freely distributed and used under the terms of the GNU GPL v2+.
7  *
8  *	SPDX-License-Identifier: GPL-2.0-or-later
9  */
10 
11 #include "internal.h"
12 #include "names.h"
13 
14 #ifdef PCI_USE_DNS
15 
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <errno.h>
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <pwd.h>
23 #include <unistd.h>
24 
25 static const char cache_version[] = "#PCI-CACHE-1.0";
26 
get_cache_name(struct pci_access * a)27 static char *get_cache_name(struct pci_access *a)
28 {
29   if (!a->id_cache_name)
30     {
31       char *name = pci_get_param(a, "net.cache_name");
32       if (!name || !name[0])
33 	return NULL;
34 
35       if (strncmp(name, "~/", 2))
36 	a->id_cache_name = pci_strdup(a, name);
37       else
38 	{
39 	  uid_t uid = getuid();
40 	  struct passwd *pw = getpwuid(uid);
41 	  if (!pw)
42 	    return name;
43 
44 	  a->id_cache_name = pci_malloc(a, strlen(pw->pw_dir) + strlen(name+1) + 1);
45 	  sprintf(a->id_cache_name, "%s%s", pw->pw_dir, name+1);
46 	}
47     }
48 
49   return a->id_cache_name;
50 }
51 
create_parent_dirs(struct pci_access * a,char * name)52 static void create_parent_dirs(struct pci_access *a, char *name)
53 {
54   // Assumes that we have a private copy of the name we can modify
55 
56   char *p = name + strlen(name);
57   while (p > name && *p != '/')
58     p--;
59   if (p == name)
60     return;
61 
62   while (p > name)
63     {
64       // We stand at a slash. Check if the current prefix exists.
65       *p = 0;
66       struct stat st;
67       int res = stat(name, &st);
68       *p = '/';
69       if (res >= 0)
70 	break;
71 
72       // Does not exist yet, move up one directory
73       p--;
74       while (p > name && *p != '/')
75 	p--;
76     }
77 
78   // We now stand at the end of the longest existing prefix.
79   // Create all directories to the right of it.
80   for (;;)
81     {
82       p++;
83       while (*p && *p != '/')
84 	p++;
85       if (!*p)
86 	break;
87 
88       *p = 0;
89       int res = mkdir(name, 0777);
90       if (res < 0)
91 	{
92 	  a->warning("Cannot create directory %s: %s", name, strerror(errno));
93 	  *p = '/';
94 	  break;
95 	}
96       *p = '/';
97     }
98 }
99 
100 int
pci_id_cache_load(struct pci_access * a,int flags)101 pci_id_cache_load(struct pci_access *a, int flags)
102 {
103   char *name;
104   char line[MAX_LINE];
105   FILE *f;
106   int lino;
107 
108   if (a->id_cache_status > 0)
109     return 0;
110   a->id_cache_status = 1;
111 
112   name = get_cache_name(a);
113   if (!name)
114     return 0;
115   a->debug("Using cache %s\n", name);
116 
117   if (flags & PCI_LOOKUP_REFRESH_CACHE)
118     {
119       a->debug("Not loading cache, will refresh everything\n");
120       a->id_cache_status = 2;
121       return 0;
122     }
123 
124   f = fopen(name, "rb");
125   if (!f)
126     {
127       a->debug("Cache file does not exist\n");
128       return 0;
129     }
130   /* FIXME: Compare timestamp with the pci.ids file? */
131 
132   lino = 0;
133   while (fgets(line, sizeof(line), f))
134     {
135       char *p = strchr(line, '\n');
136       lino++;
137       if (p)
138         {
139 	  *p = 0;
140 	  if (lino == 1)
141 	    {
142 	      if (strcmp(line, cache_version))
143 	        {
144 		  a->debug("Unrecognized cache version %s, ignoring\n", line);
145 		  break;
146 		}
147 	      continue;
148 	    }
149 	  else
150 	    {
151 	      int cat, id1, id2, id3, id4, cnt;
152 	      if (sscanf(line, "%d%x%x%x%x%n", &cat, &id1, &id2, &id3, &id4, &cnt) >= 5)
153 	        {
154 		  p = line + cnt;
155 		  while (*p && *p == ' ')
156 		    p++;
157 		  pci_id_insert(a, cat, id1, id2, id3, id4, p, SRC_CACHE);
158 		  continue;
159 		}
160 	    }
161 	}
162       a->warning("Malformed cache file %s (line %d), ignoring", name, lino);
163       break;
164     }
165 
166   if (ferror(f))
167     a->warning("Error while reading %s", name);
168   fclose(f);
169   return 1;
170 }
171 
172 void
pci_id_cache_flush(struct pci_access * a)173 pci_id_cache_flush(struct pci_access *a)
174 {
175   int orig_status = a->id_cache_status;
176   FILE *f;
177   unsigned int h;
178   struct id_entry *e, *e2;
179   char hostname[256], *tmpname, *name;
180   int this_pid;
181 
182   a->id_cache_status = 0;
183   if (orig_status < 2)
184     return;
185   name = get_cache_name(a);
186   if (!name)
187     return;
188 
189   create_parent_dirs(a, name);
190 
191   this_pid = getpid();
192   if (gethostname(hostname, sizeof(hostname)) < 0)
193     hostname[0] = 0;
194   else
195     hostname[sizeof(hostname)-1] = 0;
196   tmpname = pci_malloc(a, strlen(name) + strlen(hostname) + 64);
197   sprintf(tmpname, "%s.tmp-%s-%d", name, hostname, this_pid);
198 
199   f = fopen(tmpname, "wb");
200   if (!f)
201     {
202       a->warning("Cannot write to %s: %s", name, strerror(errno));
203       pci_mfree(tmpname);
204       return;
205     }
206   a->debug("Writing cache to %s\n", name);
207   fprintf(f, "%s\n", cache_version);
208 
209   for (h=0; h<HASH_SIZE; h++)
210     for (e=a->id_hash[h]; e; e=e->next)
211       if (e->src == SRC_CACHE || e->src == SRC_NET)
212 	{
213 	  /* Negative entries are not written */
214 	  if (!e->name[0])
215 	    continue;
216 
217 	  /* Verify that every entry is written at most once */
218 	  for (e2=a->id_hash[h]; e2 != e; e2=e2->next)
219 	    if ((e2->src == SRC_CACHE || e2->src == SRC_NET) &&
220 	        e2->cat == e->cat &&
221 		e2->id12 == e->id12 && e2->id34 == e->id34)
222 	    break;
223 	  if (e2 == e)
224 	    fprintf(f, "%d %x %x %x %x %s\n",
225 	            e->cat,
226 		    pair_first(e->id12), pair_second(e->id12),
227 		    pair_first(e->id34), pair_second(e->id34),
228 		    e->name);
229 	}
230 
231   fflush(f);
232   if (ferror(f))
233     a->warning("Error writing %s", name);
234   fclose(f);
235 
236   if (rename(tmpname, name) < 0)
237     {
238       a->warning("Cannot rename %s to %s: %s", tmpname, name, strerror(errno));
239       unlink(tmpname);
240     }
241   pci_mfree(tmpname);
242 }
243 
244 #else
245 
pci_id_cache_load(struct pci_access * a UNUSED,int flags UNUSED)246 int pci_id_cache_load(struct pci_access *a UNUSED, int flags UNUSED)
247 {
248   a->id_cache_status = 1;
249   return 0;
250 }
251 
pci_id_cache_flush(struct pci_access * a)252 void pci_id_cache_flush(struct pci_access *a)
253 {
254   a->id_cache_status = 0;
255   pci_mfree(a->id_cache_name);
256   a->id_cache_name = NULL;
257 }
258 
259 #endif
260 
261 void
pci_id_cache_dirty(struct pci_access * a)262 pci_id_cache_dirty(struct pci_access *a)
263 {
264   if (a->id_cache_status >= 1)
265     a->id_cache_status = 2;
266 }
267