| // SPDX-License-Identifier: GPL-2.0-only | |
| /* Manage a cache of file names' existence */ | |
| #include <stdlib.h> | |
| #include <unistd.h> | |
| #include <string.h> | |
| #include <linux/list.h> | |
| #include "fncache.h" | |
| struct fncache { | |
| struct hlist_node nd; | |
| bool res; | |
| char name[]; | |
| }; | |
| #define FNHSIZE 61 | |
| static struct hlist_head fncache_hash[FNHSIZE]; | |
| unsigned shash(const unsigned char *s) | |
| { | |
| unsigned h = 0; | |
| while (*s) | |
| h = 65599 * h + *s++; | |
| return h ^ (h >> 16); | |
| } | |
| static bool lookup_fncache(const char *name, bool *res) | |
| { | |
| int h = shash((const unsigned char *)name) % FNHSIZE; | |
| struct fncache *n; | |
| hlist_for_each_entry(n, &fncache_hash[h], nd) { | |
| if (!strcmp(n->name, name)) { | |
| *res = n->res; | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| static void update_fncache(const char *name, bool res) | |
| { | |
| struct fncache *n = malloc(sizeof(struct fncache) + strlen(name) + 1); | |
| int h = shash((const unsigned char *)name) % FNHSIZE; | |
| if (!n) | |
| return; | |
| strcpy(n->name, name); | |
| n->res = res; | |
| hlist_add_head(&n->nd, &fncache_hash[h]); | |
| } | |
| /* No LRU, only use when bounded in some other way. */ | |
| bool file_available(const char *name) | |
| { | |
| bool res; | |
| if (lookup_fncache(name, &res)) | |
| return res; | |
| res = access(name, R_OK) == 0; | |
| update_fncache(name, res); | |
| return res; | |
| } |