162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/* Manage a cache of file names' existence */
362306a36Sopenharmony_ci#include <stdlib.h>
462306a36Sopenharmony_ci#include <unistd.h>
562306a36Sopenharmony_ci#include <string.h>
662306a36Sopenharmony_ci#include <linux/list.h>
762306a36Sopenharmony_ci#include "fncache.h"
862306a36Sopenharmony_ci
962306a36Sopenharmony_cistruct fncache {
1062306a36Sopenharmony_ci	struct hlist_node nd;
1162306a36Sopenharmony_ci	bool res;
1262306a36Sopenharmony_ci	char name[];
1362306a36Sopenharmony_ci};
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#define FNHSIZE 61
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_cistatic struct hlist_head fncache_hash[FNHSIZE];
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ciunsigned shash(const unsigned char *s)
2062306a36Sopenharmony_ci{
2162306a36Sopenharmony_ci	unsigned h = 0;
2262306a36Sopenharmony_ci	while (*s)
2362306a36Sopenharmony_ci		h = 65599 * h + *s++;
2462306a36Sopenharmony_ci	return h ^ (h >> 16);
2562306a36Sopenharmony_ci}
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic bool lookup_fncache(const char *name, bool *res)
2862306a36Sopenharmony_ci{
2962306a36Sopenharmony_ci	int h = shash((const unsigned char *)name) % FNHSIZE;
3062306a36Sopenharmony_ci	struct fncache *n;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	hlist_for_each_entry(n, &fncache_hash[h], nd) {
3362306a36Sopenharmony_ci		if (!strcmp(n->name, name)) {
3462306a36Sopenharmony_ci			*res = n->res;
3562306a36Sopenharmony_ci			return true;
3662306a36Sopenharmony_ci		}
3762306a36Sopenharmony_ci	}
3862306a36Sopenharmony_ci	return false;
3962306a36Sopenharmony_ci}
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic void update_fncache(const char *name, bool res)
4262306a36Sopenharmony_ci{
4362306a36Sopenharmony_ci	struct fncache *n = malloc(sizeof(struct fncache) + strlen(name) + 1);
4462306a36Sopenharmony_ci	int h = shash((const unsigned char *)name) % FNHSIZE;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	if (!n)
4762306a36Sopenharmony_ci		return;
4862306a36Sopenharmony_ci	strcpy(n->name, name);
4962306a36Sopenharmony_ci	n->res = res;
5062306a36Sopenharmony_ci	hlist_add_head(&n->nd, &fncache_hash[h]);
5162306a36Sopenharmony_ci}
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci/* No LRU, only use when bounded in some other way. */
5462306a36Sopenharmony_cibool file_available(const char *name)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	bool res;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	if (lookup_fncache(name, &res))
5962306a36Sopenharmony_ci		return res;
6062306a36Sopenharmony_ci	res = access(name, R_OK) == 0;
6162306a36Sopenharmony_ci	update_fncache(name, res);
6262306a36Sopenharmony_ci	return res;
6362306a36Sopenharmony_ci}
64