/*
**       COWLOOP block device driver (2.6 kernel compliant)
** =======================================================================
** Read-write loop-driver with copy-on-write functionality.
**
** Synopsis:
**
**     modprobe cowloop [maxcows=..] [rdofile=..... cowfile=.... [option=r]]
**
** Definition of number of configured cowdevices:
**   maxcows=	number of configured cowdevices (default: 16)
** (do not confuse this with MAXCOWS: absolute maximum as compiled)
**
** One pair of filenames can be supplied during insmod/modprobe to open
** the first cowdevice:
**   rdofile=	read-only file (or filesystem)
**   cowfile=	storage-space for modified blocks of read-only file(system)
**   option=r	repair cowfile automatically if it appears to be dirty
**
** Other cowdevices can be activated via the command "cowdev"
** whenever the cowloop-driver is loaded.
**
** The read-only file may be of type 'regular' or 'block-device'.
**
** The cowfile must be of type 'regular'.
** If an existing regular file is used as cowfile, its contents will be
** used again for the current read-only file. When the cowfile has not been
** closed properly during a previous session (i.e. rmmod cowloop), the
** cowloop-driver refuses to open it unless the parameter "option=r" is
** specified.
**
** Layout of cowfile:
**
** 	+-----------------------------+
**	|       cow head block        |   MAPUNIT bytes
**	|-----------------------------|
**	|                             |   MAPUNIT bytes
**	|---                       ---|
**	|                             |   MAPUNIT bytes
**	|---                       ---|
**	|      used-block bitmap      |   MAPUNIT bytes
**	|-----------------------------|
**	|  gap to align start-offset  |
**	|        to 4K multiple       |
**	|-----------------------------|  <---- start-offset cow blocks
**	|                             |
**      |    written cow blocks       |   MAPUNIT bytes
**      |          .....              |
**
** 	cowhead block:
**   	  - contains general info about the rdofile which is related
** 	    to this cowfile
**
** 	used-block bitmap:
** 	  - contains one bit per block with a size of MAPUNIT bytes
** 	  - bit-value '1' = block has been written on cow
** 	              '0' = block unused on cow
** 	  - total bitmap rounded to multiples of MAPUNIT
**
** ============================================================================
** Author:             Gerlof Langeveld - AT Computing (March 2003)
** Current maintainer: Hendrik-Jan Thomassen - AT Computing (Summer 2006)
** Email:              hjt@ATComputing.nl
** ----------------------------------------------------------------------------
** Copyright (C) 2003-2009 AT Consultancy
**
** 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, 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
** ----------------------------------------------------------------------------
**
** Major modifications:
**
** 	200405	Ported to kernel-version 2.6		Hendrik-Jan Thomassen
**	200405	Added cowhead to cowfile to garantee
**		consistency with read-only file		Gerlof Langeveld
**	200405	Postponed flushing of bitmaps to improve
**		performance. 				Gerlof Langeveld
**	200405	Inline recovery for dirty cowfiles.	Gerlof Langeveld
**	200502	Redesign to support more cowdevices.	Gerlof Langeveld
**	200502	Support devices/file > 2 Gbytes.	Gerlof Langeveld
**	200507	Check for free space to expand cowfile.	Gerlof Langeveld
**	200902  Upgrade for kernel 2.6.28               Hendrik-Jan Thomassen
**
** Inspired by
**    loop.c  by Theodore Ts'o and
**    cloop.c by Paul `Rusty' Russell & Klaus Knopper.
**
** Design-considerations:
**
**   For the first experiments with the cowloop-driver, the request-queue
**   made use of the do_generic_file_read() which worked fine except
**   in combination with the cloop-driver; that combination
**   resulted in a non-interruptible hangup of the system during
**   heavy load. Other experiments using the `make_request' interface also
**   resulted in unpredictable system hangups (with proper use of spinlocks).
**
**   To overcome these problems, the cowloop-driver starts a kernel-thread
**   for every active cowdevice.
**   All read- and write-request on the read-only file and copy-on-write file
**   are handled in the context of that thread.
**   A scheme has been designed to wakeup the kernel-thread as
**   soon as I/O-requests are available in the request-queue; this thread
**   handles the requests one-by-one by calling the proper read- or
**   write-function related to the open read-only file or copy-on-write file.
**   When all pending requests have been handled, the kernel-thread goes
**   back to sleep-state.
**   This approach requires some additional context-switches; however the
**   performance loss during heavy I/O is less than 3%.
**
** -------------------------------------------------------------------------*/
/* The following is the cowloop package version number. It must be
   identical to the content of the include-file "version.h" that is
   used in all supporting utilities:                                  */
char revision[] = "$Revision: 3.1 $"; /* cowlo_init_module() has
			     assumptions about this string's format   */

/* Note that the following numbers are *not* the cowloop package version
   numbers, but separate revision history numbers to track the
   modifications of this particular source file:                      */
/* $Log: cowloop.c,v $
**
** Revision 1.30  2009/02/08 hjt
** Integrated earlier fixes
** Upgraded to kernel 2.6.28 (thanks Jerome Poulin)
**
** Revision 1.29  2006/12/03 22:12:00  hjt
** changed 'cowdevlock' from spinlock to semaphore, to avoid
** "scheduling while atomic". Contributed by Juergen Christ.
** Added version.h again
**
** Revision 1.28  2006/08/16 16:00:00  hjt
** malloc each individual cowloopdevice struct separately
**
** Revision 1.27  2006/03/14 14:57:03  root
** Removed include version.h
**
** Revision 1.26  2005/08/08 11:22:48  root
** Implement possibility to close a cow file or reopen a cowfile read-only.
**
** Revision 1.25  2005/08/03 14:00:39  root
** Added modinfo info to driver.
**
** Revision 1.24  2005/07/21 06:14:53  root
** Cosmetic changes source code.
**
** Revision 1.23  2005/07/20 13:07:32  root
** Supply ioctl to write watchdog program to react on lack of cowfile space.
**
** Revision 1.22  2005/07/20 07:53:34  root
** Regular verification of free space in filesystem holding the cowfile
** (give warnings whenever space is almost exhausted).
** Terminology change: checksum renamed to fingerprint.
**
** Revision 1.21  2005/07/19 09:21:52  root
** Removing maximum limit of 16 Gb per cowdevice.
**
** Revision 1.20  2005/07/19 07:50:33  root
** Minor bugfixes and cosmetic changes.
**
** Revision 1.19  2005/06/10 12:29:55  root
** Removed lock/unlock operation from cowlo_open().
**
** Revision 1.18  2005/05/09 12:56:26  root
** Allow a cowdevice to be open more than once
** (needed for support of ReiserFS and XFS).
**
** Revision 1.17  2005/03/17 14:36:16  root
** Fixed some license issues.
**
** Revision 1.16  2005/03/07 14:42:05  root
** Only allow one parallel open per cowdevice.
**
** Revision 1.15  2005/02/18 11:52:04  gerlof
** Redesign to support more than one cowdevice > 2 Gb space.
**
** Revision 1.14  2004/08/17 14:19:16  gerlof
** Modified output of /proc/cowloop.
**
** Revision 1.13  2004/08/16 07:21:10  gerlof
** Separate statistical counter for read on rdofile and cowfile.
**
** Revision 1.12  2004/08/11 06:52:11  gerlof
** Modified messages.
**
** Revision 1.11  2004/08/11 06:44:11  gerlof
** Modified log messages.
**
** Revision 1.10  2004/08/10 12:27:27  gerlof
** Cosmetic changes.
**
** Revision 1.9  2004/08/09 11:43:37  gerlof
** Removed double definition of major number (COWMAJOR).
**
** Revision 1.8  2004/08/09 08:03:39  gerlof
** Cleanup of messages.
**
** Revision 1.7  2004/05/27 06:37:33  gerlof
** Modified /proc message.
**
** Revision 1.6  2004/05/26 21:23:28  gerlof
** Modified /proc output.
**
** Revision 1.5  2004/05/26 13:23:34  gerlof
** Support cowsync to force flushing the bitmaps and cowhead.
**
** Revision 1.4  2004/05/26 11:11:10  gerlof
** Updated the comment to the actual situation.
**
** Revision 1.3  2004/05/26 10:50:00  gerlof
** Implemented recovery-option.
**
** Revision 1.2  2004/05/25 15:14:41  gerlof
** Modified bitmap flushing strategy.
**
*/

#define COWMAJOR	241

// #define COWDEBUG

#ifdef 	COWDEBUG
#define DEBUGP		printk
#define DCOW		KERN_ALERT
#else
#define DEBUGP(format, x...)
#endif

#include <linux/types.h>
#include <linux/autoconf.h>
#ifndef AUTOCONF_INCLUDED
#include <linux/config.h>
#endif
#include <linux/module.h>
#include <linux/version.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/file.h>
#include <linux/stat.h>
#include <linux/vmalloc.h>
#include <linux/slab.h>
#include <linux/semaphore.h>
#include <asm/uaccess.h>
#include <linux/proc_fs.h>
#include <linux/blkdev.h>
#include <linux/buffer_head.h>
#include <linux/hdreg.h>
#include <linux/genhd.h>
#include <linux/statfs.h>

#include "cowloop.h"

MODULE_LICENSE("GPL");
/* MODULE_AUTHOR("Gerlof Langeveld <gerlof@ATComputing.nl>");     obsolete address */
MODULE_AUTHOR("Hendrik-Jan Thomassen <hjt@ATComputing.nl>"); /* current maintainer */
MODULE_DESCRIPTION("Copy-on-write loop driver");
MODULE_PARM_DESC(maxcows, " Number of configured cowdevices (default 16)");
MODULE_PARM_DESC(rdofile, " Read-only file for /dev/cow/0");
MODULE_PARM_DESC(cowfile, " Cowfile for /dev/cow/0");
MODULE_PARM_DESC(option, "  Repair cowfile if inconsistent: option=r");

#define DEVICE_NAME	"cow"

#define	DFLCOWS		16		/* default cowloop devices	*/

static int maxcows = DFLCOWS;
module_param(maxcows, int, 0);
static char *rdofile = "";
module_param(rdofile, charp, 0);
static char *cowfile = "";
module_param(cowfile, charp, 0);
static char *option = "";
module_param(option, charp, 0);

/*
** per cowdevice several bitmap chunks are allowed of MAPCHUNKSZ each
**
** each bitmap chunk can describe MAPCHUNKSZ * 8 * MAPUNIT bytes of data
** suppose:
**	MAPCHUNKSZ 4096 and MAPUNIT 1024 --> 4096 * 8 * 1024 = 32 Mb per chunk
*/
#define	MAPCHUNKSZ	4096	/* #bytes per bitmap chunk  (do not change)  */

#define SPCMINBLK	100	/* space threshold to give warning messages  */
#define SPCDFLINTVL	16	/* once every SPCDFLINTVL writes to cowfile, */
				/* available space in filesystem is checked  */

