/* ib1_cache.c: a simple cache for blocks (fully associative with LRU 
   replacement)

   Copyright (C) 2000 Maurer IT Systemlösungen KEG

   The Gnome Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public License as
   published by the Free Software Foundation; either version 2 of the
   License, or (at your option) any later version.

   The Gnome Library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public
   License along with the Gnome Library; see the file COPYING.LIB.  If not,
   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.

   Author: Dietmar Maurer <dietmar@maurer-it.com>

*/

#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include "ib1_driver.h"

#include <sys/types.h>
#include <signal.h>

static guint32 cache_access_time = 0;

static gint
ib1_cache_unmap (IB1EFS *efs, IB1CacheEntry *ce)
{
	guint32 buf[128];
	guint32 crc = adler32 (0L, NULL, 0);

	g_return_val_if_fail (ce->block >= efs->head.cb, -1);
	if (lseek(efs->fd, ce->block*512, SEEK_SET) == ce->block*512) {
		memcpy (buf, ce->data, 512);
		crc = adler32 (crc, (guint8 *)buf, 508);
		buf[127] = GUINT32_TO_LE (crc);
		if (efs->head.head.protected&&((EFS*)efs)->driver->encrypted) 
			ib1_encrypt (efs, buf, 512/4);
		write(efs->fd, buf, 512);
		return 0;
	}
	return -1;
}

void
ib1_cache_flush (IB1EFS *efs)
{
	gint i;

	for (i=0;i<IB1_CACHE_SIZE;i++) {
		if (efs->cache[i].dirty) {
			ib1_cache_unmap(efs, &efs->cache[i]);
			efs->cache[i].dirty = FALSE;
		}
	}
}

void
ib1_cache_touch (IB1CacheEntry *ce, gboolean dirty)
{
	cache_access_time++;
	ce->at = cache_access_time;
	if (dirty) ce->dirty = dirty;
}

IB1CacheEntry*
ib1_cache_map (IB1EFS *efs, guint32 block, gboolean noread)
{
	gint i, b;

	b = -1;
	for (i=0;i<IB1_CACHE_SIZE;i++) {
		if (efs->cache[i].block == block) {
			efs->cache[i].at = ++cache_access_time;
			return &efs->cache[i];
		}
		if (efs->cache[i].lock) continue;
		if (b<0) b=i;
		if (efs->cache[i].at<efs->cache[b].at) b = i;
	}

	if (b<0) g_error ("EFS: internal error (cache look)\n");
	
	if (efs->cache[b].dirty) { 
		ib1_cache_unmap(efs, &efs->cache[b]);
		efs->cache[b].dirty = FALSE;
	}
	
	efs->cache[b].at = ++cache_access_time;
	efs->cache[b].block = block;

	if (!noread) {
		guint32 *buf;
		guint32 crc = adler32 (0L, NULL, 0);
	        if (lseek(efs->fd, block*512, SEEK_SET) != block*512) 
			return NULL; 
		if (read(efs->fd, efs->cache[b].data, 512) != 512) return NULL;
		if (efs->head.head.protected&&((EFS*)efs)->driver->encrypted) 
			ib1_decrypt (efs,(guint32 *)efs->cache[b].data,512/4);
		crc = adler32 (crc, efs->cache[b].data, 508);
		buf = (guint32 *)efs->cache[b].data;
		if (crc != GUINT32_FROM_LE (buf[127])) return NULL;
		efs->cache[b].dirty = FALSE;
	} else {
		efs->cache[b].dirty = TRUE;
	}

	return &efs->cache[b];
}

IB1CacheEntry*
ib1_cache_map_clone (IB1EFS *efs, guint32 block)
{
	IB1CacheEntry *ce, *ce1;
	guint32 nb;

	if (!(ce = ib1_cache_map (efs, block, FALSE))) return NULL;
	if (block >= efs->head.cb) return ce;

	if (!(nb = ib1_block_alloc (efs))) return NULL;
	if (!(ce1 = ib1_cache_map (efs, nb, TRUE))) return NULL;

	memcpy (ce1->data, ce->data, 512);
	return ce1;
}


syntax highlighted by Code2HTML, v. 0.9.1