18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/* Manage a cache of file names' existence */
38c2ecf20Sopenharmony_ci#include <stdlib.h>
48c2ecf20Sopenharmony_ci#include <unistd.h>
58c2ecf20Sopenharmony_ci#include <string.h>
68c2ecf20Sopenharmony_ci#include <linux/list.h>
78c2ecf20Sopenharmony_ci#include "fncache.h"
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_cistruct fncache {
108c2ecf20Sopenharmony_ci	struct hlist_node nd;
118c2ecf20Sopenharmony_ci	bool res;
128c2ecf20Sopenharmony_ci	char name[];
138c2ecf20Sopenharmony_ci};
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#define FNHSIZE 61
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_cistatic struct hlist_head fncache_hash[FNHSIZE];
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ciunsigned shash(const unsigned char *s)
208c2ecf20Sopenharmony_ci{
218c2ecf20Sopenharmony_ci	unsigned h = 0;
228c2ecf20Sopenharmony_ci	while (*s)
238c2ecf20Sopenharmony_ci		h = 65599 * h + *s++;
248c2ecf20Sopenharmony_ci	return h ^ (h >> 16);
258c2ecf20Sopenharmony_ci}
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic bool lookup_fncache(const char *name, bool *res)
288c2ecf20Sopenharmony_ci{
298c2ecf20Sopenharmony_ci	int h = shash((const unsigned char *)name) % FNHSIZE;
308c2ecf20Sopenharmony_ci	struct fncache *n;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	hlist_for_each_entry(n, &fncache_hash[h], nd) {
338c2ecf20Sopenharmony_ci		if (!strcmp(n->name, name)) {
348c2ecf20Sopenharmony_ci			*res = n->res;
358c2ecf20Sopenharmony_ci			return true;
368c2ecf20Sopenharmony_ci		}
378c2ecf20Sopenharmony_ci	}
388c2ecf20Sopenharmony_ci	return false;
398c2ecf20Sopenharmony_ci}
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic void update_fncache(const char *name, bool res)
428c2ecf20Sopenharmony_ci{
438c2ecf20Sopenharmony_ci	struct fncache *n = malloc(sizeof(struct fncache) + strlen(name) + 1);
448c2ecf20Sopenharmony_ci	int h = shash((const unsigned char *)name) % FNHSIZE;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	if (!n)
478c2ecf20Sopenharmony_ci		return;
488c2ecf20Sopenharmony_ci	strcpy(n->name, name);
498c2ecf20Sopenharmony_ci	n->res = res;
508c2ecf20Sopenharmony_ci	hlist_add_head(&n->nd, &fncache_hash[h]);
518c2ecf20Sopenharmony_ci}
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci/* No LRU, only use when bounded in some other way. */
548c2ecf20Sopenharmony_cibool file_available(const char *name)
558c2ecf20Sopenharmony_ci{
568c2ecf20Sopenharmony_ci	bool res;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	if (lookup_fncache(name, &res))
598c2ecf20Sopenharmony_ci		return res;
608c2ecf20Sopenharmony_ci	res = access(name, R_OK) == 0;
618c2ecf20Sopenharmony_ci	update_fncache(name, res);
628c2ecf20Sopenharmony_ci	return res;
638c2ecf20Sopenharmony_ci}
64