#define	CALCMAP(x)	((x)/(MAPCHUNKSZ*8))
#define	CALCBYTE(x)	(((x)%(MAPCHUNKSZ*8))>>3)
#define	CALCBIT(x)	((x)&7)

#define ALLCOW		1
#define ALLRDO		2
#define MIXEDUP		3

static char	allzeroes[MAPUNIT];

/*
** administration per cowdevice (pair of cowfile/rdofile)
*/

/* bit-values for state */
#define	COWDEVOPEN	0x01	/* cowdevice opened                          */
#define	COWRWCOWOPEN	0x02	/* cowfile opened read-write                 */
#define	COWRDCOWOPEN	0x04	/* cowfile opened read-only                  */
#define	COWWATCHDOG	0x08 	/* ioctl for watchdog cowfile space active   */

#define	COWCOWOPEN	(COWRWCOWOPEN|COWRDCOWOPEN)

struct cowloop_device
{
	/*
	** current status
	*/
	int		state;			/* bit-values (see above)    */
	int		opencnt;		/* # opens for cowdevice     */

        /*
	** open file pointers
	*/
        struct file  	*rdofp,   *cowfp;	/* open file pointers        */
	char		*rdoname, *cowname;	/* file names                */

	/*
	** request queue administration
	*/
	struct request_queue	*rqueue;
	spinlock_t		rqlock;
	struct gendisk		*gd;

	/*
	** administration about read-only file
	*/
	unsigned int	     numblocks;	/* # blocks input file in MAPUNIT    */
	unsigned int	     blocksz;   /* minimum unit to access this dev   */
	unsigned long	     fingerprint; /* fingerprint of current rdofile  */
	struct block_device  *belowdev;	/* block device below us             */
	struct gendisk       *belowgd;  /* gendisk for blk dev below us      */
	struct request_queue *belowq;	/* req. queue of blk dev below us    */

	/*
	** bitmap administration to register which blocks are modified
	*/
	long int	mapsize;	/* total size of bitmap (bytes)      */
	long int	mapremain;	/* remaining bytes in last bitmap    */
	int		mapcount;       /* number of bitmaps in use          */
	char 		**mapcache;	/* area with pointers to bitmaps     */

	char		*iobuf;		/* databuffer of MAPUNIT bytes       */
	struct cowhead	*cowhead;	/* buffer containing cowhead         */

	/*
	** administration for interface with the kernel-thread
	*/
	int		pid;		/* pid==0: no thread available       */
	struct request	*req;		/* request to be handled now         */
	wait_queue_head_t waitq;	/* wait-Q: thread waits for work     */
	char		closedown;	/* boolean: thread exit required     */
	char		qfilled;	/* boolean: I/O request pending      */
	char		iobusy;		/* boolean: req under treatment      */

	/*
	** administration to keep track of free space in cowfile filesystem
	*/
	unsigned long	blksize;	/* block size of fs (bytes)          */
	unsigned long	blktotal;	/* recent total space in fs (blocks) */
	unsigned long	blkavail;	/* recent free  space in fs (blocks) */

	wait_queue_head_t watchq;	/* wait-Q: watcher awaits threshold  */
	unsigned long	watchthresh;	/* threshold of watcher (blocks)     */

	/*
	** statistical counters
	*/
	unsigned long	rdoreads;	/* number of  read-actions rdo       */
	unsigned long	cowreads;	/* number of  read-actions cow       */
	unsigned long	cowwrites;	/* number of write-actions           */
	unsigned long	nrcowblocks;	/* number of blocks in use on cow    */
};

static struct cowloop_device	**cowdevall;	/* ptr to ptrs to all cowdevices */
static struct semaphore 	cowdevlock;	/* generic lock for cowdevs      */

static struct gendisk		*cowctlgd;	/* gendisk control channel       */
static spinlock_t		cowctlrqlock;   /* for req.q. of ctrl. channel   */

/*
** private directory /proc/cow
*/
struct proc_dir_entry	*cowlo_procdir;

/*
** function prototypes
*/
static long int cowlo_do_request (struct request *req);
static void	cowlo_sync       (void);
static int	cowlo_checkio    (struct cowloop_device *,         int, loff_t);
static int	cowlo_readmix    (struct cowloop_device *, void *, int, loff_t);
static int	cowlo_writemix   (struct cowloop_device *, void *, int, loff_t);
static long int cowlo_readrdo    (struct cowloop_device *, void *, int, loff_t);
static long int cowlo_readcow    (struct cowloop_device *, void *, int, loff_t);
static long int cowlo_readcowraw (struct cowloop_device *, void *, int, loff_t);
static long int cowlo_writecow   (struct cowloop_device *, void *, int, loff_t);
static long int cowlo_writecowraw(struct cowloop_device *, void *, int, loff_t);
static int      cowlo_ioctl      (struct block_device *, fmode_t,
           			 		unsigned int, unsigned long);
static int	cowlo_makepair    (struct cowpair __user *);
static int	cowlo_removepair  (unsigned long  __user *);
static int	cowlo_watch       (struct cowpair __user *);
static int	cowlo_cowctl      (unsigned long  __user *, int);
static int	cowlo_openpair    (char *, char *, int, int);
static int 	cowlo_closepair   (struct cowloop_device *);
static int	cowlo_openrdo     (struct cowloop_device *, char *);
static int	cowlo_opencow     (struct cowloop_device *, char *, int);
static void	cowlo_undo_openrdo(struct cowloop_device *);
static void	cowlo_undo_opencow(struct cowloop_device *);

/*****************************************************************************/
/* System call handling                                                      */
/*****************************************************************************/

/*
** handle system call open()/mount()
**
** returns:
** 	0   - okay
**    < 0   - error value
*/
static int cowlo_open(struct block_device *bdev, fmode_t mode)
{
	struct inode *inode = bdev->bd_inode;

	if (!inode)
		return -EINVAL;

	if (imajor(inode) != COWMAJOR) {
		printk(KERN_WARNING
		       "cowloop - unexpected major %d\n", imajor(inode));
		return -ENODEV;
	}

	switch (iminor(inode)) {
	   case COWCTL:
		DEBUGP(DCOW"cowloop - open %d control\n", COWCTL);
		break;

	   default:
		DEBUGP(DCOW"cowloop - open minor %d\n", iminor(inode));

		if ( iminor(inode) >= maxcows )
			return -ENODEV;

		if ( !((cowdevall[iminor(inode)])->state & COWDEVOPEN) )
			return -ENODEV;

		(cowdevall[iminor(inode)])->opencnt++;
	}

	return 0;
}

/*
** handle system call close()/umount()
**
** returns:
** 	0   - okay
*/
static int cowlo_release(struct gendisk *gd, fmode_t mode)
{
	struct block_device *bdev;
	struct inode *inode;

	bdev = bdget_disk(gd, 0);
	inode = bdev->bd_inode;
	if (!inode)
		return 0;

	DEBUGP(DCOW"cowloop - release (close) minor %d\n", iminor(inode));

	if ( iminor(inode) != COWCTL)
		(cowdevall[iminor(inode)])->opencnt--;

	return 0;
}

/*
** handle system call ioctl()
**
** returns:
** 	0   - okay
**    < 0   - error value
*/
static int cowlo_ioctl(struct block_device *bdev, fmode_t mode,
		       unsigned int cmd, unsigned long arg)
{
	struct hd_geometry	geo;
	struct inode *inode = bdev->bd_inode;

	DEBUGP(DCOW "cowloop - ioctl cmd %x\n", cmd);

	switch ( iminor(inode) ) {

	   /*
	   ** allowed via control device only
	   */
	   case COWCTL:
		switch (cmd) {
		   /*
		   ** write all bitmap chunks and cowheaders to cowfiles
		   */
		   case COWSYNC:
			down(&cowdevlock);
			cowlo_sync();
			up(&cowdevlock);
			return 0;

		   /*
		   ** open a new cowdevice (pair of rdofile/cowfile)
		   */
		   case COWMKPAIR:
			return cowlo_makepair((void __user *)arg);

		   /*
		   ** close a cowdevice (pair of rdofile/cowfile)
		   */
		   case COWRMPAIR:
			return cowlo_removepair((void __user *)arg);

		   /*
		   ** watch free space of filesystem containing cowfile
		   */
		   case COWWATCH:
			return cowlo_watch((void __user *)arg);

		   /*
		   ** close cowfile for active device
		   */
		   case COWCLOSE:
			return cowlo_cowctl((void __user *)arg, COWCLOSE);

		   /*
		   ** reopen cowfile read-only for active device
		   */
		   case COWRDOPEN:
			return cowlo_cowctl((void __user *)arg, COWRDOPEN);

		   default:
			return -EINVAL;
		} /* end of switch on command */

	   /*
	   ** allowed for any other cowdevice
	   */
	   default:
		switch (cmd) {
		   /*
		   ** HDIO_GETGEO must be supported for fdisk, etc
		   */
		   case HDIO_GETGEO:
			geo.cylinders = 0;
			geo.heads     = 0;
			geo.sectors   = 0;

			if (copy_to_user((void __user *)arg, &geo, sizeof geo))
				return -EFAULT;
			return 0;

		   default:
			return -EINVAL;
		} /* end of switch on ioctl-cmd code parameter */
	} /* end of switch on minor number */
}

static struct block_device_operations cowlo_fops =
{
	.owner	 =     THIS_MODULE,
        .open    =     cowlo_open,	/* called upon open  */
        .release =     cowlo_release,	/* called upon close */
        .ioctl   =     cowlo_ioctl,     /* called upon ioctl */
};

