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