/*  double/tools.c	tools for DouBle device utilities
 *
 *  Copyright (C) 1994 Jean-Marc Verbavatz  <verbavatz@achaz.saclay.cea.fr>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * 8 January 1994
 */

/* this file mimics the behavior of kernel functions in drivers/block/dbtools.c
 * for user programs dealing with compressed devices
 */

#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/fs.h>
#include <linux/double.h>

/* bitmap mask */
u_char mask[8] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 1 };

struct db_header dbh;
int fd;

/* load bitmap for block n */
int get_bitmap(struct dble_device *db, long n)
{
if(db->bitmap_pos==n) return 1;
if(db->bitmap_mod) {
	if(db->bitmap_pos>=0) {
		if(!db_rw_block(db, WRITE, db->bitmap_pos+db->addr_bitmap,
		db->bitmap, db->isize))
			return 0;
	} else {
		fprintf(stderr, "\n bitmap_pos = %ld <0\n", db->bitmap_pos);
		return 0;
	}
	db->bitmap_mod=0;
}
if(n<0) return 1;
if(db_rw_block(db, READ, n+db->addr_bitmap, db->bitmap, db->isize)) {
	db->bitmap_pos = n;
	return 1;
}
return 0;
}

/* load block table for cluster n */
int get_BAT(struct dble_device *db, long n)
{
long i;
/* This is not too efficient; some space is lost as db->isize is not a multiple
   BAT, but this is much easier */
i = n/(db->isize/db->BATsize);
if(db->BAT_pos!=i || n<0) {
	if(db->BAT_mod) {
		if(db->BAT_pos>=0) {
			if(!db_rw_block(db, WRITE, db->BAT_pos+db->addr_BAT,
			db->BAT_block, db->isize))
				return 0;
		} else {
			fprintf(stderr, "\n BAT_pos = %ld <0\n", db->BAT_pos);
			return 0;
		}
		db->BAT_mod=0;
	}
	if(n<0) return 1;
	if(!db_rw_block(db, READ, i+db->addr_BAT, db->BAT_block, db->isize))
		return 0;
	db->BAT_pos = i;
}
return 1;
}

/* Read/Write db->isize bytes from device/file */
int db_rw_block(struct dble_device *db, int cmd, u_long n, u_char *s, int len)
{

if(lseek(fd, n*db->isize, 0L)<0) return 0;
if(cmd==READ) {
	if(read(fd, s, len)==len) return 1;
	else return 0;
} else { /* WRITE */
	if(write(fd, s, len)==len) return 1;
	return 0;
}
}
/*****************************************************************************
 *  bitmap functions:
 * get_newbit allocates a free block 
 * clr_bit clears a bit in bitmap - which means that it's used
 * set_bit sets a bit in bitmap - which means that the block is free
 * get_bit returns the value for block
 *****************************************************************************/

u_long get_newbit(db)
struct dble_device *db;
{
int j, k, l;
u_long i, b;

i = 0;
for(b = 0; ; b++) {
	get_bitmap(db, b);
	for(j = 0; j < db->isize; j++) if((k = db->bitmap[j])!=0) {
		for(l=0x80; l; l>>=1, i++)
			if(k&l) {
				k &= ~l;
				db->bitmap[j] = k;
				db->bitmap_mod = 1;
				i += j<<3;
				return i;
			}
	}
	i += j<<3;
	if(i >= db->iblocks)
		return 0;
}
	
}


void clr_bit(struct dble_device *db, u_long n)
{
u_long i;

i=n/(db->isize<<3);
n=n%(db->isize<<3);
get_bitmap(db, i);
db->bitmap[n>>3] &= ~mask[n&7];
db->bitmap_mod=1;
}


void set_bit(struct dble_device *db, u_long n)
{
u_long i;

i=n/(db->isize<<3);
n=n%(db->isize<<3);
get_bitmap(db, i);
db->bitmap[n>>3] |= mask[n&7];
db->bitmap_mod=1;
}


int get_bit(struct dble_device *db, u_long n)
{
u_long i;

i=n/(db->isize<<3);
n=n%(db->isize<<3);
get_bitmap(db, i);
return (db->bitmap[n>>3] & mask[n&7]);
}

/* close compressed device */
void db_close(struct dble_device *db, int update)
{
if(update) {
	get_bitmap(db, -1);
	get_BAT(db, -1);
	lseek(fd, 0L, 0);
	write(fd, &dbh, sizeof(dbh));
}
close(fd);
}

/* open compressed device */
int db_open(db, s)
struct dble_device *db;
char *s;
{
if((fd = open(s, O_RDWR))<0) return 0;
db->isize = sizeof(struct db_header);
if(!db_rw_block(db, READ, 0L, (u_char *)&dbh, db->isize) ||
   dbh.Magic != DB_MAGIC) {
	fprintf(stderr, "%s: Bad magic number\n", s);
	return 0;
}
db->isize = dbh.isize;
db->osize = dbh.osize;
if(db->osize>DB_MAXSZ) {
	fprintf(stderr, "%s: Bad cluster size\n", s);
	return 0;
}
db->iblocks = dbh.iblocks;
db->oblocks = dbh.oblocks;
db->addr_bitmap = dbh.addr_bitmap;
db->addr_BAT = dbh.addr_BAT;
db->addr_blocks = dbh.addr_blocks;
db->BATsize = dbh.BATsize;
db->ratio = dbh.ratio;
db->clean = dbh.clean;
db->code = dbh.code;
db->bitmap_pos = -1;
db->bitmap_mod = 0;
db->BAT_pos = -1;
db->BAT_mod = 0;

db->bitmap = malloc(db->isize);
db->BAT_block = malloc(db->isize);
if(db->bitmap == NULL || db->BAT_block == NULL) {
	fprintf(stderr, "Allocation error\n");
	return 0;
}
return 1;
}