/*
** handle ioctl-command COWMKPAIR:
**	open a new cowdevice (pair of rdofile/cowfile) on-the-fly
**
** returns:
** 	0   - okay
**    < 0   - error value
*/
static int
cowlo_makepair(struct cowpair __user *arg)
{
	int		i, rv=0;
	struct cowpair	cowpair;
	unsigned char	*cowpath;
	unsigned char	*rdopath;

	/*
	** retrieve info about pathnames
	*/
	if ( copy_from_user(&cowpair, arg, sizeof cowpair) )
		return -EFAULT;

	if ( (MAJOR(cowpair.device) != COWMAJOR) && (cowpair.device != ANYDEV) )
		return -EINVAL;

	if ( (MINOR(cowpair.device) >= maxcows)  && (cowpair.device != ANYDEV) )
		return -EINVAL;

	/*
	** retrieve pathname strings
	*/
	if ( (cowpair.cowflen > PATH_MAX) || (cowpair.rdoflen > PATH_MAX) )
		return -ENAMETOOLONG;

	if ( !(cowpath = kmalloc(cowpair.cowflen+1, GFP_KERNEL)) )
		return -ENOMEM;

	if ( copy_from_user(cowpath, (void __user *)cowpair.cowfile,
	                                            cowpair.cowflen) ) {
		kfree(cowpath);
		return -EFAULT;
	}
	*(cowpath+cowpair.cowflen) = 0;

	if ( !(rdopath = kmalloc(cowpair.rdoflen+1, GFP_KERNEL)) ) {
		kfree(cowpath);
		return -ENOMEM;
	}

	if ( copy_from_user(rdopath, (void __user *)cowpair.rdofile,
	                                            cowpair.rdoflen) ) {
		kfree(rdopath);
		kfree(cowpath);
		return -EFAULT;
	}
	*(rdopath+cowpair.rdoflen) = 0;

	/*
	** open new cowdevice
	*/
	if ( cowpair.device == ANYDEV) {
		/*
		** search first unused minor
		*/
		for (i=0, rv=-EBUSY; i < maxcows; i++) {
			if ( !((cowdevall[i])->state & COWDEVOPEN) ) {
				rv = cowlo_openpair(rdopath, cowpath, 0, i);
				break;
			}
		}

		if (rv) { 		/* open failed? */
			kfree(rdopath);
			kfree(cowpath);
			return rv;
		}

		/*
		** return newly allocated cowdevice to user space
		*/
		cowpair.device = MKDEV(COWMAJOR, i);

		if ( copy_to_user(arg, &cowpair, sizeof cowpair)) {
			kfree(rdopath);
			kfree(cowpath);
			return -EFAULT;
		}
	} else { 		/* specific minor requested */
		if ( (rv = cowlo_openpair(rdopath, cowpath, 0,
						MINOR(cowpair.device)))) {
			kfree(rdopath);
			kfree(cowpath);
			return rv;
		}
	}

	return 0;
}

/*
** handle ioctl-command COWRMPAIR:
** 	deactivate an existing cowdevice (pair of rdofile/cowfile) on-the-fly
**
** returns:
** 	0   - okay
**    < 0   - error value
*/
static int
cowlo_removepair(unsigned long __user *arg)
{
	unsigned long		cowdevice;
	struct cowloop_device	*cowdev;

	/*
	** retrieve info about device to be removed
	*/
	if ( copy_from_user(&cowdevice, arg, sizeof cowdevice))
		return -EFAULT;

	/*
	** verify major-minor number
	*/
	if ( MAJOR(cowdevice) != COWMAJOR)
		return -EINVAL;

	if ( MINOR(cowdevice) >= maxcows)
		return -EINVAL;

	cowdev = cowdevall[MINOR(cowdevice)];

	if ( !(cowdev->state & COWDEVOPEN) )
		return -ENODEV;

	/*
	** synchronize bitmaps and close cowdevice
	*/
	if (cowdev->state & COWRWCOWOPEN) {
		down(&cowdevlock);
		cowlo_sync();
		up(&cowdevlock);
	}

	return cowlo_closepair(cowdev);
}

/*
** handle ioctl-command COWWATCH:
**	watch the free space of the filesystem containing a cowfile
**      of an open cowdevice
**
** returns:
** 	0   - okay
**    < 0   - error value
*/
static int
cowlo_watch(struct cowpair __user *arg)
{
	struct cowloop_device	*cowdev;
	struct cowwatch		cowwatch;

	/*
	** retrieve structure holding info
	*/
	if ( copy_from_user(&cowwatch, arg, sizeof cowwatch))
		return -EFAULT;

	/*
	** verify if cowdevice exists and is currently open
	*/
	if ( MINOR(cowwatch.device) >= maxcows)
		return -EINVAL;

	cowdev = cowdevall[MINOR(cowwatch.device)];

	if ( !(cowdev->state & COWDEVOPEN) )
		return -ENODEV;

	/*
	** if the WATCHWAIT-option is set, wait until the indicated
	** threshold is reached (only one waiter allowed)
	*/
	if (cowwatch.flags & WATCHWAIT) {
		/*
		** check if already another waiter active
		** for this cowdevice
		*/
		if (cowdev->state & COWWATCHDOG)
			return -EAGAIN;

		cowdev->state |= COWWATCHDOG;

		cowdev->watchthresh = (unsigned long long)
		                      cowwatch.threshold /
				      (cowdev->blksize / 1024);

		if (wait_event_interruptible(cowdev->watchq,
		                    cowdev->watchthresh >= cowdev->blkavail)) {
			cowdev->state &= ~COWWATCHDOG;
			return EINTR;
		}

		cowdev->state &= ~COWWATCHDOG;
	}

	cowwatch.totalkb = (unsigned long long)cowdev->blktotal *
	                                       cowdev->blksize / 1024;
	cowwatch.availkb = (unsigned long long)cowdev->blkavail *
	                                       cowdev->blksize / 1024;

	if ( copy_to_user(arg, &cowwatch, sizeof cowwatch))
		return -EFAULT;

	return 0;
}

/*
** handle ioctl-commands COWCLOSE and COWRDOPEN:
**	COWCLOSE  - close the cowfile while the cowdevice remains open;
**                  this allows an unmount of the filesystem on which
**                  the cowfile resides
**	COWRDOPEN - close the cowfile and reopen it for read-only;
**                  this allows a remount read-ony of the filesystem
**                  on which the cowfile resides
**
** returns:
** 	0   - okay
**    < 0   - error value
*/
static int
cowlo_cowctl(unsigned long __user *arg, int cmd)
{
	struct cowloop_device	*cowdev;
	unsigned long		cowdevice;

	/*
	** retrieve info about device to be removed
	*/
	if ( copy_from_user(&cowdevice, arg, sizeof cowdevice))
		return -EFAULT;

	/*
	** verify major-minor number
	*/
	if ( MAJOR(cowdevice) != COWMAJOR)
		return -EINVAL;

	if ( MINOR(cowdevice) >= maxcows)
		return -EINVAL;

	cowdev = cowdevall[MINOR(cowdevice)];

	if ( !(cowdev->state & COWDEVOPEN) )
		return -ENODEV;

	/*
	** synchronize bitmaps and close cowfile
	*/
	if (cowdev->state & COWRWCOWOPEN) {
		down(&cowdevlock);
		cowlo_sync();
		up(&cowdevlock);
	}

	/*
	** handle specific ioctl-command
	*/
	switch (cmd) {
	   case COWRDOPEN:
		/*
		** if the cowfile is still opened read-write
		*/
		if (cowdev->state & COWRWCOWOPEN) {
			/*
			** close the cowfile
			*/
  			if (cowdev->cowfp)
		  		filp_close(cowdev->cowfp, 0);

			cowdev->state &= ~COWRWCOWOPEN;

			/*
			** open again for read-only
			*/
			cowdev->cowfp = filp_open(cowdev->cowname,
		                          O_RDONLY|O_LARGEFILE, 0600);

			if ( (cowdev->cowfp == NULL) || IS_ERR(cowdev->cowfp) ) {
				printk(KERN_ERR
				     "cowloop - failed to reopen cowfile %s\n",
				     cowdev->cowname);
				return -EINVAL;
			}

			/*
			** mark cowfile open for read-only
			*/
			cowdev->state |= COWRDCOWOPEN;
		} else {
			return -EINVAL;
		}
		break;

	   case COWCLOSE:
		/*
		** if the cowfile is still open
		*/
		if (cowdev->state & COWCOWOPEN) {
			/*
			** close the cowfile
			*/
  			if (cowdev->cowfp)
		  		filp_close(cowdev->cowfp, 0);

			cowdev->state &= ~COWCOWOPEN;
		}
	}

	return 0;
}


/*****************************************************************************/
/* Handling of I/O-requests for a cowdevice                                  */
/*****************************************************************************/

/*
** function to be called by core-kernel to handle the I/O-requests
** in the queue
*/
static void cowlo_request(struct request_queue *q)
{
	struct request		*req;
	struct cowloop_device	*cowdev;

	DEBUGP(DCOW "cowloop - request function called....\n");

	while((req = blk_peek_request(q)) != NULL) {
		DEBUGP(DCOW "cowloop - got next request\n");

		if (! blk_fs_request(req)) {
               		 /* this is not a normal file system request */
                	__blk_end_request_cur(req, -EIO);
                	continue;
        	}
		cowdev = req->rq_disk->private_data;

		if (cowdev->iobusy)
			return;
		else
			cowdev->iobusy = 1;

		/*
		** when no kernel-thread is available, the request will
		** produce an I/O-error
		*/
		if (!cowdev->pid) {
			printk(KERN_ERR"cowloop - no thread available\n");
			__blk_end_request_cur(req, -EIO);	/* request failed */
			cowdev->iobusy	= 0;
			continue;
		}

		/*
		** handle I/O-request in the context of the kernel-thread
		*/
		cowdev->req 	= req;
		cowdev->qfilled	= 1;

		wake_up_interruptible_sync(&cowdev->waitq);

		/*
		** get out of this function now while the I/O-request is
		** under treatment of the kernel-thread; this function
		** will be called again after the current I/O-request has
		** been finished by the thread
		*/
		return;
	}
}

/*
** daemon-process (kernel-thread) executes this function
*/
static int
cowlo_daemon(struct cowloop_device *cowdev)
{
	int	rv;
	int     minor;
	char	myname[16];

	for (minor = 0; minor < maxcows; minor++) {
		if (cowdev == cowdevall[minor]) break;
	}
	sprintf(myname, "cowloopd%d", minor);

        daemonize(myname);

	while (!cowdev->closedown) {
		/*
		** sleep while waiting for an I/O request;
		** note that no non-interruptible wait has been used
		** because the non-interruptible version of
		** a *synchronous* wake_up does not exist (any more)
		*/
		if (wait_event_interruptible(cowdev->waitq, cowdev->qfilled)){
			flush_signals(current); /* ignore signal-based wakeup */
			continue;
		}

		if (cowdev->closedown)		/* module will be unloaded ? */{
			cowdev->pid = 0;
			return 0;
		}

		/*
		** woken up by the I/O-request handler:	treat requested I/O
		*/
		cowdev->qfilled = 0;

		rv = cowlo_do_request(cowdev->req);

		/*
		** reacquire the queue-spinlock for manipulating
		** the request-queue and dequeue the request
		*/
		spin_lock_irq(&cowdev->rqlock);

		__blk_end_request_cur(cowdev->req, rv);
		cowdev->iobusy = 0;

		/*
		** initiate the next request from the queue
		*/
		cowlo_request(cowdev->rqueue);

		spin_unlock_irq(&cowdev->rqlock);
	}
	return 0;
}

