
/*  double/mkdble.c	create DouBle device/file  24 Dec 1993
 *
 *  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.
 */

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

extern int	fd;
struct stat	stbuf;
char	buffer[DB_MAXSZ];
extern struct db_header	dbh;
struct dble_device	db;

/* Find number of blocks of size "size" needed to store "total" bytes */
u_long get_size(long total, int size)
{

return (total+size-1)/size;
}

creation_error()
{
	fprintf(stderr, "\n Creation error\n");
	exit(1);
}

main (argc, argv)
int argc;
char **argv;
{
char *dev;
struct BAT *b;
int i,j;
u_long size = 0, atol();
float factor, atof();

dbh.Magic = DB_MAGIC;
/* Default values */
factor = 2.0;
db.isize = BLOCK_SIZE;
db.osize = DB_MAXSZ;
db.iblocks = db.oblocks = 0;
db.code = 11;		/* Default compression is LZV */

if(argc < 3 || argc > 7) usage(argv[0]);
dev = argv[1];
size = atol(argv[2]);
if((fd = open(dev, O_RDWR|O_CREAT|O_TRUNC, S_IRUSR| S_IWUSR))<0) usage(argv[0]);
if(size < 10) usage(argv[0]); /* is it a joke ? */
if(argc>3)
	factor = atof(argv[3]);
if(argc>4)
	db.code  = atoi(argv[4]);
if(argc>5)
	db.isize = atoi(argv[5]);
if(argc>6)
	db.osize = atoi(argv[6]);

if(factor<1 || factor>10) usage(argv[0]);

if(db.code && (db.code < 10 || db.code > 13) && db.code != 1)
	usage(argv[0]);

if(db.isize < 256 || db.isize>BLOCK_SIZE) usage(argv[0]);
if(db.osize<db.isize || db.osize>DB_MAXSZ) usage(argv[0]);

for(i = db.isize; !(i&1); i>>=1);
if(i!=1) {
	fprintf(stderr,"Block size must be a power of 2\n");
	usage(argv[0]);
}
if(db.osize%db.isize) {
	fprintf(stderr,"Cluster size must a multiple of block size\n");
	usage(argv[0]);
}

db.iblocks = size*1024/db.isize; /* including header, bitmap and blockmap */

fstat(fd, &stbuf);
db.addr_bitmap = 1;
if(S_ISREG(stbuf.st_mode)) {
	if(!(i = stbuf.st_blksize))
		i = BLOCK_SIZE;
	size = get_size(db.iblocks*db.isize, i);
/* First block of bmap is kept in memory, not written to disk */
	db.addr_bitmap += get_size(size*sizeof(u_long), db.isize) - 1;
}

size = get_size(db.iblocks, 8);	/* Bytes bitmap */
db.addr_BAT = db.addr_bitmap + get_size(size, db.isize);

db.ratio = db.osize/db.isize;
db.BATsize = sizeof(struct BAT)+sizeof(u_long)*(db.ratio-1);
i = db.addr_BAT+1;
do {
	db.addr_blocks = i;
	db.oblocks=(db.iblocks-i)*factor/db.ratio;
	i=db.addr_BAT+ get_size(db.oblocks, db.isize/db.BATsize);
} while(abs(i-db.addr_blocks)>1);
db.addr_blocks = i;
db.bitmap = malloc(db.isize);
db.bitmap_mod = 0;
db.bitmap_pos = -1;
if(db.bitmap == NULL) {
	fprintf(stderr, "\n Memory allocation error\n");
	exit(1);
}

dbh.iblocks = db.iblocks;
dbh.oblocks = db.oblocks;
dbh.isize = db.isize;
dbh.osize = db.osize;
dbh.addr_bitmap = db.addr_bitmap;
dbh.addr_BAT = db.addr_BAT;
dbh.addr_blocks = db.addr_blocks;
dbh.BATsize = db.BATsize;
dbh.ratio = db.ratio;
dbh.version = 0x0004;
dbh.clean = 1;
dbh.code = db.code; 
memset(buffer, 0, db.isize);
memcpy(buffer, &dbh, sizeof(dbh));

/* Write header */
if(!db_rw_block(&db, WRITE, 0L, buffer, db.isize))
	creation_error();

/* Create bmap && bitmap (not up to date yet) */
memset(buffer, 0, db.isize);
for(size = 1; size < db.addr_BAT; size++) {
	if(!db_rw_block(&db, WRITE, size, buffer, db.isize))
		creation_error();
	if(!(size&0x7f)) putchar('.'), fflush(stdout);
}

/* Write empty BAT */
for(i = 0; i < (db.isize/db.BATsize); i++) {
	b = (struct BAT *)(buffer+i*db.BATsize);
	b->type = -1;
}
for(; size<db.addr_blocks; size++) {
	if(!(size&127)) putchar('.'), fflush(stdout);
	if(!db_rw_block(&db, WRITE, size, buffer, db.isize))
		creation_error();
	if(!(size&0x7f)) putchar('.'), fflush(stdout);
}
putchar('\n');

/* Update bitmap */
/* Clear blocks already in use */
for(size = 0; size < db.addr_blocks; size++) clr_bit(&db, size);
/* Set free blocks */
for(; size < db.iblocks; size++) set_bit(&db, size);
if(db.bitmap_mod && db.bitmap_pos >= 0)
	if(!db_rw_block(&db, WRITE, db.addr_bitmap+db.bitmap_pos, db.bitmap,
	db.isize))
		creation_error();
	
/* create other blocks */
memset(buffer, 0, db.isize);
for(size = db.addr_blocks; size < db.iblocks; size++) {
	if(!db_rw_block(&db, WRITE, size, buffer, db.isize)) {
		fprintf(stderr, "\nError writing block %ld", size);
		creation_error();
	}
	if(!(size&0x7f)) putchar('.'), fflush(stdout);
}
putchar('\n');

size = db.oblocks*db.osize;

db_close(&db, 0);
printf("%ld clusters of %d bytes (%ld K) compression=%2.2f, granularity=%2.1f%%\n",
	db.oblocks, db.osize, size/1024, ((float) size)/((db.iblocks-db.addr_blocks)*db.isize),
	((float) db.isize)/db.osize*100);
printf("Creation Complete\n\n");
}

usage(s) 
char *s;
{
	fprintf(stderr, "\n%s file/device size [factor] [encoding] [block size] [cluster size]\n", s);
	fprintf(stderr, "\tsize: device size in Kb\n");
	fprintf(stderr, "\tfactor: minimum average compression factor (2.0*)\n");
	fprintf(stderr, "\tencoding: default compression algorithm:\n");
	fprintf(stderr, "\t\t0: no compression\n");
	fprintf(stderr, "\t\t1: LZW\n");
	fprintf(stderr, "\t\t10: predictor algorithm\n");
	fprintf(stderr, "\t\t11: LZV*\n");
	fprintf(stderr, "\t\t12: LZRW2\n");
	fprintf(stderr, "\t\t13: LZRW3A\n");
	fprintf(stderr, "\t(No compression can be achieved unless cluster size > block size).\n");
	fprintf(stderr, "\tblock size: [256-%d*]\n", BLOCK_SIZE);
	fprintf(stderr, "\tcluster size: [block size-%d*]\n", DB_MAXSZ);
	fprintf(stderr, "Note: * is the default value\n\n");
	exit(1);
}
