1 // SPDX-License-Identifier: GPL-2.0-only
2 /* Manage a cache of file names' existence */
3 #include <stdlib.h>
4 #include <unistd.h>
5 #include <string.h>
6 #include <linux/list.h>
7 #include "fncache.h"
8 
9 struct fncache {
10 	struct hlist_node nd;
11 	bool res;
12 	char name[];
13 };
14 
15 #define FNHSIZE 61
16 
17 static struct hlist_head fncache_hash[FNHSIZE];
18 
shash(const unsigned char * s)19 unsigned shash(const unsigned char *s)
20 {
21 	unsigned h = 0;
22 	while (*s)
23 		h = 65599 * h + *s++;
24 	return h ^ (h >> 16);
25 }
26 
lookup_fncache(const char * name,bool * res)27 static bool lookup_fncache(const char *name, bool *res)
28 {
29 	int h = shash((const unsigned char *)name) % FNHSIZE;
30 	struct fncache *n;
31 
32 	hlist_for_each_entry(n, &fncache_hash[h], nd) {
33 		if (!strcmp(n->name, name)) {
34 			*res = n->res;
35 			return true;
36 		}
37 	}
38 	return false;
39 }
40 
update_fncache(const char * name,bool res)41 static void update_fncache(const char *name, bool res)
42 {
43 	struct fncache *n = malloc(sizeof(struct fncache) + strlen(name) + 1);
44 	int h = shash((const unsigned char *)name) % FNHSIZE;
45 
46 	if (!n)
47 		return;
48 	strcpy(n->name, name);
49 	n->res = res;
50 	hlist_add_head(&n->nd, &fncache_hash[h]);
51 }
52 
53 /* No LRU, only use when bounded in some other way. */
file_available(const char * name)54 bool file_available(const char *name)
55 {
56 	bool res;
57 
58 	if (lookup_fncache(name, &res))
59 		return res;
60 	res = access(name, R_OK) == 0;
61 	update_fncache(name, res);
62 	return res;
63 }
64