/*
** function to be called in the context of the kernel thread
** to handle the queued I/O-requests
**
** returns:
** 	0   - fail
**      1   - success
*/
static long int
cowlo_do_request(struct request *req)
{
	unsigned long		len;
	long int		rv;
	loff_t 			offset;
	struct cowloop_device	*cowdev = req->rq_disk->private_data;

	/*
	** calculate some variables which are needed later on
	*/
	len     =          blk_rq_cur_sectors(req) << 9;
	offset  = (loff_t) blk_rq_pos(req)         << 9;

	DEBUGP(DCOW"cowloop - req cmd=%d offset=%lld len=%lu addr=%p\n",
				*(req->cmd), offset, len, req->buffer);

	/*
	** handle READ- or WRITE-request
	*/
	switch (rq_data_dir(req)) {
	   /**********************************************************/
	   case READ:
		switch ( cowlo_checkio(cowdev, len, offset) ) {
		   case ALLCOW:
			rv = cowlo_readcow(cowdev, req->buffer, len, offset);
			break;

		   case ALLRDO:
			rv = cowlo_readrdo(cowdev, req->buffer, len, offset);
			break;

	   	   case MIXEDUP:
			rv = cowlo_readmix(cowdev, req->buffer, len, offset);
			break;

		   default:
			rv = 0;	/* never happens */
		}
		break;

	   /**********************************************************/
	   case WRITE:
		switch ( cowlo_checkio(cowdev, len, offset) ) {
		   case ALLCOW:
			/*
			** straight-forward write will do...
			*/
			DEBUGP(DCOW"cowloop - write straight ");

			rv = cowlo_writecow(cowdev, req->buffer, len, offset);
			break;	/* from switch */

		   case ALLRDO:
			if ( (len & MUMASK) == 0) {
				DEBUGP(DCOW"cowloop - write straight ");

				rv = cowlo_writecow(cowdev, req->buffer,
								len, offset);
				break;
			}

	   	   case MIXEDUP:
			rv = cowlo_writemix(cowdev, req->buffer, len, offset);
			break;

		   default:
			rv = 0;	/* never happens */
		}
		break;

	   default:
		printk(KERN_ERR
		       "cowloop - unrecognized command %d\n", *(req->cmd));
		rv = 0;
	}

	return (rv <= 0 ? 0 : 1);
}

/*
** check for a given I/O-request if all underlying blocks
** (with size MAPUNIT) are either in the read-only file or in
** the cowfile (or a combination of the two)
**
** returns:
** 	ALLRDO  - all underlying blocks in rdofile
**      ALLCOW  - all underlying blocks in cowfile
**      MIXEDUP - underlying blocks partly in rdofile and partly in cowfile
*/
static int
cowlo_checkio(struct cowloop_device *cowdev, int len, loff_t offset)
{
	unsigned long	mapnum, bytenum, bitnum, blocknr, partlen;
	long int	totcnt, cowcnt;
	char		*mc;

	/*
	** notice that the requested block might cross
	** a blocksize boundary while one of the concerned
	** blocks resides in the read-only file and another
	** one in the copy-on-write file; in that case the
        ** request will be broken up into pieces
	*/
	if ( (len <= MAPUNIT) &&
	     (MAPUNIT - (offset & MUMASK) <= len) ) {
		/*
		** easy situation:
		** requested data-block entirely fits within
		** the mapunit used for the bitmap
		** check if that block is located in rdofile or
		** cowfile
		*/
		blocknr = offset >> MUSHIFT;

		mapnum  = CALCMAP (blocknr);
		bytenum = CALCBYTE(blocknr);
		bitnum  = CALCBIT (blocknr);

		if (*(*(cowdev->mapcache+mapnum)+bytenum)&(1<<bitnum))
			return ALLCOW;
		else
			return ALLRDO;
	}

	/*
	** less easy situation:
	** the requested data-block does not fit within the mapunit
	** used for the bitmap
	** check if *all* underlying blocks involved reside on the rdofile
       	** or the cowfile (so still no breakup required)
	*/
	for (cowcnt=totcnt=0; len > 0; len-=partlen, offset+=partlen, totcnt++){
		/*
		** calculate blocknr of involved block
		*/
		blocknr = offset >> MUSHIFT;

		/*
		** calculate partial length for this transfer
		*/
		partlen = MAPUNIT - (offset & MUMASK);
		if (partlen > len)
			partlen = len;

		/*
		** is this block located in the cowfile
		*/
		mapnum  = CALCMAP (blocknr);
		bytenum = CALCBYTE(blocknr);
		bitnum  = CALCBIT (blocknr);

		mc	= *(cowdev->mapcache+mapnum);

		if (*(mc+bytenum)&(1<<bitnum))
			cowcnt++;;

		DEBUGP(DCOW
		       "cowloop - check %lu - map %lu, byte %lu, bit %lu, "
		       "cowcnt %ld, totcnt %ld %02x %p\n",
			blocknr, mapnum, bytenum, bitnum, cowcnt, totcnt,
			*(mc+bytenum), mc);
	}

	if (cowcnt == 0)	/* all involved blocks on rdofile? */
		return ALLRDO;

	if (cowcnt == totcnt)	/* all involved blocks on cowfile? */
		return ALLCOW;

	/*
	** situation somewhat more complicated:
	** involved underlying blocks spread over both files
	*/
	return MIXEDUP;
}

/*
** read requested chunk partly from rdofile and partly from cowfile
**
** returns:
** 	0   - fail
**      1   - success
*/
static int
cowlo_readmix(struct cowloop_device *cowdev, void *buf, int len, loff_t offset)
{
	unsigned long	mapnum, bytenum, bitnum, blocknr, partlen;
	long int	rv;
	char		*mc;

	/*
	** complicated approach: breakup required of read-request
	*/
	for (rv=1; len > 0; len-=partlen, buf+=partlen, offset+=partlen) {
		/*
		** calculate blocknr of entire block
		*/
		blocknr = offset >> MUSHIFT;

		/*
		** calculate partial length for this transfer
		*/
		partlen = MAPUNIT - (offset & MUMASK);
		if (partlen > len)
			partlen = len;

		/*
		** is this block located in the cowfile
		*/
		mapnum  = CALCMAP (blocknr);
		bytenum = CALCBYTE(blocknr);
		bitnum  = CALCBIT (blocknr);
		mc	= *(cowdev->mapcache+mapnum);

		if (*(mc+bytenum)&(1<<bitnum)) {
			/*
			** read (partial) block from cowfile
			*/
			DEBUGP(DCOW"cowloop - split read "
				"cow partlen=%ld off=%lld\n", partlen, offset);

			if (cowlo_readcow(cowdev, buf, partlen, offset) <= 0)
				rv = 0;
		} else {
			/*
			** read (partial) block from rdofile
			*/
			DEBUGP(DCOW"cowloop - split read "
				"rdo partlen=%ld off=%lld\n", partlen, offset);

			if (cowlo_readrdo(cowdev, buf, partlen, offset) <= 0)
				rv = 0;
		}
	}

	return rv;
}

/*
** chunk to be written to the cowfile needs pieces to be
** read from the rdofile
**
** returns:
** 	0   - fail
**      1   - success
*/
static int
cowlo_writemix(struct cowloop_device *cowdev, void *buf, int len, loff_t offset)
{
	unsigned long	mapnum, bytenum, bitnum, blocknr, partlen;
	long int	rv;
	char		*mc;

	/*
	** somewhat more complicated stuff is required:
	** if the request is larger than one underlying
	** block or is spread over two underlying blocks,
	** split the request into pieces; if a block does not
	** start at a block boundary, take care that
	** surrounding data is read first (if needed),
	** fit the new data in and write it as a full block
	*/
	for (rv=1; len > 0; len-=partlen, buf+=partlen, offset+=partlen) {
		/*
		** calculate partial length for this transfer
		*/
		partlen = MAPUNIT - (offset & MUMASK);
		if (partlen > len)
			partlen = len;

		/*
		** calculate blocknr of entire block
		*/
		blocknr = offset >> MUSHIFT;

		/*
		** has this block been written before?
		*/
		mapnum  = CALCMAP (blocknr);
		bytenum = CALCBYTE(blocknr);
		bitnum  = CALCBIT (blocknr);
		mc	= *(cowdev->mapcache+mapnum);

		if (*(mc+bytenum)&(1<<bitnum)) {
			/*
			** block has been written before;
			** write transparantly to cowfile
			*/
			DEBUGP(DCOW
			       "cowloop - splitwr transp\n");

			if (cowlo_writecow(cowdev, buf, partlen, offset) <= 0)
				rv = 0;
		} else {
			/*
			** block has never been written before,
			** so read entire block from
			** read-only file first, unless
			** a full block is requested to
			** be written
			*/
			if (partlen < MAPUNIT) {
				if (cowlo_readrdo(cowdev, cowdev->iobuf,
				      MAPUNIT, (loff_t)blocknr << MUSHIFT) <= 0)
					rv = 0;
			}

			/*
			** transfer modified part into
			** the block just read
			*/
			memcpy(cowdev->iobuf + (offset & MUMASK), buf, partlen);

			/*
			** write entire block to cowfile
			*/
			DEBUGP(DCOW"cowloop - split "
				"partlen=%ld off=%lld\n",
				partlen, (loff_t)blocknr << MUSHIFT);

			if (cowlo_writecow(cowdev, cowdev->iobuf, MAPUNIT,
					     (loff_t)blocknr << MUSHIFT) <= 0)
				rv = 0;
		}
	}

	return rv;
}

/*****************************************************************************/
/* I/O-support for read-only file and copy-on-write file                     */
/*****************************************************************************/

/*
** read data from the read-only file
**
** return-value: similar to user-mode read
*/
static long int
cowlo_readrdo(struct cowloop_device *cowdev, void *buf, int len, loff_t offset)
{
	long int	rv;
	mm_segment_t	old_fs;
	loff_t		saveoffset = offset;

	DEBUGP(DCOW"cowloop - readrdo called\n");

        old_fs = get_fs();
	set_fs( get_ds() );
	rv = cowdev->rdofp->f_op->read(cowdev->rdofp, buf, len, &offset);
        set_fs(old_fs);

	if (rv < len) {
		printk(KERN_WARNING "cowloop - read-failure %ld on rdofile"
		                    "- offset=%lld len=%d\n",
					rv, saveoffset, len);
	}

	cowdev->rdoreads++;
	return rv;
}

/*
** read cowfile from a modified offset, i.e. skipping the bitmap and cowhead
**
** return-value: similar to user-mode read
*/
static long int
cowlo_readcow(struct cowloop_device *cowdev, void *buf, int len, loff_t offset)
{
	DEBUGP(DCOW"cowloop - readcow called\n");

	offset += cowdev->cowhead->doffset;

	return cowlo_readcowraw(cowdev, buf, len, offset);
}

