| // 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; | 
 | } |