/*
 *	This file implements some sort of filename -> un_index conversion
 *	cache. Speeds up for real FS's (where the inode numbers are NOT
 *	junk) and slows down for the silly FS's where the inode numbers
 *	are junk.
 *
 *	12/12/93, kay roemer.
 */

#include "config.h"
#include "kerbind.h"
#include "file.h"
#include "atarierr.h"
#include "sockerr.h"

#define CACHE_ENTRIES	10

struct lookup_cache {
	short	valid;
#define CACHE_VALID	0
#define CACHE_DIRTY	1
	short	dev;
	long	inode;
	long	stamp;
#define MK_STAMP(time, date)	((((long)(date)) << 16) | (time))
	long	un_index;
};

static struct lookup_cache f_cache[CACHE_ENTRIES];

long
un_cache_lookup (name, index)
	char *name;
	long *index;
{
	XATTR attr;
	short dirty_idx, i;
	long r, stamp, fd;
	static short last_deleted = 0;

	r = f_xattr (1, name, &attr);
	if (r != 0) {
		DEBUG (("unix: un_cache_lookup: Fxattr(%s) -> %ld", name, r));
		return r;
	}
	dirty_idx = -1;
	stamp = MK_STAMP (attr.mtime, attr.mdate);
	for (i = 0; i < CACHE_ENTRIES; ++i) {
		if (f_cache[i].valid == CACHE_DIRTY) {
			dirty_idx = i;
			continue;
		}
		if (f_cache[i].inode == attr.index &&
		    f_cache[i].dev == attr.dev) {
			if (f_cache[i].stamp == stamp) {
				*index = f_cache[i].un_index;
				return 0;
			} else {
				f_cache[i].valid = CACHE_DIRTY;
				dirty_idx = i;
				break;
			}
		}
	}

/* No matching entry found. We drop dirty_entry or some other entry
   in a round robin manner if no dirty entry is left. */

	fd = f_open (name, O_RDWR);
	if (fd < 0) {
		DEBUG (("unix: un_cache_lookup: Fopen(%s) -> %ld", name, fd));
		return fd;
	}
	r = f_read (fd, sizeof (*index), index);
	f_close (fd);
	if (r >= 0 && r != sizeof (*index)) r = EACCDN;
	if (r < 0) {
		DEBUG (("unix: un_namei: Could not read idx from %s", name));
		return r;
	}
	if (dirty_idx == -1) {
		dirty_idx = last_deleted++;
		if (last_deleted >= CACHE_ENTRIES)
			last_deleted = 0;
	}
	f_cache[dirty_idx].dev = attr.dev;
	f_cache[dirty_idx].inode = attr.index;
	f_cache[dirty_idx].stamp = stamp;
	f_cache[dirty_idx].un_index = *index;
	f_cache[dirty_idx].valid = CACHE_VALID;
	return 0;
}

void
un_cache_remove (name)
	char *name;
{
	XATTR attr;
	long r;
	short i;

	r = f_xattr (1, name, &attr);
	if (r) return;
	
	for (i = 0; i < CACHE_ENTRIES; ++i) {
		if (f_cache[i].valid == CACHE_VALID &&
		    f_cache[i].inode == attr.index &&
		    f_cache[i].dev == attr.dev) {
			f_cache[i].valid = CACHE_DIRTY;
			return;
		}
	}
}

void
un_cache_init (void)
{
	int i;

	for (i = 0; i < CACHE_ENTRIES; ++i) {
		f_cache[i].valid = CACHE_DIRTY;
	}
}