/*
** read cowfile from an absolute offset
**
** return-value: similar to user-mode read
*/
static long int
cowlo_readcowraw(struct cowloop_device *cowdev,
					void *buf, int len, loff_t offset)
{
	long int	rv;
	mm_segment_t	old_fs;
	loff_t		saveoffset = offset;

	DEBUGP(DCOW"cowloop - readcowraw called\n");

	/*
	** be sure that cowfile is opened for read-write
	*/
	if ( !(cowdev->state & COWCOWOPEN) ) {
		 printk(KERN_WARNING
		        "cowloop - read request from cowfile refused\n");

		return -EBADF;
	}

	/*
	** issue low level read
	*/
        old_fs = get_fs();
	set_fs( get_ds() );
	rv = cowdev->cowfp->f_op->read(cowdev->cowfp, buf, len, &offset);
        set_fs(old_fs);

	if (rv < len) {
		printk(KERN_WARNING
		       "cowloop - read-failure %ld on cowfile"
		       "- offset=%lld len=%d\n", rv, saveoffset, len);
	}

	cowdev->cowreads++;
	return rv;
}

/*
** write cowfile from a modified offset, i.e. skipping the bitmap and cowhead
**
** if a block is written for the first time while its contents consists
** of binary zeroes only, the concerning bitmap is flushed to the cowfile
**
** return-value: similar to user-mode write
*/
static long int
cowlo_writecow(struct cowloop_device *cowdev, void *buf, int len, loff_t offset)
{
	long int	rv;
	unsigned long	mapnum=0, mapbyte=0, mapbit=0, cowblock=0, partlen;
	char		*tmpptr,  *mapptr = NULL;
	loff_t		tmpoffset, mapoffset = 0;

	DEBUGP(DCOW"cowloop - writecow called\n");

	/*
	** be sure that cowfile is opened for read-write
	*/
	if ( !(cowdev->state & COWRWCOWOPEN) ) {
		 printk(KERN_WARNING
		        "cowloop - Write request to cowfile refused\n");

		return -EBADF;
	}

	/*
	** write the entire block to the cowfile
	*/
	tmpoffset = offset + cowdev->cowhead->doffset;

	rv = cowlo_writecowraw(cowdev, buf, len, tmpoffset);

	/*
	** verify if enough space available on filesystem holding
	** the cowfile
	**   - when the last write failed (might be caused by lack of space)
	**   - when a watcher is active (to react adequatly)
	**   - when the previous check indicated fs was almost full
	**   - with regular intervals
	*/
	if ( (rv <= 0)				       ||
	     (cowdev->state        & COWWATCHDOG)      ||
	     (cowdev->blkavail / 2 < SPCDFLINTVL)      ||
	     (cowdev->cowwrites    % SPCDFLINTVL == 0) ) {
		struct kstatfs		ks;

		if (vfs_statfs(cowdev->cowfp->f_dentry, &ks)==0){
			if (ks.f_bavail <= SPCMINBLK) {
				switch (ks.f_bavail) {
				   case 0:
				   case 1:
				   case 2:
				   case 3:
					printk(KERN_ALERT
					       "cowloop - "
					       "ALERT: cowfile full!\n");
					break;

				   default:
					printk(KERN_WARNING
					       "cowloop - cowfile almost "
					       "full (only %llu Kb free)\n",
						(unsigned long long)
                                                ks.f_bsize * ks.f_bavail /1024);
				}
			}

			cowdev->blktotal = ks.f_blocks;
			cowdev->blkavail = ks.f_bavail;

			/*
			** wakeup watcher if threshold has been reached
			*/
			if ( (cowdev->state & COWWATCHDOG) &&
			    (cowdev->watchthresh >= cowdev->blkavail) ) {
				wake_up_interruptible(&cowdev->watchq);
			}
		}
	}

	if (rv <= 0)
		return rv;

	DEBUGP(DCOW"cowloop - block written\n");

	/*
	** check if block(s) is/are written to the cowfile
	** for the first time; if so, adapt the bitmap
	*/
	for (; len > 0; len-=partlen, offset+=partlen, buf+=partlen) {
		/*
		** calculate partial length for this transfer
		*/
		partlen = MAPUNIT - (offset & MUMASK);
		if (partlen > len)
			partlen = len;

		/*
		** calculate bitnr of written chunk of cowblock
		*/
		cowblock = offset >> MUSHIFT;

		mapnum   = CALCMAP (cowblock);
		mapbyte  = CALCBYTE(cowblock);
		mapbit   = CALCBIT (cowblock);

		if (*(*(cowdev->mapcache+mapnum)+mapbyte) & (1<<mapbit))
			continue;	/* already written before */

	       	/*
		** if the block is written for the first time,
		** the corresponding bit should be set in the bitmap
		*/
		*(*(cowdev->mapcache+mapnum)+mapbyte) |= (1<<mapbit);

		cowdev->nrcowblocks++;

		DEBUGP(DCOW"cowloop - bitupdate blk=%ld map=%ld "
		        "byte=%ld bit=%ld\n",
			cowblock, mapnum, mapbyte, mapbit);

		/*
		** check if the cowhead in the cowfile is currently
		** marked clean; if so, mark it dirty and flush it
		*/
		if ( !(cowdev->cowhead->flags &= COWDIRTY)) {
			cowdev->cowhead->flags	|= COWDIRTY;

			cowlo_writecowraw(cowdev, cowdev->cowhead,
							MAPUNIT, (loff_t)0);
		}

		/*
		** if the written datablock contained binary zeroes,
		** the bitmap block should be marked to be flushed to disk
		** (blocks containing all zeroes cannot be recovered by
		** the cowrepair-program later on if cowloop is not properly
		** removed via rmmod)
		*/
		if ( memcmp(buf, allzeroes, partlen) ) /* not all zeroes? */
			continue;                      /* no flush needed */

		/*
		** calculate positions of bitmap block to be flushed
		** - pointer of bitmap block in memory
		** - offset  of bitmap block in cowfile
		*/
		tmpptr    = *(cowdev->mapcache+mapnum) + (mapbyte & (~MUMASK));
		tmpoffset = (loff_t) MAPUNIT + mapnum * MAPCHUNKSZ +
		                                       (mapbyte & (~MUMASK));

		/*
		** flush a bitmap block at the moment that all bits have
		** been set in that block, i.e. at the moment that we
		** switch to another bitmap block
		*/
		if ( (mapoffset != 0) && (mapoffset != tmpoffset) ) {
			if (cowlo_writecowraw(cowdev, mapptr, MAPUNIT,
							mapoffset) < 0) {
				printk(KERN_WARNING
				       "cowloop - write-failure on bitmap - "
				       "blk=%ld map=%ld byte=%ld bit=%ld\n",
				  	cowblock, mapnum, mapbyte, mapbit);
			}

			DEBUGP(DCOW"cowloop - bitmap blk written %lld\n",
								mapoffset);
		}

		/*
		** remember offset in cowfile and offset in memory
		** for bitmap to be flushed; flushing will be done
		** as soon as all updates in this bitmap block have
		** been done
		*/
		mapoffset = tmpoffset;
		mapptr    = tmpptr;
	}

	/*
	** any new block written containing binary zeroes?
	*/
	if (mapoffset) {
		if (cowlo_writecowraw(cowdev, mapptr, MAPUNIT, mapoffset) < 0) {
			printk(KERN_WARNING
			       "cowloop - write-failure on bitmap - "
			       "blk=%ld map=%ld byte=%ld bit=%ld\n",
			       cowblock, mapnum, mapbyte, mapbit);
		}

		DEBUGP(DCOW"cowloop - bitmap block written %lld\n", mapoffset);
	}

	return rv;
}

/*
** write cowfile from an absolute offset
**
** return-value: similar to user-mode write
*/
static long int
cowlo_writecowraw(struct cowloop_device *cowdev,
					void *buf, int len, loff_t offset)
{
	long int	rv;
	mm_segment_t	old_fs;
	loff_t		saveoffset = offset;

	DEBUGP(DCOW"cowloop - writecowraw called\n");

	/*
	** be sure that cowfile is opened for read-write
	*/
	if ( !(cowdev->state & COWRWCOWOPEN) ) {
		 printk(KERN_WARNING
		        "cowloop - write request to cowfile refused\n");

		return -EBADF;
	}

	/*
	** issue low level write
	*/
        old_fs = get_fs();
	set_fs( get_ds() );
	rv = cowdev->cowfp->f_op->write(cowdev->cowfp, buf, len, &offset);
        set_fs(old_fs);

	if (rv < len) {
		printk(KERN_WARNING
		       "cowloop - write-failure %ld on cowfile"
		       "- offset=%lld len=%d\n", rv, saveoffset, len);
	}

	cowdev->cowwrites++;
	return rv;
}


/*
** readproc-function: called when the corresponding /proc-file is read
*/
static int
cowlo_readproc(char *buf, char **start, off_t pos, int cnt, int *eof, void *p)
{
	struct cowloop_device *cowdev = p;

	revision[sizeof revision - 3] = '\0';

	return sprintf(buf,
		"   cowloop version: %9s\n\n"
		"      device state: %s%s%s%s\n"
		"   number of opens: %9d\n"
		"     pid of thread: %9d\n\n"
		"    read-only file: %9s\n"
		"          rdoreads: %9lu\n\n"
		"copy-on-write file: %9s\n"
		"     state cowfile: %9s\n"
		"     bitmap-blocks: %9lu (of %d bytes)\n"
		"  cowblocks in use: %9lu (of %d bytes)\n"
		"          cowreads: %9lu\n"
		"         cowwrites: %9lu\n",
			&revision[11],

			cowdev->state & COWDEVOPEN   ? "devopen "   : "",
			cowdev->state & COWRWCOWOPEN ? "cowopenrw " : "",
			cowdev->state & COWRDCOWOPEN ? "cowopenro " : "",
			cowdev->state & COWWATCHDOG  ? "watchdog "  : "",

			cowdev->opencnt,
			cowdev->pid,
			cowdev->rdoname,
			cowdev->rdoreads,
			cowdev->cowname,
			cowdev->cowhead->flags & COWDIRTY ? "dirty":"clean",
			cowdev->mapsize >> MUSHIFT, MAPUNIT,
			cowdev->nrcowblocks, MAPUNIT,
			cowdev->cowreads,
			cowdev->cowwrites);
}

/*****************************************************************************/
/* Setup and destroy cowdevices                                              */
/*****************************************************************************/

/*
** open and prepare a cowdevice (rdofile and cowfile) and allocate bitmaps
**
** returns:
** 	0   - okay
**    < 0   - error value
*/
static int
cowlo_openpair(char *rdof, char *cowf, int autorecover, int minor)
{
	long int		rv;
	struct cowloop_device	*cowdev = cowdevall[minor];
	struct kstatfs		ks;

	down(&cowdevlock);

	/*
	** requested device exists?
	*/
	if (minor >= maxcows) {
		up(&cowdevlock);
		return -ENODEV;
	}

	/*
	** requested device already assigned to cowdevice?
	*/
	if (cowdev->state & COWDEVOPEN) {
		up(&cowdevlock);
		return -EBUSY;
	}

	/*
	** initialize administration
	*/
	memset(cowdev, 0, sizeof *cowdev);

	spin_lock_init     (&cowdev->rqlock);
	init_waitqueue_head(&cowdev->waitq);
	init_waitqueue_head(&cowdev->watchq);

	/*
	** open the read-only file
	*/
	DEBUGP(DCOW"cowloop - call openrdo....\n");

	if ( (rv = cowlo_openrdo(cowdev, rdof)) ) {
		cowlo_undo_openrdo(cowdev);
		up(&cowdevlock);
		return rv;
	}

	/*
	** open the cowfile
	*/
	DEBUGP(DCOW"cowloop - call opencow....\n");

	if ( (rv = cowlo_opencow(cowdev, cowf, autorecover)) ) {
		cowlo_undo_openrdo(cowdev);
		cowlo_undo_opencow(cowdev);
		up(&cowdevlock);
		return rv;
	}

	/*
	** administer total and available size of filesystem holding cowfile
	*/
	if (vfs_statfs(cowdev->cowfp->f_dentry, &ks)==0) {
		cowdev->blksize  = ks.f_bsize;
		cowdev->blktotal = ks.f_blocks;
		cowdev->blkavail = ks.f_bavail;
	} else {
		cowdev->blksize  = 1024;	/* avoid division by zero */
	}

	/*
	** flush the (recovered) bitmaps and cowhead to the cowfile
	*/
	DEBUGP(DCOW"cowloop - call cowsync....\n");

	cowlo_sync();

	/*
	** allocate gendisk for the cow device
	*/
	DEBUGP(DCOW"cowloop - alloc disk....\n");

	if ((cowdev->gd = alloc_disk(1)) == NULL) {
		printk(KERN_WARNING
		       "cowloop - unable to alloc_disk for cowloop\n");

		cowlo_undo_openrdo(cowdev);
		cowlo_undo_opencow(cowdev);
		up(&cowdevlock);
		return -ENOMEM;
	}

	cowdev->gd->major        = COWMAJOR;
	cowdev->gd->first_minor  = minor;
	cowdev->gd->minors       = 1;
	cowdev->gd->fops         = &cowlo_fops;
	cowdev->gd->private_data = cowdev;
	sprintf(cowdev->gd->disk_name, "%s%d", DEVICE_NAME, minor);

	/* in .5 Kb units */
	set_capacity(cowdev->gd, (cowdev->numblocks*(MAPUNIT/512)));

	DEBUGP(DCOW"cowloop - init request queue....\n");

	if ((cowdev->rqueue = blk_init_queue(cowlo_request, &cowdev->rqlock))
								== NULL) {
		printk(KERN_WARNING
		       "cowloop - unable to get request queue for cowloop\n");

		del_gendisk(cowdev->gd);
		cowlo_undo_openrdo(cowdev);
		cowlo_undo_opencow(cowdev);
		up(&cowdevlock);
		return -EINVAL;
	}

	blk_queue_logical_block_size(cowdev->rqueue, cowdev->blocksz);
	cowdev->gd->queue = cowdev->rqueue;

	/*
	** start kernel thread to handle requests
	*/
	DEBUGP(DCOW"cowloop - kickoff daemon....\n");

	cowdev->pid = kernel_thread((int (*)(void *))cowlo_daemon, cowdev, 0);

	/*
	** create a file below directory /proc/cow for this new cowdevice
	*/
	if (cowlo_procdir) {
		char 	tmpname[64];

		sprintf(tmpname, "%d", minor);

		create_proc_read_entry(tmpname, 0 , cowlo_procdir,
						cowlo_readproc, cowdev);
	}

	cowdev->state	|= COWDEVOPEN;

	cowdev->rdoname = rdof;
	cowdev->cowname = cowf;

	/*
	** enable the new disk; this triggers the first request!
	*/
	DEBUGP(DCOW"cowloop - call add_disk....\n");

	add_disk(cowdev->gd);

	up(&cowdevlock);
	return 0;
}

/*
** close a cowdevice (pair of rdofile/cowfile) and release memory
**
** returns:
** 	0   - okay
**    < 0   - error value
*/
static int
cowlo_closepair(struct cowloop_device *cowdev)
{
	int minor;

	down(&cowdevlock);

	/*
	** if cowdevice is not activated at all, refuse
	*/
	if ( !(cowdev->state & COWDEVOPEN) ) {
		up(&cowdevlock);
		return -ENODEV;
	}

	/*
	** if this cowdevice is still open, refuse
	*/
	if (cowdev->opencnt > 0) {
		up(&cowdevlock);
		return -EBUSY;
	}

	up(&cowdevlock);

	/*
	** wakeup watcher (if any)
	*/
	if (cowdev->state & COWWATCHDOG) {
		cowdev->watchthresh = cowdev->blkavail;
		wake_up_interruptible(&cowdev->watchq);
	}

	/*
	** wakeup kernel-thread to be able to exit
	** and wait until it has exited
	*/
	cowdev->closedown = 1;
	cowdev->qfilled   = 1;
	wake_up_interruptible(&cowdev->waitq);

       	while (cowdev->pid)
               	schedule();

	del_gendisk(cowdev->gd);  /* revert the alloc_disk() */
	put_disk(cowdev->gd);     /* revert the add_disk()   */

	if (cowlo_procdir) {
		char 	tmpname[64];

		for (minor = 0; minor < maxcows; minor++) {
			if (cowdev == cowdevall[minor]) break;
		}
		sprintf(tmpname, "%d", minor);

		remove_proc_entry(tmpname, cowlo_procdir);
	}

	blk_cleanup_queue(cowdev->rqueue);

	/*
	** release memory for filenames if these names have
	** been allocated dynamically
	*/
	if ( (cowdev->cowname) && (cowdev->cowname != cowfile))
		kfree(cowdev->cowname);

	if ( (cowdev->rdoname) && (cowdev->rdoname != rdofile))
		kfree(cowdev->rdoname);

	cowlo_undo_openrdo(cowdev);
	cowlo_undo_opencow(cowdev);

	cowdev->state &= ~COWDEVOPEN;

	return 0;
}

/*
** open the read-only file
**
** returns:
** 	0   - okay
**    < 0   - error value
*/
static int
cowlo_openrdo(struct cowloop_device *cowdev, char *rdof)
{
	struct file	*f;
	struct inode	*inode;
	long int	i, nrval;

	DEBUGP(DCOW"cowloop - openrdo called\n");

	/*
	** open the read-only file
	*/
        if(*rdof == '\0') {
     	     	printk(KERN_ERR
		       "cowloop - specify name for read-only file\n\n");
         	return -EINVAL;
        }

	f = filp_open(rdof, O_RDONLY|O_LARGEFILE, 0);

	if ( (f == NULL) || IS_ERR(f) ) {
		printk(KERN_ERR
		       "cowloop - open of rdofile %s failed\n", rdof);
		return -EINVAL;
	}

	cowdev->rdofp = f;

	inode = f->f_dentry->d_inode;

	if ( !S_ISREG(inode->i_mode) && !S_ISBLK(inode->i_mode) ) {
		printk(KERN_ERR
		       "cowloop - %s not regular file or blockdev\n", rdof);
		return -EINVAL;
	}

	DEBUGP(DCOW"cowloop - determine size rdo....\n");

	/*
	** determine block-size and total size of read-only file
	*/
	if (S_ISREG(inode->i_mode)) {
		/*
		** read-only file is a regular file
		*/
		cowdev->blocksz   = 512;	/* other value fails */
		cowdev->numblocks = inode->i_size >> MUSHIFT;

		if (inode->i_size & MUMASK) {
			printk(KERN_WARNING
			       "cowloop - rdofile %s truncated to multiple "
			       "of %d bytes\n", rdof, MAPUNIT);
		}

		DEBUGP(DCOW"cowloop - RO=regular: numblocks=%d, blocksz=%d\n",
			cowdev->numblocks, cowdev->blocksz);
	} else {
		/*
		** read-only file is a block device
		*/
		cowdev->belowdev  = inode->i_bdev;
		cowdev->belowgd   = cowdev->belowdev->bd_disk; /* gendisk */

		if (cowdev->belowdev->bd_part) {
			cowdev->numblocks = cowdev->belowdev->bd_part->nr_sects
								/ (MAPUNIT/512);
		}

		if (cowdev->belowgd) {
			cowdev->belowq = cowdev->belowgd->queue;

			if (cowdev->numblocks == 0) {
				cowdev->numblocks = get_capacity(cowdev->belowgd)
                         					/ (MAPUNIT/512);
			}
		}


		if (cowdev->belowq)
			cowdev->blocksz = queue_logical_block_size(cowdev->belowq);

		if (cowdev->blocksz == 0)
			cowdev->blocksz = BLOCK_SIZE; /* default 2^10 */

		DEBUGP(DCOW"cowloop - numblocks=%d, "
		           "blocksz=%d, belowgd=%p, belowq=%p\n",
		  	cowdev->numblocks, cowdev->blocksz,
			cowdev->belowgd, cowdev->belowq);

		DEBUGP(DCOW"cowloop - belowdev.bd_block_size=%d\n",
		  	cowdev->belowdev->bd_block_size);
	}

	if (cowdev->numblocks == 0) {
		printk(KERN_ERR "cowloop - %s has no contents\n", rdof);
		return -EINVAL;
	}

	/*
	** reserve space in memory as generic I/O buffer
	*/
	cowdev->iobuf  = kmalloc(MAPUNIT, GFP_KERNEL);

	if (!cowdev->iobuf) {
		printk(KERN_ERR
		       "cowloop - cannot get space for buffer %d\n", MAPUNIT);
		return -ENOMEM;
	}

	DEBUGP(DCOW"cowloop - determine fingerprint rdo....\n");

	/*
	** determine fingerprint for read-only file
	** 	calculate fingerprint from first four datablocks
	**	which do not contain binary zeroes
	*/
	for (i=0, cowdev->fingerprint=0, nrval=0;
			(nrval < 4)&&(i < cowdev->numblocks); i++) {
		int 		j;
		unsigned char	cs;

		/*
		** read next block
		*/
		if (cowlo_readrdo(cowdev, cowdev->iobuf, MAPUNIT,
						(loff_t)i << MUSHIFT) < 1)
			break;

		/*
		** calculate fingerprint by adding all byte-values
		*/
		for (j=0, cs=0; j < MAPUNIT; j++)
			cs += *(cowdev->iobuf+j);

		if (cs == 0)	/* block probably contained zeroes */
			continue;

		/*
		** shift byte-value to proper place in final fingerprint
		*/
		cowdev->fingerprint |= cs << (nrval*8);
		nrval++;
	}

	return 0;
}

/*
** undo memory allocs and file opens issued so far
** related to the read-only file
*/
static void
cowlo_undo_openrdo(struct cowloop_device *cowdev)
{
	if(cowdev->iobuf);
		kfree(cowdev->iobuf);

	if (cowdev->rdofp)
  		filp_close(cowdev->rdofp, 0);
}

/*
** open the cowfile
**
** returns:
** 	0   - okay
**    < 0   - error value
*/
static int
cowlo_opencow(struct cowloop_device *cowdev, char *cowf, int autorecover)
{
	long int		i, rv;
	int			minor;
	unsigned long		nb;
	struct file		*f;
	struct inode		*inode;
	loff_t			offset;
	struct cowloop_device	*cowtmp;

	DEBUGP(DCOW"cowloop - opencow called\n");

	/*
	** open copy-on-write file (read-write)
	*/
        if (cowf[0] == '\0') {
          	printk(KERN_ERR
                 "cowloop - specify name of copy-on-write file\n\n");
         	return -EINVAL;
        }

	f = filp_open(cowf, O_RDWR|O_LARGEFILE, 0600);

	if ( (f == NULL) || IS_ERR(f) ) {
		/*
		** non-existing cowfile: try to create
		*/
		f = filp_open(cowf, O_RDWR|O_CREAT|O_LARGEFILE, 0600);

		if ( (f == NULL) || IS_ERR(f) ) {
			printk(KERN_ERR
		       	  "cowloop - failed to open file %s for read-write\n\n",
		       						cowf);
			return -EINVAL;
	 	}
	}

	cowdev->cowfp = f;

	inode = f->f_dentry->d_inode;

	if (!S_ISREG(inode->i_mode)) {
		printk(KERN_ERR "cowloop - %s is not regular file\n", cowf);
		return -EINVAL;
	}

	/*
	** check if this cowfile is already in use for another cowdevice
	*/
	for (minor = 0; minor < maxcows; minor++) {

		cowtmp = cowdevall[minor];

		if ( !(cowtmp->state & COWDEVOPEN) )
			continue;

		if (cowtmp == cowdev)
			continue;

		if (cowtmp->cowfp->f_dentry->d_inode == f->f_dentry->d_inode) {
			printk(KERN_ERR
			       "cowloop - %s: already in use as cow\n", cowf);
			return -EBUSY;
		}
	}

	/*
	** mark cowfile open for read-write
	*/
	cowdev->state |= COWRWCOWOPEN;

	/*
	** calculate size (in bytes) for total bitmap in cowfile;
	** when the size of the cowhead block is added, the start-offset
	** for the modified data blocks can be found
	*/
	nb = cowdev->numblocks;

	if (nb%8)		/* transform #bits to #bytes */
		nb+=8;  	/* rounded if necessary      */
	nb /= 8;

	if (nb & MUMASK)	/* round up #bytes to MAPUNIT chunks */
		cowdev->mapsize = ( (nb>>MUSHIFT) +1) << MUSHIFT;
	else
		cowdev->mapsize = nb;

	/*
	** reserve space in memory for the cowhead
	*/
	cowdev->cowhead = kmalloc(MAPUNIT, GFP_KERNEL);

	if (!cowdev->cowhead) {
		printk(KERN_ERR "cowloop - cannot get space for cowhead %d\n",
								     MAPUNIT);
		return -ENOMEM;
	}

	memset(cowdev->cowhead, 0, MAPUNIT);

	DEBUGP(DCOW"cowloop - prepare cowhead....\n");

	/*
	** check if the cowfile exists or should be created
	*/
	if (inode->i_size != 0) {
		/*
		** existing cowfile: read the cow head
		*/
		if (inode->i_size < MAPUNIT) {
			printk(KERN_ERR
			       "cowloop - existing cowfile %s too small\n",
				cowf);
			return -EINVAL;
		}

		cowlo_readcowraw(cowdev, cowdev->cowhead, MAPUNIT, (loff_t) 0);

		/*
		** verify if the existing file is really a cowfile
		*/
		if (cowdev->cowhead->magic != COWMAGIC) {
			printk(KERN_ERR
			       "cowloop - cowfile %s has incorrect format\n",
				cowf);
			return -EINVAL;
		}

		/*
		** verify the cowhead version of the cowfile
		*/
		if (cowdev->cowhead->version > COWVERSION) {
			printk(KERN_ERR
			       "cowloop - cowfile %s newer than this driver\n",
				cowf);
			return -EINVAL;
		}

		/*
		** make sure that this is not a packed cowfile
		*/
		if (cowdev->cowhead->flags & COWPACKED) {
			printk(KERN_ERR
			    "cowloop - packed cowfile %s not accepted\n", cowf);
			return -EINVAL;
		}

		/*
		** verify if the cowfile has been properly closed
		*/
		if (cowdev->cowhead->flags & COWDIRTY) {
			/*
			** cowfile was not properly closed;
			** check if automatic recovery is required
			** (actual recovery will be done later on)
			*/
			if (!autorecover) {
				printk(KERN_ERR
				       "cowloop - cowfile %s is dirty "
				       "(not properly closed by rmmod?)\n",
					cowf);
				printk(KERN_ERR
				       "cowloop - run cowrepair or specify "
				       "'option=r' to recover\n");
				return -EINVAL;
			}
		}

		/*
		** verify if the cowfile is really related to this rdofile
		*/
		if (cowdev->cowhead->rdoblocks != cowdev->numblocks) {
			printk(KERN_ERR
		       	       "cowloop - cowfile %s (size %lld) not related "
		       	       "to rdofile (size %lld)\n",
				cowf,
				(long long)cowdev->cowhead->rdoblocks <<MUSHIFT,
				(long long)cowdev->numblocks <<MUSHIFT);
			return -EINVAL;
		}

		if (cowdev->cowhead->rdofingerprint != cowdev->fingerprint) {
			printk(KERN_ERR
		       	     "cowloop - cowfile %s not related to rdofile "
			     " (fingerprint err - rdofile modified?)\n", cowf);
			return -EINVAL;
		}
	} else {
		/*
		** new cowfile: determine the minimal size (cowhead+bitmap)
		*/
		offset = (loff_t) MAPUNIT + cowdev->mapsize - 1;

		if ( cowlo_writecowraw(cowdev, "", 1, offset) < 1) {
			printk(KERN_ERR
			       "cowloop - cannot set cowfile to size %lld\n",
				offset+1);
			return -EINVAL;
		}

		/*
		** prepare new cowhead
		*/
		cowdev->cowhead->magic		= COWMAGIC;
		cowdev->cowhead->version	= COWVERSION;
		cowdev->cowhead->mapunit	= MAPUNIT;
		cowdev->cowhead->mapsize	= cowdev->mapsize;
		cowdev->cowhead->rdoblocks	= cowdev->numblocks;
		cowdev->cowhead->rdofingerprint	= cowdev->fingerprint;
		cowdev->cowhead->cowused	= 0;

		/*
		** calculate start offset of data in cowfile,
		** rounded up to multiple of 4K to avoid
		** unnecessary disk-usage for written datablocks in
		** the sparsed cowfile on e.g. 4K filesystems
		*/
		cowdev->cowhead->doffset =
			((MAPUNIT+cowdev->mapsize+4095)>>12)<<12;
	}

	cowdev->cowhead->flags	= 0;

	DEBUGP(DCOW"cowloop - reserve space bitmap....\n");

	/*
	** reserve space in memory for the entire bitmap and
	** fill it with the bitmap-data from disk; the entire
	** bitmap is allocated in several chunks because kmalloc
	** has restrictions regarding the allowed size per kmalloc
	*/
	cowdev->mapcount = (cowdev->mapsize+MAPCHUNKSZ-1)/MAPCHUNKSZ;

	/*
	** the size of every bitmap chunk will be MAPCHUNKSZ bytes, except for
	** the last bitmap chunk: calculate remaining size for this chunk
	*/
	if (cowdev->mapsize % MAPCHUNKSZ == 0)
		cowdev->mapremain = MAPCHUNKSZ;
	else
		cowdev->mapremain = cowdev->mapsize % MAPCHUNKSZ;

	/*
	** allocate space to store all pointers for the bitmap-chunks
	** (initialize area with zeroes to allow proper undo)
	*/
	cowdev->mapcache = kmalloc(cowdev->mapcount * sizeof(char *),
								GFP_KERNEL);
	if (!cowdev->mapcache) {
		printk(KERN_ERR
		       "cowloop - can not allocate space for bitmap ptrs\n");
		return -ENOMEM;
	}

	memset(cowdev->mapcache, 0, cowdev->mapcount * sizeof(char *));

	/*
	** allocate space to store the bitmap-chunks themselves
	*/
	for (i=0; i < cowdev->mapcount; i++) {
		if (i < (cowdev->mapcount-1))
			*(cowdev->mapcache+i) = kmalloc(MAPCHUNKSZ, GFP_KERNEL);
		else
			*(cowdev->mapcache+i) = kmalloc(cowdev->mapremain,
						                  GFP_KERNEL);

		if (*(cowdev->mapcache+i) == NULL) {
			printk(KERN_ERR "cowloop - no space for bitmapchunk %ld"
					" totmapsz=%ld, mapcnt=%d mapunit=%d\n",
					i, cowdev->mapsize, cowdev->mapcount,
					MAPUNIT);
			return -ENOMEM;
		}
	}

	DEBUGP(DCOW"cowloop - read bitmap from cow....\n");

	/*
	** read the entire bitmap from the cowfile into the in-memory cache;
	** count the number of blocks that are in use already
	** (statistical purposes)
	*/
	for (i=0, offset=MAPUNIT; i < cowdev->mapcount;
					i++, offset+=MAPCHUNKSZ) {
		unsigned long	numbytes;

		if (i < (cowdev->mapcount-1))
			/*
			** full bitmap chunk
			*/
			numbytes = MAPCHUNKSZ;
		else
			/*
			** last bitmap chunk: might be partly filled
			*/
			numbytes = cowdev->mapremain;

		cowlo_readcowraw(cowdev, *(cowdev->mapcache+i),
							numbytes, offset);
	}

	/*
	** if the cowfile was dirty and automatic recovery is required,
	** reconstruct a proper bitmap in memory now
	*/
	if (cowdev->cowhead->flags & COWDIRTY) {
		unsigned long long	blocknum;
		char			databuf[MAPUNIT];
		unsigned long		mapnum, mapbyte, mapbit;

		printk(KERN_NOTICE "cowloop - recover dirty cowfile %s....\n",
							cowf);

		/*
		** read all data blocks
		*/
		for (blocknum=0, rv=1, offset=0;
			cowlo_readcow(cowdev, databuf, MAPUNIT, offset) > 0;
			blocknum++, offset += MAPUNIT) {

			/*
			** if this datablock contains real data (not binary
			** zeroes), set the corresponding bit in the bitmap
			*/
			if ( memcmp(databuf, allzeroes, MAPUNIT) == 0)
				continue;

			mapnum  = CALCMAP (blocknum);
			mapbyte = CALCBYTE(blocknum);
			mapbit  = CALCBIT (blocknum);

			*(*(cowdev->mapcache+mapnum)+mapbyte) |= (1<<mapbit);
		}

		printk(KERN_NOTICE "cowloop - cowfile recovery completed\n");
	}

	/*
	** count all bits set in the bitmaps for statistical purposes
	*/
	for (i=0, cowdev->nrcowblocks = 0; i < cowdev->mapcount; i++) {
		long	numbytes;
		char	*p;

		if (i < (cowdev->mapcount-1))
			numbytes = MAPCHUNKSZ;
		else
			numbytes = cowdev->mapremain;

		p = *(cowdev->mapcache+i);

		for (numbytes--; numbytes >= 0; numbytes--, p++) {
			/*
			** for only eight checks the following construction
			** is faster than a loop-construction
			*/
			if ((*p) & 0x01)	cowdev->nrcowblocks++;
			if ((*p) & 0x02)	cowdev->nrcowblocks++;
			if ((*p) & 0x04)	cowdev->nrcowblocks++;
			if ((*p) & 0x08)	cowdev->nrcowblocks++;
			if ((*p) & 0x10)	cowdev->nrcowblocks++;
			if ((*p) & 0x20)	cowdev->nrcowblocks++;
			if ((*p) & 0x40)	cowdev->nrcowblocks++;
			if ((*p) & 0x80)	cowdev->nrcowblocks++;
		}
	}

	/*
	** consistency-check for number of bits set in bitmap
	*/
	if ( !(cowdev->cowhead->flags & COWDIRTY) &&
	    (cowdev->cowhead->cowused != cowdev->nrcowblocks) ) {
		printk(KERN_ERR "cowloop - inconsistent cowfile admi\n");
		return -EINVAL;
	}

	return 0;
}

/*
** undo memory allocs and file opens issued so far
** related to the cowfile
*/
static void
cowlo_undo_opencow(struct cowloop_device *cowdev)
{
	int	i;

	if (cowdev->mapcache) {
		for (i=0; i < cowdev->mapcount; i++) {
			if (*(cowdev->mapcache+i) != NULL)
				kfree( *(cowdev->mapcache+i) );
		}

		kfree(cowdev->mapcache);
	}

	if (cowdev->cowhead)
		kfree(cowdev->cowhead);

	if ( (cowdev->state & COWCOWOPEN) && (cowdev->cowfp) )
  		filp_close(cowdev->cowfp, 0);

	/*
	** mark cowfile closed
	*/
	cowdev->state &= ~COWCOWOPEN;
}

/*
** flush the entire bitmap and the cowhead (clean) to the cowfile
**
** must be called with the cowdevices-lock set
*/
static void
cowlo_sync(void)
{
	int			i, minor;
	loff_t			offset;
	struct cowloop_device	*cowdev;

	for (minor=0; minor < maxcows;  minor++) {
		cowdev = cowdevall[minor];
		if ( ! (cowdev->state & COWRWCOWOPEN) )
			continue;

		for (i=0, offset=MAPUNIT; i < cowdev->mapcount;
					i++, offset += MAPCHUNKSZ) {
			unsigned long	numbytes;

			if (i < (cowdev->mapcount-1))
				/*
				** full bitmap chunk
				*/
				numbytes = MAPCHUNKSZ;
			else
				/*
				** last bitmap chunk: might be partly filled
				*/
				numbytes = cowdev->mapremain;

			DEBUGP(DCOW
			       "cowloop - flushing bitmap %2d (%3ld Kb)\n",
							i, numbytes/1024);

			if (cowlo_writecowraw(cowdev, *(cowdev->mapcache+i),
						numbytes, offset) < numbytes) {
				break;
			}
		}

		/*
		** flush clean up-to-date cowhead to cowfile
		*/
		cowdev->cowhead->cowused	 = cowdev->nrcowblocks;
		cowdev->cowhead->flags		&= ~COWDIRTY;

		DEBUGP(DCOW "cowloop - flushing cowhead (%3d Kb)\n",
							MAPUNIT/1024);

		cowlo_writecowraw(cowdev, cowdev->cowhead, MAPUNIT, (loff_t) 0);
	}
}

/*****************************************************************************/
/* Module loading/unloading                                                  */
/*****************************************************************************/

/*
** called during insmod/modprobe
*/
static int __init
cowlo_init_module(void)
{
	int	rv;
	int	minor, uptocows;

        revision[sizeof revision - 3] = '\0';

        printk(KERN_NOTICE "cowloop - (C) 2009 ATComputing.nl - version: %s\n", &revision[11]);
        printk(KERN_NOTICE "cowloop - info: www.ATComputing.nl/cowloop\n");

	memset(allzeroes, 0, MAPUNIT);

	/*
	** Setup administration for all possible cowdevices.
        ** Note that their minor numbers go from 0 to MAXCOWS-1 inclusive
        ** and minor == MAXCOWS-1 is reserved for the control device.
	*/
	if ((maxcows < 1) || (maxcows > MAXCOWS)) {
		printk(KERN_WARNING
		       "cowloop - maxcows exceeds maximum of %d\n", MAXCOWS);

                maxcows = DFLCOWS;
        }

	/* allocate room for a table with a pointer to each cowloop_device: */
        if ( (cowdevall = kmalloc(maxcows * sizeof(struct cowloop_device *),
							GFP_KERNEL)) == NULL) {
		printk(KERN_WARNING
		        "cowloop - can not alloc table for %d devs\n", maxcows);
		uptocows = 0;
		rv = -ENOMEM;
		goto error_out;
	}
	memset(cowdevall, 0, maxcows * sizeof(struct cowloop_device *));
	/* then hook an actual cowloop_device struct to each pointer: */
	for (minor=0; minor < maxcows; minor++) {
		if ((cowdevall[minor] = kmalloc(sizeof(struct cowloop_device),
						GFP_KERNEL)) == NULL) {
			printk(KERN_WARNING
		           "cowloop - can not alloc admin-struct for dev no %d\n", minor);

			uptocows = minor; /* this is how far we got.... */
			rv = -ENOMEM;
			goto error_out;
		}
        	memset(cowdevall[minor], 0, sizeof(struct cowloop_device));
	}
	uptocows = maxcows; /* we got all devices */

	sema_init(&cowdevlock, 1);

	/*
	** register cowloop module
	*/
	if ( register_blkdev(COWMAJOR, DEVICE_NAME) < 0) {
		printk(KERN_WARNING
		    "cowloop - unable to get major %d for cowloop\n", COWMAJOR);
		rv = -EIO;
		goto error_out;
	}

	/*
	** create a directory below /proc to allocate a file
	** for each cowdevice that is allocated later on
	*/
	cowlo_procdir = proc_mkdir("cow", NULL);

	/*
	** check if a cowdevice has to be opened during insmod/modprobe;
	** two parameters should be specified then: rdofile= and cowfile=
	*/
	if( (rdofile[0] != '\0') && (cowfile[0] != '\0') ) {
		char	*po = option;
		int	wantrecover = 0;

		/*
		** check if automatic recovery is wanted
		*/
		while (*po) {
			if (*po == 'r') {
				wantrecover = 1;
				break;
                        }
			po++;
		}

		/*
		** open new cowdevice with minor number 0
		*/
		if ( (rv = cowlo_openpair(rdofile, cowfile, wantrecover, 0))) {
			remove_proc_entry("cow", NULL);
			unregister_blkdev(COWMAJOR, DEVICE_NAME);
			goto error_out;
		}
        } else {
		/*
		** check if only one parameter has been specified
		*/
		if( (rdofile[0] != '\0') || (cowfile[0] != '\0') ) {
			printk(KERN_ERR
			       "cowloop - only one filename specified\n");
			remove_proc_entry("cow", NULL);
			unregister_blkdev(COWMAJOR, DEVICE_NAME);
			rv = -EINVAL;
			goto error_out;
		}
	}

	/*
	** allocate fake disk as control channel to handle the requests
	** to activate and deactivate cowdevices dynamically
	*/
	if (!(cowctlgd = alloc_disk(1))) {
		printk(KERN_WARNING
		       "cowloop - unable to alloc_disk for cowctl\n");

		remove_proc_entry("cow", NULL);
		(void) cowlo_closepair(cowdevall[0]);
		unregister_blkdev(COWMAJOR, DEVICE_NAME);
		rv = -ENOMEM;
		goto error_out;
	}

	spin_lock_init(&cowctlrqlock);
	cowctlgd->major        = COWMAJOR;
	cowctlgd->first_minor  = COWCTL;
	cowctlgd->minors       = 1;
	cowctlgd->fops         = &cowlo_fops;
	cowctlgd->private_data = NULL;
	/* the device has capacity 0, so there will be no q-requests */
	cowctlgd->queue = blk_init_queue(NULL, &cowctlrqlock);
	sprintf(cowctlgd->disk_name, "cowctl");
	set_capacity(cowctlgd, 0);

	add_disk(cowctlgd);

        printk(KERN_NOTICE "cowloop - number of configured cowdevices: %d\n",
								maxcows);
	if (rdofile[0] != '\0') {
	    printk(KERN_NOTICE "cowloop - initialized on rdofile=%s\n",
								rdofile);
	} else {
	    printk(KERN_NOTICE "cowloop - initialized without rdofile yet\n");
	}
	return 0;

error_out:
	for (minor=0; minor < uptocows ; minor++) {
		kfree(cowdevall[minor]);
	}
	kfree(cowdevall);
	return rv;
}

/*
** called during rmmod
*/
static void __exit
cowlo_cleanup_module(void)
{
	int	minor;

	/*
	** flush bitmaps and cowheads to the cowfiles
	*/
	down(&cowdevlock);
	cowlo_sync();
	up(&cowdevlock);

	/*
	** close all cowdevices
	*/
	for (minor=0; minor < maxcows;  minor++)
		(void) cowlo_closepair(cowdevall[minor]);

	unregister_blkdev(COWMAJOR, DEVICE_NAME);

	/*
	** get rid of /proc/cow and unregister the driver
	*/
	remove_proc_entry("cow", NULL);

	for (minor = 0; minor < maxcows; minor++) {
		kfree(cowdevall[minor]);
	}
	kfree(cowdevall);

	del_gendisk(cowctlgd);  /* revert the alloc_disk() */
	put_disk   (cowctlgd);  /* revert the add_disk()   */
	blk_cleanup_queue(cowctlgd->queue); /* cleanup the empty queue */

	printk(KERN_NOTICE "cowloop - unloaded\n");
}

module_init(cowlo_init_module);
module_exit(cowlo_cleanup_module);
