--- 2.4.3aa/include/linux/lvm.h.orig	Mon Apr  2 20:33:32 2001
+++ 2.4.3aa/include/linux/lvm.h	Fri Apr  6 04:37:24 2001
@@ -3,28 +3,28 @@
  * kernel/lvm.h
  * tools/lib/lvm.h
  *
- * Copyright (C) 1997 - 2000  Heinz Mauelshagen, Sistina Software
+ * Copyright (C) 1997 - 2001  Heinz Mauelshagen, Sistina Software
  *
  * February-November 1997
  * May-July 1998
  * January-March,July,September,October,Dezember 1999
  * January,February,July,November 2000
- * January 2001
+ * January-March 2001
  *
  * lvm 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.
- * 
+ *
  * lvm 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 GNU CC; see the file COPYING.  If not, write to
  * the Free Software Foundation, 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA. 
+ * Boston, MA 02111-1307, USA.
  *
  */
 
@@ -52,7 +52,7 @@
  *    08/12/1999 - changed LVM_LV_SIZE_MAX macro to reflect current 1TB limit
  *    01/01/2000 - extended lv_v2 core structure by wait_queue member
  *    12/02/2000 - integrated Andrea Arcagnelli's snapshot work
- *    18/02/2000 - seperated user and kernel space parts by 
+ *    18/02/2000 - seperated user and kernel space parts by
  *                 #ifdef them with __KERNEL__
  *    08/03/2000 - implemented cluster/shared bits for vg_access
  *    26/06/2000 - implemented snapshot persistency and resizing support
@@ -60,6 +60,13 @@
  *    12/11/2000 - removed unneeded timestamp definitions
  *    24/12/2000 - removed LVM_TO_{CORE,DISK}*, use cpu_{from, to}_le*
  *                 instead - Christoph Hellwig
+ *    22/01/2001 - Change ulong to uint32_t
+ *    14/02/2001 - changed LVM_SNAPSHOT_MIN_CHUNK to 1 page
+ *    20/02/2001 - incremented IOP version to 11 because of incompatible
+ *                 change in VG activation (in order to support devfs better)
+ *    01/03/2001 - Revert to IOP10 and add VG_CREATE_OLD call for compatibility
+ *    08/03/2001 - new lv_t (in core) version number 5: changed page member
+ *                 to (struct kiobuf *) to use for COW exception table io
  *
  */
 
@@ -67,9 +74,11 @@
 #ifndef _LVM_H_INCLUDE
 #define _LVM_H_INCLUDE
 
-#define	_LVM_KERNEL_H_VERSION	"LVM 0.9.1_beta2 (18/01/2001)"
+#define LVM_RELEASE_NAME "0.9.1_beta6"
+#define LVM_RELEASE_DATE "12/03/2001"
+
+#define	_LVM_KERNEL_H_VERSION	"LVM "LVM_RELEASE_NAME" ("LVM_RELEASE_DATE")"
 
-#include <linux/config.h>
 #include <linux/version.h>
 
 /*
@@ -90,13 +99,20 @@
    #define DEBUG_READ
    #define DEBUG_GENDISK
    #define DEBUG_VG_CREATE
-   #define DEBUG_LVM_BLK_OPEN
+   #define DEBUG_DEVICE
    #define DEBUG_KFREE
  */
 #endif				/* #ifdef __KERNEL__ */
 
+#ifndef __KERNEL__
+#define __KERNEL__
+#include <linux/kdev_t.h>
+#include <linux/list.h>
+#undef __KERNEL__
+#else
 #include <linux/kdev_t.h>
 #include <linux/list.h>
+#endif				/* #ifndef __KERNEL__ */
 
 #include <asm/types.h>
 #include <linux/major.h>
@@ -117,10 +133,10 @@
 #undef	BLOCK_SIZE
 #endif
 
-#ifdef CONFIG_ARCH_S390 
+#ifdef CONFIG_ARCH_S390
 #define BLOCK_SIZE	4096
 #else
-#define BLOCK_SIZE	1024
+#define BLOCK_SIZE	512
 #endif
 
 #ifndef	SECTOR_SIZE
@@ -141,7 +157,7 @@
 /* set the default structure version */
 #if ( LVM_STRUCT_VERSION == 1)
 #define pv_t pv_v2_t
-#define lv_t lv_v4_t
+#define lv_t lv_v5_t
 #define vg_t vg_v3_t
 #define pv_disk_t pv_disk_v2_t
 #define lv_disk_t lv_disk_v3_t
@@ -299,7 +315,7 @@
 
 #define	LVM_SNAPSHOT_MAX_CHUNK	1024	/* 1024 KB */
 #define	LVM_SNAPSHOT_DEF_CHUNK	64	/* 64  KB */
-#define	LVM_SNAPSHOT_MIN_CHUNK	1	/* 1   KB */
+#define	LVM_SNAPSHOT_MIN_CHUNK	(PAGE_SIZE/1024)	/* 4 or 8 KB */
 
 #define	UNDEF	-1
 #define FALSE	0
@@ -323,7 +339,7 @@
  * ioctls
  */
 /* volume group */
-#define	VG_CREATE               _IOW ( 0xfe, 0x00, 1)
+#define	VG_CREATE_OLD           _IOW ( 0xfe, 0x00, 1)
 #define	VG_REMOVE               _IOW ( 0xfe, 0x01, 1)
 
 #define	VG_EXTEND               _IOW ( 0xfe, 0x03, 1)
@@ -336,6 +352,8 @@
 #define	VG_SET_EXTENDABLE       _IOW ( 0xfe, 0x08, 1)
 #define	VG_RENAME		_IOW ( 0xfe, 0x09, 1)
 
+/* Since 0.9beta6 */
+#define	VG_CREATE               _IOW ( 0xfe, 0x0a, 1)
 
 /* logical volume */
 #define	LV_CREATE               _IOW ( 0xfe, 0x20, 1)
@@ -418,6 +436,9 @@
 #define	PV_ALLOCATABLE       0x02	/* pv_allocatable */
 
 
+/* misc */
+#define LVM_SNAPSHOT_DROPPED_SECTOR 1
+
 /*
  * Structure definitions core/disk follow
  *
@@ -440,10 +461,10 @@
 /* remap physical sector/rdev pairs including hash */
 typedef struct {
 	struct list_head hash;
-	ulong rsector_org;
-	kdev_t rdev_org;
-	ulong rsector_new;
-	kdev_t rdev_new;
+	uint32_t rsector_org;
+	kdev_t   rdev_org;
+	uint32_t rsector_new;
+	kdev_t   rdev_new;
 } lv_block_exception_v1_t;
 
 /* disk stored pe information */
@@ -571,21 +592,21 @@
 /* core PE information */
 typedef struct {
 	kdev_t dev;
-	ulong pe;		/* to be changed if > 2TB */
-	ulong reads;
-	ulong writes;
+	uint32_t pe;		/* to be changed if > 2TB */
+	uint32_t reads;
+	uint32_t writes;
 } pe_t;
 
 typedef struct {
 	char lv_name[NAME_LEN];
 	kdev_t old_dev;
 	kdev_t new_dev;
-	ulong old_pe;
-	ulong new_pe;
+	uint32_t old_pe;
+	uint32_t new_pe;
 } le_remap_req_t;
 
 typedef struct lv_bmap {
-	ulong lv_block;
+	uint32_t lv_block;
 	dev_t lv_dev;
 } lv_bmap_t;
 
@@ -627,11 +648,11 @@
 	uint lv_snapshot_minor;
 #ifdef __KERNEL__
 	struct kiobuf *lv_iobuf;
+	struct kiobuf *lv_COW_table_iobuf;
 	struct semaphore lv_snapshot_sem;
 	struct list_head *lv_snapshot_hash_table;
-	ulong lv_snapshot_hash_table_size;
-	ulong lv_snapshot_hash_mask;
-	struct page *lv_COW_table_page;
+	uint32_t lv_snapshot_hash_table_size;
+	uint32_t lv_snapshot_hash_mask;
 	wait_queue_head_t lv_snapshot_wait;
 	int	lv_snapshot_use_rate;
 	void	*vg;
@@ -640,7 +661,7 @@
 #else
 	char dummy[200];
 #endif
-} lv_v4_t;
+} lv_v5_t;
 
 /* disk */
 typedef struct {
@@ -791,7 +812,7 @@
 	struct {
 		kdev_t lv_dev;
 		kdev_t pv_dev;
-		ulong pv_offset;
+		uint32_t pv_offset;
 	} data;
 } pe_lock_req_t;
 
@@ -804,7 +825,7 @@
 
 /* Request structure LV_STATUS_BYINDEX */
 typedef struct {
-	ulong lv_index;
+	uint32_t lv_index;
 	lv_t *lv;
 	/* Transfer size because user space and kernel space differ */
 	ushort size;
@@ -813,7 +834,7 @@
 /* Request structure LV_STATUS_BYDEV... */
 typedef struct {
 	dev_t dev;
-	pv_t *lv;
+	lv_t *lv;
 } lv_status_bydev_req_t;
 
 
--- 2.4.3aa/drivers/md/lvm.c.orig	Sat Feb 10 02:34:09 2001
+++ 2.4.3aa/drivers/md/lvm.c	Fri Apr  6 04:37:24 2001
@@ -1,13 +1,13 @@
 /*
  * kernel/lvm.c
  *
- * Copyright (C) 1997 - 2000  Heinz Mauelshagen, Sistina Software
+ * Copyright (C) 1997 - 2001  Heinz Mauelshagen, Sistina Software
  *
  * February-November 1997
  * April-May,July-August,November 1998
  * January-March,May,July,September,October 1999
  * January,February,July,September-November 2000
- * January 2001
+ * January,February,March 2001
  *
  *
  * LVM driver is free software; you can redistribute it and/or modify
@@ -43,7 +43,8 @@
  *                 support for free (eg. longer) logical volume names
  *    12/05/1998 - added spin_locks (thanks to Pascal van Dam
  *                 <pascal@ramoth.xs4all.nl>)
- *    25/05/1998 - fixed handling of locked PEs in lvm_map() and lvm_chr_ioctl()
+ *    25/05/1998 - fixed handling of locked PEs in lvm_map() and
+ *                 lvm_chr_ioctl()
  *    26/05/1998 - reactivated verify_area by access_ok
  *    07/06/1998 - used vmalloc/vfree instead of kmalloc/kfree to go
  *                 beyond 128/256 KB max allocation limit per call
@@ -72,7 +73,7 @@
  *                 only other update ioctls are blocked now
  *               - fixed pv->pe to NULL for pv_status
  *               - using lv_req structure in lvm_chr_ioctl() now
- *               - fixed NULL ptr reference bug in lvm_do_lv_extend_reduce()
+ *               - fixed NULL ptr reference bug in lvm_do_lv_extendreduce()
  *                 caused by uncontiguous PV array in lvm_chr_ioctl(VG_REDUCE)
  *    09/02/1999 - changed BLKRASET and BLKRAGET in lvm_chr_ioctl() to
  *                 handle lgoical volume private read ahead sector
@@ -125,7 +126,8 @@
  *    14/02/2000 - support for 2.3.43
  *               - integrated Andrea Arcagneli's snapshot code
  *    25/06/2000 - james (chip) , IKKHAYD! roffl
- *    26/06/2000 - enhanced lv_extend_reduce for snapshot logical volume support
+ *    26/06/2000 - enhanced lv_extend_reduce for snapshot logical volume
+ *                 support
  *    06/09/2000 - added devfs support
  *    07/09/2000 - changed IOP version to 9
  *               - started to add new char ioctl LV_STATUS_BYDEV_T to support
@@ -148,32 +150,67 @@
  *                 procfs is always supported now. (JT)
  *    12/01/2001 - avoided flushing logical volume in case of shrinking
  *                 because of unecessary overhead in case of heavy updates
+ *    25/01/2001 - Allow RO open of an inactive LV so it can be reactivated.
+ *    31/01/2001 - removed blk_init_queue/blk_cleanup_queue queueing will be
+ *                 handled by the proper devices.
+ *               - If you try and BMAP a snapshot you now get an -EPERM
+ *    01/01/2001 - lvm_map() now calls buffer_IO_error on error for 2.4
+ *               - factored __remap_snapshot out of lvm_map
+ *    12/02/2001 - move devfs code to create VG before LVs
+ *    13/02/2001 - allow VG_CREATE on /dev/lvm
+ *    14/02/2001 - removed modversions.h
+ *               - tidied device defines for blk.h
+ *               - tidied debug statements
+ *               - bug: vg[] member not set back to NULL if activation fails
+ *               - more lvm_map tidying
+ *    15/02/2001 - register /dev/lvm with devfs correctly (major/minor
+ *                 were swapped)
+ *    19/02/2001 - preallocated buffer_heads for rawio when using
+ *                 snapshots [JT]
+ *    28/02/2001 - introduced the P_DEV macro and changed some internel
+ *                 functions to be static [AD]
+ *    28/02/2001 - factored lvm_get_snapshot_use_rate out of blk_ioctl [AD]
+ *               - fixed user address accessing bug in lvm_do_lv_create()
+ *                 where the check for an existing LV takes place right at
+ *                 the beginning
+ *    01/03/2001 - Add VG_CREATE_OLD for IOP 10 compatibility
+ *    02/03/2001 - Don't destroy usermode pointers in lv_t structures duing
+ *                 LV_STATUS_BYxxx
+ *                 and remove redundant lv_t variables from same.
+ *               - avoid compilation of lvm_dummy_device_request in case of
+ *                 Linux >= 2.3.0 to avoid a warning
+ *               - added lvm_name argument to printk in buffer allocation
+ *                 in order to avoid a warning
+ *    04/03/2001 - moved linux/version.h above first use of KERNEL_VERSION
+ *                 macros
+ *    05/03/2001 - restore copying pe_t array in lvm_do_lv_status_byname. For
+ *                 lvdisplay -v (PC)
+ *               - restore copying pe_t array in lvm_do_lv_status_byindex (HM)
+ *               - added copying pe_t array in lvm_do_lv_status_bydev (HM)
+ *               - enhanced lvm_do_lv_status_by{name,index,dev} to be capable
+ *                 to copy the lv_block_exception_t array to userspace (HM)
+ *    08/03/2001 - initialize new lv_ptr->lv_COW_table_iobuf for snapshots;
+ *                 removed obsolete lv_ptr->lv_COW_table_page initialization
+ *               - factored lvm_do_pv_flush out of lvm_chr_ioctl (HM)
+ *    09/03/2001 - Added _lock_open_count to ensure we only drop the lock
+ *                 when the locking process closes.
  *
  */
 
+#include <linux/version.h>
 
-static char *lvm_version = "LVM version 0.9.1_beta2  by Heinz Mauelshagen  (18/01/2001)\n";
-static char *lvm_short_version = "version 0.9.1_beta2 (18/01/2001)";
-
-#define MAJOR_NR	LVM_BLK_MAJOR
-#define	DEVICE_OFF(device)
+#define MAJOR_NR LVM_BLK_MAJOR
+#define DEVICE_OFF(device)
+#define LOCAL_END_REQUEST
 
 /* lvm_do_lv_create calls fsync_dev_lockfs()/unlockfs() */
 /* #define	LVM_VFS_ENHANCEMENT */
 
 #include <linux/config.h>
-#include <linux/version.h>
-
-#ifdef MODVERSIONS
-#undef MODULE
-#define MODULE
-#include <linux/modversions.h>
-#endif
-
 #include <linux/module.h>
-
 #include <linux/kernel.h>
 #include <linux/vmalloc.h>
+
 #include <linux/slab.h>
 #include <linux/init.h>
 
@@ -184,6 +221,8 @@
 #include <linux/blkdev.h>
 #include <linux/genhd.h>
 #include <linux/locks.h>
+
+
 #include <linux/smp_lock.h>
 #include <asm/ioctl.h>
 #include <asm/segment.h>
@@ -199,7 +238,7 @@
 #include <linux/errno.h>
 #include <linux/lvm.h>
 
-#include "lvm-snap.h"
+#include "lvm-internal.h"
 
 #define	LVM_CORRECT_READ_AHEAD( a) \
    if      ( a < LVM_MIN_READ_AHEAD || \
@@ -209,24 +248,6 @@
 #  define WRITEA WRITE
 #endif
 
-/* debug macros */
-#ifdef DEBUG_IOCTL
-#define P_IOCTL(fmt, args...) printk(KERN_DEBUG "lvm ioctl: " fmt, ## args)
-#else
-#define P_IOCTL(fmt, args...)
-#endif
-
-#ifdef DEBUG_MAP
-#define P_MAP(fmt, args...) printk(KERN_DEBUG "lvm map: " fmt, ## args)
-#else
-#define P_MAP(fmt, args...)
-#endif
-
-#ifdef DEBUG_KFREE
-#define P_KFREE(fmt, args...) printk(KERN_DEBUG "lvm kfree: " fmt, ## args)
-#else
-#define P_KFREE(fmt, args...)
-#endif
 
 /*
  * External function prototypes
@@ -236,27 +257,14 @@
 static int lvm_blk_ioctl(struct inode *, struct file *, uint, ulong);
 static int lvm_blk_open(struct inode *, struct file *);
 
-static int lvm_chr_open(struct inode *, struct file *);
-
-static int lvm_chr_close(struct inode *, struct file *);
 static int lvm_blk_close(struct inode *, struct file *);
+static int lvm_get_snapshot_use_rate(lv_t *lv_ptr, void *arg);
 static int lvm_user_bmap(struct inode *, struct lv_bmap *);
 
+static int lvm_chr_open(struct inode *, struct file *);
+static int lvm_chr_close(struct inode *, struct file *);
 static int lvm_chr_ioctl(struct inode *, struct file *, uint, ulong);
 
-int lvm_proc_read_vg_info(char *, char **, off_t, int, int *, void *);
-int lvm_proc_read_lv_info(char *, char **, off_t, int, int *, void *);
-int lvm_proc_read_pv_info(char *, char **, off_t, int, int *, void *);
-static int lvm_proc_get_global_info(char *, char **, off_t, int, int *, void *);
-
-void lvm_do_create_devfs_entry_of_vg ( vg_t *);
-
-void lvm_do_create_proc_entry_of_vg ( vg_t *);
-void lvm_do_remove_proc_entry_of_vg ( vg_t *);
-void lvm_do_create_proc_entry_of_lv ( vg_t *, lv_t *);
-void lvm_do_remove_proc_entry_of_lv ( vg_t *, lv_t *);
-void lvm_do_create_proc_entry_of_pv ( vg_t *, pv_t *);
-void lvm_do_remove_proc_entry_of_pv ( vg_t *, pv_t *);
 
 /* End external function prototypes */
 
@@ -288,33 +296,38 @@
 
 static int lvm_do_pv_change(vg_t*, void*);
 static int lvm_do_pv_status(vg_t *, void *);
+static int lvm_do_pv_flush(void *);
 
-static int lvm_do_vg_create(int, void *);
+static int lvm_do_vg_create(void *, int minor);
 static int lvm_do_vg_extend(vg_t *, void *);
 static int lvm_do_vg_reduce(vg_t *, void *);
 static int lvm_do_vg_rename(vg_t *, void *);
 static int lvm_do_vg_remove(int);
 static void lvm_geninit(struct gendisk *);
-static char *lvm_show_uuid ( char *);
+static void __update_hardblocksize(lv_t *lv);
+
+
 #ifdef LVM_HD_NAME
 void lvm_hd_name(char *, int);
 #endif
 /* END Internal function prototypes */
 
 
-/* volume group descriptor area pointers */
-static vg_t *vg[ABS_MAX_VG];
+/* variables */
+char *lvm_version = "LVM version "LVM_RELEASE_NAME"  by Heinz Mauelshagen  "
+                    "("LVM_RELEASE_DATE")\n";
+char *lvm_short_version = "version "LVM_RELEASE_NAME" ("LVM_RELEASE_DATE")";
+ushort lvm_iop_version = LVM_DRIVER_IOP_VERSION;
+int loadtime = 0;
+const char *const lvm_name = LVM_NAME;
 
-static devfs_handle_t lvm_devfs_handle;
-static devfs_handle_t vg_devfs_handle[MAX_VG];
-static devfs_handle_t ch_devfs_handle[MAX_VG];
-static devfs_handle_t lv_devfs_handle[MAX_LV];
+
+/* volume group descriptor area pointers */
+vg_t *vg[ABS_MAX_VG];
 
 static pv_t *pvp = NULL;
 static lv_t *lvp = NULL;
 static pe_t *pep = NULL;
-static pe_t *pep1 = NULL;
-static char *basename = NULL;
 
 
 /* map from block minor number to VG and LV numbers */
@@ -327,7 +340,6 @@
 
 /* Request structures (lvm_chr_ioctl()) */
 static pv_change_req_t pv_change_req;
-static pv_flush_req_t pv_flush_req;
 static pv_status_req_t pv_status_req;
 static pe_lock_req_t pe_lock_req;
 static le_remap_req_t le_remap_req;
@@ -339,32 +351,24 @@
 
 static char pv_name[NAME_LEN];
 /* static char rootvg[NAME_LEN] = { 0, }; */
-const char *const lvm_name = LVM_NAME;
 static int lock = 0;
-static int loadtime = 0;
+static int _lock_open_count = 0;
 static uint vg_count = 0;
 static long lvm_chr_open_count = 0;
-static ushort lvm_iop_version = LVM_DRIVER_IOP_VERSION;
 static DECLARE_WAIT_QUEUE_HEAD(lvm_wait);
 static DECLARE_WAIT_QUEUE_HEAD(lvm_map_wait);
 
 static spinlock_t lvm_lock = SPIN_LOCK_UNLOCKED;
 static spinlock_t lvm_snapshot_lock = SPIN_LOCK_UNLOCKED;
 
-static struct proc_dir_entry *lvm_proc_dir = NULL;
-static struct proc_dir_entry *lvm_proc_vg_subdir = NULL;
-struct proc_dir_entry *pde = NULL;
-
-static struct file_operations lvm_chr_fops =
-{
+struct file_operations lvm_chr_fops = {
 	open:		lvm_chr_open,
 	release:	lvm_chr_close,
 	ioctl:		lvm_chr_ioctl,
 };
 
-
 /* block device operations structure needed for 2.3.38? and above */
-static struct block_device_operations lvm_blk_dops =
+struct block_device_operations lvm_blk_dops =
 {
 	open: 		lvm_blk_open,
 	release:	lvm_blk_close,
@@ -374,10 +378,10 @@
 
 /* gendisk structures */
 static struct hd_struct lvm_hd_struct[MAX_LV];
-static int lvm_blocksizes[MAX_LV] =
-{0,};
-static int lvm_size[MAX_LV] =
-{0,};
+static int lvm_blocksizes[MAX_LV];
+static int lvm_hardsectsizes[MAX_LV];
+static int lvm_size[MAX_LV];
+
 static struct gendisk lvm_gendisk =
 {
 	MAJOR_NR,		/* major # */
@@ -412,18 +416,7 @@
 		return -EIO;
 	}
 
-	lvm_devfs_handle = devfs_register(
-		0 , "lvm", 0, 0, LVM_CHAR_MAJOR,
-		S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP,
-		&lvm_chr_fops, NULL);
-
-	lvm_proc_dir = create_proc_entry (LVM_DIR, S_IFDIR, &proc_root);
-	if (lvm_proc_dir != NULL) {
-		lvm_proc_vg_subdir = create_proc_entry (LVM_VG_SUBDIR, S_IFDIR, lvm_proc_dir);
-		pde = create_proc_entry(LVM_GLOBAL, S_IFREG, lvm_proc_dir);
-		if ( pde != NULL) pde->read_proc = &lvm_proc_get_global_info;
-	}
-
+	lvm_init_fs();
 	lvm_init_vars();
 	lvm_geninit(&lvm_gendisk);
 
@@ -467,16 +460,14 @@
 	return 0;
 } /* lvm_init() */
 
-
 /*
  * cleanup...
  */
+
 static void lvm_cleanup(void)
 {
 	struct gendisk *gendisk_ptr = NULL, *gendisk_ptr_prev = NULL;
 
-	devfs_unregister (lvm_devfs_handle);
-
 	if (unregister_chrdev(LVM_CHAR_MAJOR, lvm_name) < 0) {
 		printk(KERN_ERR "%s -- unregister_chrdev failed\n", lvm_name);
 	}
@@ -485,6 +476,7 @@
 	}
 
 
+
 	gendisk_ptr = gendisk_ptr_prev = gendisk_head;
 	while (gendisk_ptr != NULL) {
 		if (gendisk_ptr == &lvm_gendisk)
@@ -500,25 +492,23 @@
 	blksize_size[MAJOR_NR] = NULL;
 	hardsect_size[MAJOR_NR] = NULL;
 
-	remove_proc_entry(LVM_GLOBAL, lvm_proc_dir);
-	remove_proc_entry(LVM_VG_SUBDIR, lvm_proc_dir);
-	remove_proc_entry(LVM_DIR, &proc_root);
-
 #ifdef LVM_HD_NAME
 	/* reference from linux/drivers/block/genhd.c */
 	lvm_hd_name_ptr = NULL;
 #endif
 
+	/* unregister with procfs and devfs */
+	lvm_fin_fs();
+
 	printk(KERN_INFO "%s -- Module successfully deactivated\n", lvm_name);
 
 	return;
 }	/* lvm_cleanup() */
 
-
 /*
  * support function to initialize lvm variables
  */
-void __init lvm_init_vars(void)
+static void __init lvm_init_vars(void)
 {
 	int v;
 
@@ -554,16 +544,12 @@
 /*
  * character device open routine
  */
-static int lvm_chr_open(struct inode *inode,
-			struct file *file)
+static int lvm_chr_open(struct inode *inode, struct file *file)
 {
 	int minor = MINOR(inode->i_rdev);
 
-#ifdef DEBUG
-	printk(KERN_DEBUG
-	 "%s -- lvm_chr_open MINOR: %d  VG#: %d  mode: 0x%X  lock: %d\n",
-	       lvm_name, minor, VG_CHR(minor), file->f_mode, lock);
-#endif
+	P_DEV("%s -- lvm_chr_open MINOR: %d  VG#: %d  mode: 0x%X  lock: %d\n",
+	      lvm_name, minor, VG_CHR(minor), file->f_mode, lock);
 
 	/* super user validation */
 	if (!capable(CAP_SYS_ADMIN)) return -EACCES;
@@ -571,6 +557,11 @@
 	/* Group special file open */
 	if (VG_CHR(minor) > MAX_VG) return -ENXIO;
 
+	spin_lock(&lvm_lock);
+	if(lock == current->pid)
+		_lock_open_count++;
+	spin_unlock(&lvm_lock);
+
 	lvm_chr_open_count++;
 
 	MOD_INC_USE_COUNT;
@@ -587,7 +578,7 @@
  *
  */
 static int lvm_chr_ioctl(struct inode *inode, struct file *file,
-			 uint command, ulong a)
+		  uint command, ulong a)
 {
 	int minor = MINOR(inode->i_rdev);
 	uint extendable, l, v;
@@ -648,9 +639,13 @@
 		   physical volume (move's done in user space's pvmove) */
 		return lvm_do_pe_lock_unlock(vg_ptr,arg);
 
-	case VG_CREATE:
+	case VG_CREATE_OLD:
 		/* create a VGDA */
-		return lvm_do_vg_create(minor, arg);
+		return lvm_do_vg_create(arg, minor);
+
+	case VG_CREATE:
+	        /* create a VGDA, assume VG number is filled in */
+		return lvm_do_vg_create(arg, -1);
 
 	case VG_EXTEND:
 		/* extend a volume group */
@@ -701,7 +696,7 @@
 
 
 	case VG_STATUS_GET_NAMELIST:
-		/* get volume group count */
+		/* get volume group names */
 		for (l = v = 0; v < ABS_MAX_VG; v++) {
 			if (vg[v] != NULL) {
 				if (copy_to_user(arg + l * NAME_LEN,
@@ -756,6 +751,7 @@
 
 
 	case LV_STATUS_BYDEV:
+		/* get status of a logical volume by device */
 		return lvm_do_lv_status_bydev(vg_ptr, arg);
 
 
@@ -771,18 +767,12 @@
 
 	case PV_FLUSH:
 		/* physical volume buffer flush/invalidate */
-		if (copy_from_user(&pv_flush_req, arg,
-				   sizeof(pv_flush_req)) != 0)
-			return -EFAULT;
-
-		fsync_dev(pv_flush_req.pv_dev);
-		invalidate_buffers(pv_flush_req.pv_dev);
-		return 0;
+		return lvm_do_pv_flush(arg);
 
 
 	default:
 		printk(KERN_WARNING
-		       "%s -- lvm_chr_ioctl: unknown command %x\n",
+		       "%s -- lvm_chr_ioctl: unknown command 0x%x\n",
 		       lvm_name, command);
 		return -EINVAL;
 	}
@@ -794,7 +784,7 @@
 /*
  * character device close routine
  */
-static int lvm_chr_close(struct inode *inode, struct file *file)
+int lvm_chr_close(struct inode *inode, struct file *file)
 {
 #ifdef DEBUG
 	int minor = MINOR(inode->i_rdev);
@@ -810,10 +800,16 @@
 #endif
 
 	if (lvm_chr_open_count > 0) lvm_chr_open_count--;
-	if (lock == current->pid) {
-		lock = 0;	/* release lock */
-		wake_up_interruptible(&lvm_wait);
+
+	spin_lock(&lvm_lock);
+	if(lock == current->pid) {
+		if(!_lock_open_count) {
+			lock = 0;
+			wake_up_interruptible(&lvm_wait);
+		} else
+			_lock_open_count--;
 	}
+	spin_unlock(&lvm_lock);
 
 	MOD_DEC_USE_COUNT;
 
@@ -837,11 +833,8 @@
 	lv_t *lv_ptr;
 	vg_t *vg_ptr = vg[VG_BLK(minor)];
 
-#ifdef DEBUG_LVM_BLK_OPEN
-	printk(KERN_DEBUG
-	  "%s -- lvm_blk_open MINOR: %d  VG#: %d  LV#: %d  mode: 0x%X\n",
-	    lvm_name, minor, VG_BLK(minor), LV_BLK(minor), file->f_mode);
-#endif
+	P_DEV("%s -- lvm_blk_open MINOR: %d  VG#: %d  LV#: %d  mode: 0x%X\n",
+	      lvm_name, minor, VG_BLK(minor), LV_BLK(minor), file->f_mode);
 
 #ifdef LVM_TOTAL_RESET
 	if (lvm_reset_spindown > 0)
@@ -854,12 +847,16 @@
 	    LV_BLK(minor) >= 0 &&
 	    LV_BLK(minor) < vg_ptr->lv_max) {
 
-		/* Check parallel LV spindown (LV remove) */
+	        /* Check parallel LV spindown (LV remove) */
 		if (lv_ptr->lv_status & LV_SPINDOWN) return -EPERM;
 
 		/* Check inactive LV and open for read/write */
-		if (!(lv_ptr->lv_status & LV_ACTIVE))
-			return -EPERM;
+		/* We need to be able to "read" an inactive LV
+		   to re-activate it again */
+		if ((file->f_mode & FMODE_WRITE) &&
+		    (!(lv_ptr->lv_status & LV_ACTIVE)))
+		    return -EPERM;
+
 		if (!(lv_ptr->lv_access & LV_WRITE) &&
 		    (file->f_mode & FMODE_WRITE))
 			return -EACCES;
@@ -871,12 +868,7 @@
 
 		MOD_INC_USE_COUNT;
 
-#ifdef DEBUG_LVM_BLK_OPEN
-		printk(KERN_DEBUG
-		       "%s -- lvm_blk_open MINOR: %d  VG#: %d  LV#: %d  size: %d\n",
-		       lvm_name, minor, VG_BLK(minor), LV_BLK(minor),
-		       lv_ptr->lv_size);
-#endif
+		P_DEV("%s -- OPEN OK, LV size %d\n", lvm_name, lv_ptr->lv_size);
 
 		return 0;
 	}
@@ -896,7 +888,7 @@
 	void *arg = (void *) a;
 	struct hd_geometry *hd = (struct hd_geometry *) a;
 
-	P_IOCTL("%s -- lvm_blk_ioctl MINOR: %d  command: 0x%X  arg: %X  "
+	P_IOCTL("%s -- lvm_blk_ioctl MINOR: %d  command: 0x%X  arg: %lX  "
 		"VG#: %dl  LV#: %d\n",
 		lvm_name, minor, command, (ulong) arg,
 		VG_BLK(minor), LV_BLK(minor));
@@ -926,8 +918,8 @@
 		/* set read ahead for block device */
 		if (!capable(CAP_SYS_ADMIN)) return -EACCES;
 
-		P_IOCTL("%s -- lvm_blk_ioctl -- BLKRASET: %d sectors for %02X:%02X\n",
-			lvm_name, (long) arg, MAJOR(inode->i_rdev), minor);
+		P_IOCTL("%s -- lvm_blk_ioctl -- BLKRASET: %ld sectors for %s\n",
+			lvm_name, (long) arg, kdevname(inode->i_rdev));
 
 		if ((long) arg < LVM_MIN_READ_AHEAD ||
 		    (long) arg > LVM_MAX_READ_AHEAD)
@@ -964,10 +956,10 @@
 			    copy_to_user((long *) &hd->start, &start,
 					 sizeof(start)) != 0)
 				return -EFAULT;
-		}
 
-		P_IOCTL("%s -- lvm_blk_ioctl -- cylinders: %d\n",
-			lvm_name, lv_ptr->lv_size / heads / sectors);
+			P_IOCTL("%s -- lvm_blk_ioctl -- cylinders: %d\n",
+				lvm_name, cylinders);
+		}
 		break;
 
 
@@ -991,6 +983,11 @@
 		break;
 
 	case LV_BMAP:
+                /* turn logical block into (dev_t, block).  non privileged. */
+                /* don't bmap a snapshot, since the mapping can change */
+		if(lv_ptr->lv_access & LV_SNAPSHOT)
+			return -EPERM;
+
 		/* turn logical block into (dev_t, block). non privileged. */
 		return lvm_user_bmap(inode, (struct lv_bmap *) arg);
 		break;
@@ -1002,40 +999,11 @@
 		break;
 
 	case LV_SNAPSHOT_USE_RATE:
-		if (!(lv_ptr->lv_access & LV_SNAPSHOT)) return -EPERM;
-		{
-			lv_snapshot_use_rate_req_t	lv_snapshot_use_rate_req;
-
-			if (copy_from_user(&lv_snapshot_use_rate_req, arg,
-					   sizeof(lv_snapshot_use_rate_req_t)))
-				return -EFAULT;
-			if (lv_snapshot_use_rate_req.rate < 0 ||
-			    lv_snapshot_use_rate_req.rate  > 100) return -EFAULT;
-
-			switch (lv_snapshot_use_rate_req.block)
-			{
-			case 0:
-				lv_ptr->lv_snapshot_use_rate = lv_snapshot_use_rate_req.rate;
-				if (lv_ptr->lv_remap_ptr * 100 / lv_ptr->lv_remap_end < lv_ptr->lv_snapshot_use_rate)
-					interruptible_sleep_on (&lv_ptr->lv_snapshot_wait);
-				break;
-
-			case O_NONBLOCK:
-				break;
-
-			default:
-				return -EFAULT;
-			}
-			lv_snapshot_use_rate_req.rate = lv_ptr->lv_remap_ptr * 100 / lv_ptr->lv_remap_end;
-			if (copy_to_user(arg, &lv_snapshot_use_rate_req,
-					 sizeof(lv_snapshot_use_rate_req_t)))
-				return -EFAULT;
-		}
-		break;
+		return lvm_get_snapshot_use_rate(lv_ptr, arg);
 
 	default:
 		printk(KERN_WARNING
-		       "%s -- lvm_blk_ioctl: unknown command %d\n",
+		       "%s -- lvm_blk_ioctl: unknown command 0x%x\n",
 		       lvm_name, command);
 		return -EINVAL;
 	}
@@ -1068,6 +1036,38 @@
 	return 0;
 } /* lvm_blk_close() */
 
+static int lvm_get_snapshot_use_rate(lv_t *lv, void *arg)
+{
+	lv_snapshot_use_rate_req_t lv_rate_req;
+
+	if (!(lv->lv_access & LV_SNAPSHOT))
+		return -EPERM;
+
+	if (copy_from_user(&lv_rate_req, arg, sizeof(lv_rate_req)))
+		return -EFAULT;
+
+	if (lv_rate_req.rate < 0 || lv_rate_req.rate > 100)
+		return -EINVAL;
+
+	switch (lv_rate_req.block) {
+	case 0:
+		lv->lv_snapshot_use_rate = lv_rate_req.rate;
+		if (lv->lv_remap_ptr * 100 / lv->lv_remap_end <
+		    lv->lv_snapshot_use_rate)
+			interruptible_sleep_on(&lv->lv_snapshot_wait);
+		break;
+
+	case O_NONBLOCK:
+		break;
+
+	default:
+		return -EINVAL;
+	}
+	lv_rate_req.rate = lv->lv_remap_ptr * 100 / lv->lv_remap_end;
+
+	return copy_to_user(arg, &lv_rate_req,
+			    sizeof(lv_rate_req)) ? -EFAULT : 0;
+}
 
 static int lvm_user_bmap(struct inode *inode, struct lv_bmap *user_result)
 {
@@ -1079,398 +1079,16 @@
 		return -EFAULT;
 
 	memset(&bh,0,sizeof bh);
-	bh.b_rsector = block;
-	bh.b_dev = bh.b_rdev = inode->i_dev;
+	bh.b_blocknr = block;
+	bh.b_dev = bh.b_rdev = inode->i_rdev;
 	bh.b_size = lvm_get_blksize(bh.b_dev);
 	if ((err=lvm_map(&bh, READ)) < 0)  {
 		printk("lvm map failed: %d\n", err);
 		return -EINVAL;
 	}
 
-	return put_user(kdev_t_to_nr(bh.b_rdev), &user_result->lv_dev) ||
-	put_user(bh.b_rsector, &user_result->lv_block) ? -EFAULT : 0;
-}
-
-
-/*
- * provide VG info for proc filesystem use (global)
- */
-int lvm_vg_info(vg_t *vg_ptr, char *buf) {
-	int sz = 0;
-	char inactive_flag = ' ';
-
-	if (!(vg_ptr->vg_status & VG_ACTIVE)) inactive_flag = 'I';
-	sz = sprintf(buf,
-		     "\nVG: %c%s  [%d PV, %d LV/%d open] "
-		     " PE Size: %d KB\n"
-		     "  Usage [KB/PE]: %d /%d total  "
-		     "%d /%d used  %d /%d free",
-		     inactive_flag,
-		     vg_ptr->vg_name,
-		     vg_ptr->pv_cur,
-		     vg_ptr->lv_cur,
-		     vg_ptr->lv_open,
-	     	     vg_ptr->pe_size >> 1,
-		     vg_ptr->pe_size * vg_ptr->pe_total >> 1,
-		     vg_ptr->pe_total,
-		     vg_ptr->pe_allocated * vg_ptr->pe_size >> 1,
-	     	     vg_ptr->pe_allocated,
-		     (vg_ptr->pe_total - vg_ptr->pe_allocated) *
-	     	     vg_ptr->pe_size >> 1,
-		     vg_ptr->pe_total - vg_ptr->pe_allocated);
-	return sz;
-}
-
-
-/*
- * provide LV info for proc filesystem use (global)
- */
-int lvm_lv_info(vg_t *vg_ptr, lv_t *lv_ptr, char *buf) {
-	int sz = 0;
-	char inactive_flag = 'A', allocation_flag = ' ',
-	     stripes_flag = ' ', rw_flag = ' ';
-
-	if (!(lv_ptr->lv_status & LV_ACTIVE))
-		inactive_flag = 'I';
-	rw_flag = 'R';
-	if (lv_ptr->lv_access & LV_WRITE)
-		rw_flag = 'W';
-	allocation_flag = 'D';
-	if (lv_ptr->lv_allocation & LV_CONTIGUOUS)
-		allocation_flag = 'C';
-	stripes_flag = 'L';
-	if (lv_ptr->lv_stripes > 1)
-		stripes_flag = 'S';
-	sz += sprintf(buf+sz,
-		      "[%c%c%c%c",
-		      inactive_flag,
-	 rw_flag,
-		      allocation_flag,
-		      stripes_flag);
-	if (lv_ptr->lv_stripes > 1)
-		sz += sprintf(buf+sz, "%-2d",
-			      lv_ptr->lv_stripes);
-	else
-		sz += sprintf(buf+sz, "  ");
-	basename = strrchr(lv_ptr->lv_name, '/');
-	if ( basename == 0) basename = lv_ptr->lv_name;
-	else                basename++;
-	sz += sprintf(buf+sz, "] %-25s", basename);
-	if (strlen(basename) > 25)
-		sz += sprintf(buf+sz,
-			      "\n                              ");
-	sz += sprintf(buf+sz, "%9d /%-6d   ",
-		      lv_ptr->lv_size >> 1,
-		      lv_ptr->lv_size / vg_ptr->pe_size);
-
-	if (lv_ptr->lv_open == 0)
-		sz += sprintf(buf+sz, "close");
-	else
-		sz += sprintf(buf+sz, "%dx open",
-			      lv_ptr->lv_open);
-
-	return sz;
-}
-
-
-/*
- * provide PV info for proc filesystem use (global)
- */
-int lvm_pv_info(pv_t *pv_ptr, char *buf) {
-	int sz = 0;
-	char inactive_flag = 'A', allocation_flag = ' ';
-	char *pv_name = NULL;
-
-	if (!(pv_ptr->pv_status & PV_ACTIVE))
-		inactive_flag = 'I';
-	allocation_flag = 'A';
-	if (!(pv_ptr->pv_allocatable & PV_ALLOCATABLE))
-		allocation_flag = 'N';
-	pv_name = strrchr(pv_ptr->pv_name+1,'/');
-	if ( pv_name == 0) pv_name = pv_ptr->pv_name;
-	else               pv_name++;
-	sz = sprintf(buf,
-		     "[%c%c] %-21s %8d /%-6d  "
-		     "%8d /%-6d  %8d /%-6d",
-		     inactive_flag,
-		     allocation_flag,
-		     pv_name,
-		     pv_ptr->pe_total *
-		     pv_ptr->pe_size >> 1,
-		     pv_ptr->pe_total,
-		     pv_ptr->pe_allocated *
-		     pv_ptr->pe_size >> 1,
-		     pv_ptr->pe_allocated,
-		     (pv_ptr->pe_total -
-		      pv_ptr->pe_allocated) *
-		     pv_ptr->pe_size >> 1,
-		     pv_ptr->pe_total -
-		     pv_ptr->pe_allocated);
-	return sz;
-}
-
-
-/*
- * Support functions /proc-Filesystem
- */
-
-#define  LVM_PROC_BUF   ( i == 0 ? dummy_buf : &buf[sz])
-
-/*
- * provide global LVM information
- */
-static int lvm_proc_get_global_info(char *page, char **start, off_t pos, int count, int *eof, void *data)
-{
-	int c, i, l, p, v, vg_counter, pv_counter, lv_counter, lv_open_counter,
-	 lv_open_total, pe_t_bytes, hash_table_bytes, lv_block_exception_t_bytes, seconds;
-	static off_t sz;
-	off_t sz_last;
-	static char *buf = NULL;
-	static char dummy_buf[160];	/* sized for 2 lines */
-	vg_t *vg_ptr;
-	lv_t *lv_ptr;
-	pv_t *pv_ptr;
-
-
-#ifdef DEBUG_LVM_PROC_GET_INFO
-	printk(KERN_DEBUG
-	       "%s - lvm_proc_get_global_info CALLED  pos: %lu  count: %d  whence: %d\n",
-	       lvm_name, pos, count, whence);
-#endif
-
-	MOD_INC_USE_COUNT;
-
-	if (pos == 0 || buf == NULL) {
-		sz_last = vg_counter = pv_counter = lv_counter = lv_open_counter = \
-		lv_open_total = pe_t_bytes = hash_table_bytes = \
-		lv_block_exception_t_bytes = 0;
-
-		/* search for activity */
-		for (v = 0; v < ABS_MAX_VG; v++) {
-			if ((vg_ptr = vg[v]) != NULL) {
-				vg_counter++;
-				pv_counter += vg_ptr->pv_cur;
-				lv_counter += vg_ptr->lv_cur;
-				if (vg_ptr->lv_cur > 0) {
-					for (l = 0; l < vg[v]->lv_max; l++) {
-						if ((lv_ptr = vg_ptr->lv[l]) != NULL) {
-							pe_t_bytes += lv_ptr->lv_allocated_le;
-							hash_table_bytes += lv_ptr->lv_snapshot_hash_table_size;
-							if (lv_ptr->lv_block_exception != NULL)
-								lv_block_exception_t_bytes += lv_ptr->lv_remap_end;
-							if (lv_ptr->lv_open > 0) {
-								lv_open_counter++;
-								lv_open_total += lv_ptr->lv_open;
-							}
-						}
-					}
-				}
-			}
-		}
-		pe_t_bytes *= sizeof(pe_t);
-		lv_block_exception_t_bytes *= sizeof(lv_block_exception_t);
-
-		if (buf != NULL) {
-			P_KFREE("%s -- vfree %d\n", lvm_name, __LINE__);
-			lock_kernel();
-			vfree(buf);
-			unlock_kernel();
-			buf = NULL;
-		}
-		/* 2 times: first to get size to allocate buffer,
-		   2nd to fill the malloced buffer */
-		for (i = 0; i < 2; i++) {
-			sz = 0;
-			sz += sprintf(LVM_PROC_BUF,
-				      "LVM "
-#ifdef MODULE
-				      "module"
-#else
-				      "driver"
-#endif
-				      " %s\n\n"
-				    "Total:  %d VG%s  %d PV%s  %d LV%s ",
-				      lvm_short_version,
-				  vg_counter, vg_counter == 1 ? "" : "s",
-				  pv_counter, pv_counter == 1 ? "" : "s",
-				 lv_counter, lv_counter == 1 ? "" : "s");
-			sz += sprintf(LVM_PROC_BUF,
-				      "(%d LV%s open",
-				      lv_open_counter,
-				      lv_open_counter == 1 ? "" : "s");
-			if (lv_open_total > 0)
-				sz += sprintf(LVM_PROC_BUF,
-					      " %d times)\n",
-					      lv_open_total);
-			else
-				sz += sprintf(LVM_PROC_BUF, ")");
-			sz += sprintf(LVM_PROC_BUF,
-				      "\nGlobal: %lu bytes malloced   IOP version: %d   ",
-				      vg_counter * sizeof(vg_t) +
-				      pv_counter * sizeof(pv_t) +
-				      lv_counter * sizeof(lv_t) +
-				      pe_t_bytes + hash_table_bytes + lv_block_exception_t_bytes + sz_last,
-				      lvm_iop_version);
-
-			seconds = CURRENT_TIME - loadtime;
-			if (seconds < 0)
-				loadtime = CURRENT_TIME + seconds;
-			if (seconds / 86400 > 0) {
-				sz += sprintf(LVM_PROC_BUF, "%d day%s ",
-					      seconds / 86400,
-					      seconds / 86400 == 0 ||
-					 seconds / 86400 > 1 ? "s" : "");
-			}
-			sz += sprintf(LVM_PROC_BUF, "%d:%02d:%02d active\n",
-				      (seconds % 86400) / 3600,
-				      (seconds % 3600) / 60,
-				      seconds % 60);
-
-			if (vg_counter > 0) {
-				for (v = 0; v < ABS_MAX_VG; v++) {
-					/* volume group */
-					if ((vg_ptr = vg[v]) != NULL) {
-						sz += lvm_vg_info(vg_ptr, LVM_PROC_BUF);
-
-						/* physical volumes */
-						sz += sprintf(LVM_PROC_BUF,
-							      "\n  PV%s ",
-							      vg_ptr->pv_cur == 1 ? ": " : "s:");
-						c = 0;
-						for (p = 0; p < vg_ptr->pv_max; p++) {
-							if ((pv_ptr = vg_ptr->pv[p]) != NULL) {
-								sz += lvm_pv_info(pv_ptr, LVM_PROC_BUF);
-
-								c++;
-								if (c < vg_ptr->pv_cur)
-									sz += sprintf(LVM_PROC_BUF,
-										      "\n       ");
-							}
-						}
-
-						/* logical volumes */
-						sz += sprintf(LVM_PROC_BUF,
-							   "\n    LV%s ",
-							      vg_ptr->lv_cur == 1 ? ": " : "s:");
-						c = 0;
-						for (l = 0; l < vg_ptr->lv_max; l++) {
-							if ((lv_ptr = vg_ptr->lv[l]) != NULL) {
-								sz += lvm_lv_info(vg_ptr, lv_ptr, LVM_PROC_BUF);
-								c++;
-								if (c < vg_ptr->lv_cur)
-									sz += sprintf(LVM_PROC_BUF,
-										      "\n         ");
-							}
-						}
-						if (vg_ptr->lv_cur == 0) sz += sprintf(LVM_PROC_BUF, "none");
-						sz += sprintf(LVM_PROC_BUF, "\n");
-					}
-				}
-			}
-			if (buf == NULL) {
-				lock_kernel();
-				buf = vmalloc(sz);
-				unlock_kernel();
-				if (buf == NULL) {
-					sz = 0;
-					MOD_DEC_USE_COUNT;
-					return sprintf(page, "%s - vmalloc error at line %d\n",
-						     lvm_name, __LINE__);
-				}
-			}
-			sz_last = sz;
-		}
-	}
-	MOD_DEC_USE_COUNT;
-	if (pos > sz - 1) {
-		lock_kernel();
-		vfree(buf);
-		unlock_kernel();
-		buf = NULL;
-		return 0;
-	}
-	*start = &buf[pos];
-	if (sz - pos < count)
-		return sz - pos;
-	else
-		return count;
-} /* lvm_proc_get_global_info() */
-
-
-/*
- * provide VG information
- */
-int lvm_proc_read_vg_info(char *page, char **start, off_t off,
-			  int count, int *eof, void *data) {
-	int sz = 0;
-	vg_t *vg = data;
-
-	sz += sprintf ( page+sz, "name:         %s\n", vg->vg_name);
-	sz += sprintf ( page+sz, "size:         %u\n",
-		        vg->pe_total * vg->pe_size / 2);
-	sz += sprintf ( page+sz, "access:       %u\n", vg->vg_access);
-	sz += sprintf ( page+sz, "status:       %u\n", vg->vg_status);
-	sz += sprintf ( page+sz, "number:       %u\n", vg->vg_number);
-	sz += sprintf ( page+sz, "LV max:       %u\n", vg->lv_max);
-	sz += sprintf ( page+sz, "LV current:   %u\n", vg->lv_cur);
-	sz += sprintf ( page+sz, "LV open:      %u\n", vg->lv_open);
-	sz += sprintf ( page+sz, "PV max:       %u\n", vg->pv_max);
-	sz += sprintf ( page+sz, "PV current:   %u\n", vg->pv_cur);
-	sz += sprintf ( page+sz, "PV active:    %u\n", vg->pv_act);
-	sz += sprintf ( page+sz, "PE size:      %u\n", vg->pe_size / 2);
-	sz += sprintf ( page+sz, "PE total:     %u\n", vg->pe_total);
-	sz += sprintf ( page+sz, "PE allocated: %u\n", vg->pe_allocated);
-	sz += sprintf ( page+sz, "uuid:         %s\n", lvm_show_uuid(vg->vg_uuid));
-
-	return sz;
-}
-
-
-/*
- * provide LV information
- */
-int lvm_proc_read_lv_info(char *page, char **start, off_t off,
-			  int count, int *eof, void *data) {
-	int sz = 0;
-	lv_t *lv = data;
-
-	sz += sprintf ( page+sz, "name:         %s\n", lv->lv_name);
-	sz += sprintf ( page+sz, "size:         %u\n", lv->lv_size);
-	sz += sprintf ( page+sz, "access:       %u\n", lv->lv_access);
-	sz += sprintf ( page+sz, "status:       %u\n", lv->lv_status);
-	sz += sprintf ( page+sz, "number:       %u\n", lv->lv_number);
-	sz += sprintf ( page+sz, "open:         %u\n", lv->lv_open);
-	sz += sprintf ( page+sz, "allocation:   %u\n", lv->lv_allocation);
-	sz += sprintf ( page+sz, "device:       %02u:%02u\n",
-                        MAJOR(lv->lv_dev), MINOR(lv->lv_dev));
-
-	return sz;
-}
-
-
-/*
- * provide PV information
- */
-int lvm_proc_read_pv_info(char *page, char **start, off_t off,
-			  int count, int *eof, void *data) {
-	int sz = 0;
-	pv_t *pv = data;
-
-	sz += sprintf ( page+sz, "name:         %s\n", pv->pv_name);
-	sz += sprintf ( page+sz, "size:         %u\n", pv->pv_size);
-	sz += sprintf ( page+sz, "status:       %u\n", pv->pv_status);
-	sz += sprintf ( page+sz, "number:       %u\n", pv->pv_number);
-	sz += sprintf ( page+sz, "allocatable:  %u\n", pv->pv_allocatable);
-	sz += sprintf ( page+sz, "LV current:   %u\n", pv->lv_cur);
-	sz += sprintf ( page+sz, "PE size:      %u\n", pv->pe_size / 2);
-	sz += sprintf ( page+sz, "PE total:     %u\n", pv->pe_total);
-	sz += sprintf ( page+sz, "PE allocated: %u\n", pv->pe_allocated);
-	sz += sprintf ( page+sz, "device:       %02u:%02u\n",
-                        MAJOR(pv->pv_dev), MINOR(pv->pv_dev));
-	sz += sprintf ( page+sz, "uuid:         %s\n", lvm_show_uuid(pv->pv_uuid));
-
-
-	return sz;
+	return (put_user(kdev_t_to_nr(bh.b_rdev), &user_result->lv_dev) ||
+		put_user(bh.b_rsector/(bh.b_size>>9), &user_result->lv_block));
 }
 
 
@@ -1478,172 +1096,151 @@
  * block device support function for /usr/src/linux/drivers/block/ll_rw_blk.c
  * (see init_module/lvm_init)
  */
+static inline void __remap_snapshot(kdev_t rdev, ulong rsector,
+				    ulong pe_start, lv_t *lv, vg_t *vg) {
+	if (!lvm_snapshot_remap_block(&rdev, &rsector, pe_start, lv) &&
+	    !lvm_snapshot_COW(rdev, rsector, pe_start, rsector, vg, lv))
+		lvm_write_COW_table_block(vg, lv);
+}
+
 static int lvm_map(struct buffer_head *bh, int rw)
 {
 	int minor = MINOR(bh->b_dev);
-	int ret = 0;
 	ulong index;
 	ulong pe_start;
 	ulong size = bh->b_size >> 9;
-	ulong rsector_tmp = bh->b_blocknr * size;
-	ulong rsector_sav;
-	kdev_t rdev_tmp = bh->b_dev;
-	kdev_t rdev_sav;
+	ulong rsector_org = bh->b_blocknr * size;
+	ulong rsector_map;
+	kdev_t rdev_map;
 	vg_t *vg_this = vg[VG_BLK(minor)];
 	lv_t *lv = vg_this->lv[LV_BLK(minor)];
 
 
+	down(&lv->lv_snapshot_sem);
 	if (!(lv->lv_status & LV_ACTIVE)) {
 		printk(KERN_ALERT
 		       "%s - lvm_map: ll_rw_blk for inactive LV %s\n",
 		       lvm_name, lv->lv_name);
-		return -1;
+		goto bad;
 	}
 
 	if ((rw == WRITE || rw == WRITEA) &&
 	    !(lv->lv_access & LV_WRITE)) {
 		printk(KERN_CRIT
-		    "%s - lvm_map: ll_rw_blk write for readonly LV %s\n",
+		       "%s - lvm_map: ll_rw_blk write for readonly LV %s\n",
 		       lvm_name, lv->lv_name);
-		return -1;
+		goto bad;
 	}
 
-	P_MAP("%s - lvm_map minor:%d  *rdev: %02d:%02d  *rsector: %lu  "
-	      "size:%lu\n",
+	P_MAP("%s - lvm_map minor: %d  *rdev: %s  *rsector: %lu  size:%lu\n",
 	      lvm_name, minor,
-	      MAJOR(rdev_tmp),
-	      MINOR(rdev_tmp),
-	      rsector_tmp, size);
+	      kdevname(bh->b_dev),
+	      rsector_org, size);
 
-	if (rsector_tmp + size > lv->lv_size) {
+	if (rsector_org + size > lv->lv_size) {
 		printk(KERN_ALERT
 		       "%s - lvm_map access beyond end of device; *rsector: "
                        "%lu or size: %lu wrong for minor: %2d\n",
-                       lvm_name, rsector_tmp, size, minor);
-		return -1;
+                       lvm_name, rsector_org, size, minor);
+		goto bad;
 	}
-	rsector_sav = rsector_tmp;
-	rdev_sav = rdev_tmp;
 
-lvm_second_remap:
-	/* linear mapping */
-	if (lv->lv_stripes < 2) {
+remap:
+	if (lv->lv_stripes < 2) { /* linear mapping */
 		/* get the index */
-		index = rsector_tmp / vg_this->pe_size;
+		index = rsector_org / vg_this->pe_size;
 		pe_start = lv->lv_current_pe[index].pe;
-		rsector_tmp = lv->lv_current_pe[index].pe +
-		    (rsector_tmp % vg_this->pe_size);
-		rdev_tmp = lv->lv_current_pe[index].dev;
-
-		P_MAP("lv_current_pe[%ld].pe: %ld  rdev: %02d:%02d  "
-		      "rsector:%ld\n",
-		       index,
-		       lv->lv_current_pe[index].pe,
-		       MAJOR(rdev_tmp),
-		       MINOR(rdev_tmp),
-		       rsector_tmp);
+		rsector_map = lv->lv_current_pe[index].pe +
+			(rsector_org % vg_this->pe_size);
+		rdev_map = lv->lv_current_pe[index].dev;
+
+		P_MAP("lv_current_pe[%ld].pe: %d  rdev: %s  rsector:%ld\n",
+		      index, lv->lv_current_pe[index].pe,
+		      kdevname(rdev_map), rsector_map);
 
-		/* striped mapping */
-	} else {
+	} else {		/* striped mapping */
 		ulong stripe_index;
 		ulong stripe_length;
 
 		stripe_length = vg_this->pe_size * lv->lv_stripes;
-		stripe_index = (rsector_tmp % stripe_length) / lv->lv_stripesize;
-		index = rsector_tmp / stripe_length +
-		    (stripe_index % lv->lv_stripes) *
-		    (lv->lv_allocated_le / lv->lv_stripes);
+		stripe_index = (rsector_org % stripe_length) /
+			lv->lv_stripesize;
+		index = rsector_org / stripe_length +
+			(stripe_index % lv->lv_stripes) *
+			(lv->lv_allocated_le / lv->lv_stripes);
 		pe_start = lv->lv_current_pe[index].pe;
-		rsector_tmp = lv->lv_current_pe[index].pe +
-		    (rsector_tmp % stripe_length) -
-		    (stripe_index % lv->lv_stripes) * lv->lv_stripesize -
-		    stripe_index / lv->lv_stripes *
-		    (lv->lv_stripes - 1) * lv->lv_stripesize;
-		rdev_tmp = lv->lv_current_pe[index].dev;
-	}
-
-	P_MAP("lv_current_pe[%ld].pe: %ld  rdev: %02d:%02d  rsector:%ld\n"
-	       "stripe_length: %ld  stripe_index: %ld\n",
-	       index,
-	       lv->lv_current_pe[index].pe,
-	       MAJOR(rdev_tmp),
-	       MINOR(rdev_tmp),
-	       rsector_tmp,
-	       stripe_length,
-	       stripe_index);
+		rsector_map = lv->lv_current_pe[index].pe +
+			(rsector_org % stripe_length) -
+			(stripe_index % lv->lv_stripes) * lv->lv_stripesize -
+			stripe_index / lv->lv_stripes *
+			(lv->lv_stripes - 1) * lv->lv_stripesize;
+		rdev_map = lv->lv_current_pe[index].dev;
+
+		P_MAP("lv_current_pe[%ld].pe: %d  rdev: %s  rsector:%ld\n"
+		      "stripe_length: %ld  stripe_index: %ld\n",
+		      index, lv->lv_current_pe[index].pe, kdevname(rdev_map),
+		      rsector_map, stripe_length, stripe_index);
+	}
 
 	/* handle physical extents on the move */
 	if (pe_lock_req.lock == LOCK_PE) {
-		if (rdev_tmp == pe_lock_req.data.pv_dev &&
-		    rsector_tmp >= pe_lock_req.data.pv_offset &&
-		    rsector_tmp < (pe_lock_req.data.pv_offset +
+		if (rdev_map == pe_lock_req.data.pv_dev &&
+		    rsector_map >= pe_lock_req.data.pv_offset &&
+		    rsector_map < (pe_lock_req.data.pv_offset +
 				   vg_this->pe_size)) {
 			sleep_on(&lvm_map_wait);
-			rsector_tmp = rsector_sav;
-			rdev_tmp = rdev_sav;
-			goto lvm_second_remap;
+			goto remap;
 		}
 	}
+
 	/* statistic */
 	if (rw == WRITE || rw == WRITEA)
 		lv->lv_current_pe[index].writes++;
 	else
 		lv->lv_current_pe[index].reads++;
 
-	/* snapshot volume exception handling on physical device address base */
-	if (lv->lv_access & (LV_SNAPSHOT|LV_SNAPSHOT_ORG)) {
-		/* original logical volume */
-		if (lv->lv_access & LV_SNAPSHOT_ORG) {
-			/* Serializes the access to the lv_snapshot_next list */
-			down(&lv->lv_snapshot_sem);
-			if (rw == WRITE || rw == WRITEA)
-			{
-				lv_t *lv_ptr;
-
-				/* start with first snapshot and loop thrugh all of them */
-				for (lv_ptr = lv->lv_snapshot_next;
-				     lv_ptr != NULL;
-				     lv_ptr = lv_ptr->lv_snapshot_next) {
-					/* Check for inactive snapshot */
-					if (!(lv_ptr->lv_status & LV_ACTIVE)) continue;
-					/* Serializes the COW with the accesses to the snapshot device */
-					down(&lv_ptr->lv_snapshot_sem);
-					/* do we still have exception storage for this snapshot free? */
-					if (lv_ptr->lv_block_exception != NULL) {
-						rdev_sav = rdev_tmp;
-						rsector_sav = rsector_tmp;
-						if (!lvm_snapshot_remap_block(&rdev_tmp,
-									      &rsector_tmp,
-									      pe_start,
-									      lv_ptr)) {
-							/* create a new mapping */
-							if (!(ret = lvm_snapshot_COW(rdev_tmp,
-									       	     rsector_tmp,
-									             pe_start,
-									             rsector_sav,
-									             lv_ptr)))
-								ret = lvm_write_COW_table_block(vg_this,
-												lv_ptr);
-						}
-						rdev_tmp = rdev_sav;
-						rsector_tmp = rsector_sav;
-					}
-					up(&lv_ptr->lv_snapshot_sem);
-				}
-			}
-			up(&lv->lv_snapshot_sem);
-		} else {
-			/* remap snapshot logical volume */
-			down(&lv->lv_snapshot_sem);
-			if (lv->lv_block_exception != NULL)
-				lvm_snapshot_remap_block(&rdev_tmp, &rsector_tmp, pe_start, lv);
-			up(&lv->lv_snapshot_sem);
+	/* snapshot volume exception handling on physical device
+           address base */
+	if (!(lv->lv_access & (LV_SNAPSHOT|LV_SNAPSHOT_ORG)))
+		goto out;
+
+	if (lv->lv_access & LV_SNAPSHOT) { /* remap snapshot */
+		if (lv->lv_block_exception)
+			lvm_snapshot_remap_block(&rdev_map, &rsector_map,
+						 pe_start, lv);
+		else
+			goto bad;
+
+	} else if(rw == WRITE || rw == WRITEA) { /* snapshot origin */
+		lv_t *snap;
+
+		/* start with first snapshot and loop through all of
+		   them */
+		for (snap = lv->lv_snapshot_next; snap;
+		     snap = snap->lv_snapshot_next) {
+			/* Check for inactive snapshot */
+			if (!(snap->lv_status & LV_ACTIVE))
+				continue;
+
+			/* Serializes the COW with the accesses to the
+			   snapshot device */
+			down(&snap->lv_snapshot_sem);
+			__remap_snapshot(rdev_map, rsector_map,
+					 pe_start, snap, vg_this);
+			up(&snap->lv_snapshot_sem);
 		}
 	}
-	bh->b_rdev = rdev_tmp;
-	bh->b_rsector = rsector_tmp;
 
-	return ret;
+ out:
+	bh->b_rdev = rdev_map;
+	bh->b_rsector = rsector_map;
+	up(&lv->lv_snapshot_sem);
+	return 1;
+
+ bad:
+	buffer_IO_error(bh);
+	up(&lv->lv_snapshot_sem);
+	return -1;
 } /* lvm_map() */
 
 
@@ -1671,6 +1268,8 @@
 #endif
 
 
+
+
 /*
  * make request function
  */
@@ -1780,6 +1379,8 @@
 					    le_remap_req.new_dev;
 					lv_ptr->lv_current_pe[le].pe =
 					    le_remap_req.new_pe;
+
+					__update_hardblocksize(lv_ptr);
 					return 0;
 				}
 			}
@@ -1793,7 +1394,7 @@
 /*
  * character device support function VGDA create
  */
-int lvm_do_vg_create(int minor, void *arg)
+static int lvm_do_vg_create(void *arg, int minor)
 {
 	int ret = 0;
 	ulong l, ls = 0, p, size;
@@ -1801,8 +1402,6 @@
 	vg_t *vg_ptr;
 	lv_t **snap_lv_ptr;
 
-	if (vg[VG_CHR(minor)] != NULL) return -EPERM;
-
 	if ((vg_ptr = kmalloc(sizeof(vg_t),GFP_KERNEL)) == NULL) {
 		printk(KERN_CRIT
 		       "%s -- VG_CREATE: kmalloc error VG at line %d\n",
@@ -1815,28 +1414,40 @@
 		return -EFAULT;
 	}
 
+	/* VG_CREATE now uses minor number in VG structure */
+	if (minor == -1) minor = vg_ptr->vg_number;
+
+	/* Validate it */
+	if (vg[VG_CHR(minor)] != NULL) {
+	    kfree(vg_ptr);
+	    return -EPERM;
+	}
+
 	/* we are not that active so far... */
 	vg_ptr->vg_status &= ~VG_ACTIVE;
-	vg[VG_CHR(minor)] = vg_ptr;
-	vg[VG_CHR(minor)]->pe_allocated = 0;
+	vg_ptr->pe_allocated = 0;
 
 	if (vg_ptr->pv_max > ABS_MAX_PV) {
 		printk(KERN_WARNING
 		       "%s -- Can't activate VG: ABS_MAX_PV too small\n",
 		       lvm_name);
 		kfree(vg_ptr);
-		vg[VG_CHR(minor)] = NULL;
 		return -EPERM;
 	}
+
 	if (vg_ptr->lv_max > ABS_MAX_LV) {
 		printk(KERN_WARNING
 		"%s -- Can't activate VG: ABS_MAX_LV too small for %u\n",
 		       lvm_name, vg_ptr->lv_max);
 		kfree(vg_ptr);
-		vg_ptr = NULL;
 		return -EPERM;
 	}
 
+	/* create devfs and procfs entries */
+	lvm_fs_create_vg(vg_ptr);
+
+	vg[VG_CHR(minor)] = vg_ptr;
+
 	/* get the physical volume structures */
 	vg_ptr->pv_act = vg_ptr->pv_cur = 0;
 	for (p = 0; p < vg_ptr->pv_max; p++) {
@@ -1884,8 +1495,6 @@
 		}
 	}
 
-	lvm_do_create_devfs_entry_of_vg ( vg_ptr);
-
 	/* Second path to correct snapshot logical volumes which are not
 	   in place during first path above */
 	for (l = 0; l < ls; l++) {
@@ -1900,8 +1509,6 @@
 		}
 	}
 
-	lvm_do_create_proc_entry_of_vg ( vg_ptr);
-
 	vfree(snap_lv_ptr);
 
 	vg_count++;
@@ -1933,7 +1540,6 @@
 				if ( ret != 0) return ret;
 				pv_ptr = vg_ptr->pv[p];
 				vg_ptr->pe_total += pv_ptr->pe_total;
-				lvm_do_create_proc_entry_of_pv(vg_ptr, pv_ptr);
 				return 0;
 			}
 		}
@@ -1983,10 +1589,13 @@
 	lv_t *lv_ptr = NULL;
 	pv_t *pv_ptr = NULL;
 
+	/* If the VG doesn't exist in the kernel then just exit */
+	if (!vg_ptr) return 0;
+
 	if (copy_from_user(vg_name, arg, sizeof(vg_name)) != 0)
 		return -EFAULT;
 
-	lvm_do_remove_proc_entry_of_vg ( vg_ptr);
+	lvm_fs_remove_vg(vg_ptr);
 
 	strncpy ( vg_ptr->vg_name, vg_name, sizeof ( vg_name)-1);
 	for ( l = 0; l < vg_ptr->lv_max; l++)
@@ -2008,7 +1617,7 @@
 		strncpy(pv_ptr->vg_name, vg_name, NAME_LEN);
 	}
 
-	lvm_do_create_proc_entry_of_vg ( vg_ptr);
+	lvm_fs_create_vg(vg_ptr);
 
 	return 0;
 } /* lvm_do_vg_rename */
@@ -2035,6 +1644,9 @@
 	/* let's go inactive */
 	vg_ptr->vg_status &= ~VG_ACTIVE;
 
+	/* remove from procfs and devfs */
+	lvm_fs_remove_vg(vg_ptr);
+
 	/* free LVs */
 	/* first free snapshot logical volumes */
 	for (i = 0; i < vg_ptr->lv_max; i++) {
@@ -2062,11 +1674,6 @@
 		}
 	}
 
-	devfs_unregister (ch_devfs_handle[vg_ptr->vg_number]);
-	devfs_unregister (vg_devfs_handle[vg_ptr->vg_number]);
-
-	lvm_do_remove_proc_entry_of_vg ( vg_ptr);
-
 	P_KFREE("%s -- kfree %d\n", lvm_name, __LINE__);
 	kfree(vg_ptr);
 	vg[VG_CHR(minor)] = NULL;
@@ -2088,7 +1695,7 @@
 	pv_ptr = vg_ptr->pv[p] = kmalloc(sizeof(pv_t),GFP_KERNEL);
 	if (pv_ptr == NULL) {
 		printk(KERN_CRIT
-		       "%s -- VG_CREATE: kmalloc error PV at line %d\n",
+		       "%s -- PV_CREATE: kmalloc error PV at line %d\n",
 		       lvm_name, __LINE__);
 		return -ENOMEM;
 	}
@@ -2102,18 +1709,20 @@
 	pv_ptr->pv_status = PV_ACTIVE;
 	vg_ptr->pv_act++;
 	vg_ptr->pv_cur++;
+	lvm_fs_create_pv(vg_ptr, pv_ptr);
 
 	return 0;
 } /* lvm_do_pv_create() */
 
 
 /*
- * character device support function physical volume create
+ * character device support function physical volume remove
  */
 static int lvm_do_pv_remove(vg_t *vg_ptr, ulong p) {
 	pv_t *pv_ptr = vg_ptr->pv[p];
 
-	lvm_do_remove_proc_entry_of_pv ( vg_ptr, pv_ptr);
+	lvm_fs_remove_pv(vg_ptr, pv_ptr);
+
 	vg_ptr->pe_total -= pv_ptr->pe_total;
 	vg_ptr->pv_cur--;
 	vg_ptr->pv_act--;
@@ -2127,12 +1736,39 @@
 }
 
 
+static void __update_hardblocksize(lv_t *lv) {
+	int le, e;
+	int max_hardblocksize = 0, hardblocksize;
+
+	for (le = 0; le < lv->lv_allocated_le; le++) {
+		hardblocksize = get_hardblocksize(lv->lv_current_pe[le].dev);
+		if (hardblocksize == 0)
+			hardblocksize = 512;
+		if (hardblocksize > max_hardblocksize)
+			max_hardblocksize = hardblocksize;
+	}
+
+	if (lv->lv_access & LV_SNAPSHOT) {
+		for (e = 0; e < lv->lv_remap_end; e++) {
+			hardblocksize =
+				get_hardblocksize(
+					lv->lv_block_exception[e].rdev_new);
+			if (hardblocksize == 0)
+				hardblocksize = 512;
+			if (hardblocksize > max_hardblocksize)
+				max_hardblocksize = hardblocksize;
+		}
+	}
+
+	lvm_hardsectsizes[MINOR(lv->lv_dev)] = max_hardblocksize;
+}
+
 /*
  * character device support function logical volume create
  */
 static int lvm_do_lv_create(int minor, char *lv_name, lv_t *lv)
 {
-	int e, ret, l, le, l_new, p, size;
+	int e, ret, l, le, l_new, p, size, activate = 1;
 	ulong lv_status_save;
 	lv_block_exception_t *lvbe = lv->lv_block_exception;
 	vg_t *vg_ptr = vg[VG_CHR(minor)];
@@ -2142,7 +1778,7 @@
 	if (lv->lv_chunk_size > LVM_SNAPSHOT_MAX_CHUNK)
 		return -EINVAL;
 
-	for (l = 0; l < vg_ptr->lv_max; l++) {
+	for (l = 0; l < vg_ptr->lv_cur; l++) {
 		if (vg_ptr->lv[l] != NULL &&
 		    strcmp(vg_ptr->lv[l]->lv_name, lv_name) == 0)
 			return -EEXIST;
@@ -2176,16 +1812,17 @@
 	lv_ptr->lv_snapshot_next = NULL;
 	lv_ptr->lv_block_exception = NULL;
 	lv_ptr->lv_iobuf = NULL;
+	lv_ptr->lv_COW_table_iobuf = NULL;
 	lv_ptr->lv_snapshot_hash_table = NULL;
 	lv_ptr->lv_snapshot_hash_table_size = 0;
 	lv_ptr->lv_snapshot_hash_mask = 0;
-	lv_ptr->lv_COW_table_page = NULL;
 	init_MUTEX(&lv_ptr->lv_snapshot_sem);
 	lv_ptr->lv_snapshot_use_rate = 0;
+
 	vg_ptr->lv[l] = lv_ptr;
 
 	/* get the PE structures from user space if this
-	   is no snapshot logical volume */
+	   is not a snapshot logical volume */
 	if (!(lv_ptr->lv_access & LV_SNAPSHOT)) {
 		size = lv_ptr->lv_allocated_le * sizeof(pe_t);
 		if ((lv_ptr->lv_current_pe = vmalloc(size)) == NULL) {
@@ -2237,6 +1874,16 @@
 					vg_ptr->lv[l] = NULL;
 					return -EFAULT;
 				}
+
+				if(lv_ptr->lv_block_exception[0].rsector_org ==
+				   LVM_SNAPSHOT_DROPPED_SECTOR)
+				{
+					printk(KERN_WARNING
+   "%s -- lvm_do_lv_create: snapshot has been dropped and will not be activated\n",
+					       lvm_name);
+					activate = 0;
+				}
+
 				/* point to the original logical volume */
 				lv_ptr = lv_ptr->lv_snapshot_org;
 
@@ -2270,10 +1917,13 @@
 						       lv_ptr->lv_block_exception[e].rsector_org, lv_ptr);
 				/* need to fill the COW exception table data
 				   into the page for disk i/o */
-				lvm_snapshot_fill_COW_page(vg_ptr, lv_ptr);
+				if(lvm_snapshot_fill_COW_page(vg_ptr, lv_ptr)) {
+					kfree(lv_ptr);
+					vg_ptr->lv[l] = NULL;
+					return -EINVAL;
+				}
 				init_waitqueue_head(&lv_ptr->lv_snapshot_wait);
 			} else {
-				vfree(lv_ptr->lv_block_exception);
 				kfree(lv_ptr);
 				vg_ptr->lv[l] = NULL;
 				return -EFAULT;
@@ -2295,21 +1945,7 @@
 	vg_ptr->lv_cur++;
 	lv_ptr->lv_status = lv_status_save;
 
-	{
-	char *lv_tmp, *lv_buf = lv->lv_name;
-
-	strtok(lv->lv_name, "/");       /* /dev */
-	while((lv_tmp = strtok(NULL, "/")) != NULL)
-		lv_buf = lv_tmp;
-
-	lv_devfs_handle[lv->lv_number] = devfs_register(
-		vg_devfs_handle[vg_ptr->vg_number], lv_buf,
-		DEVFS_FL_DEFAULT, LVM_BLK_MAJOR, lv->lv_number,
-		S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP,
-		&lvm_blk_dops, NULL);
-	}
-
-	lvm_do_create_proc_entry_of_lv ( vg_ptr, lv_ptr);
+	__update_hardblocksize(lv_ptr);
 
 	/* optionally add our new snapshot LV */
 	if (lv_ptr->lv_access & LV_SNAPSHOT) {
@@ -2326,6 +1962,7 @@
 		org->lv_access |= LV_SNAPSHOT_ORG;
 		lv_ptr->lv_access &= ~LV_SNAPSHOT_ORG; /* this can only hide an userspace bug */
 
+
 		/* Link in the list of snapshot volumes */
 		for (last = org; last->lv_snapshot_next; last = last->lv_snapshot_next);
 		lv_ptr->lv_snapshot_prev = last;
@@ -2334,7 +1971,11 @@
 	}
 
 	/* activate the logical volume */
-	lv_ptr->lv_status |= LV_ACTIVE;
+	if(activate)
+		lv_ptr->lv_status |= LV_ACTIVE;
+	else
+		lv_ptr->lv_status &= ~LV_ACTIVE;
+
 	if ( lv_ptr->lv_access & LV_WRITE)
 		set_device_ro(lv_ptr->lv_dev, 0);
 	else
@@ -2349,6 +1990,7 @@
 
 	lv_ptr->vg = vg_ptr;
 
+	lvm_fs_create_lv(vg_ptr, lv_ptr);
 	return 0;
 } /* lvm_do_lv_create() */
 
@@ -2386,6 +2028,8 @@
 	    lv_ptr->lv_snapshot_next != NULL)
 		return -EPERM;
 
+	lvm_fs_remove_lv(vg_ptr, lv_ptr);
+
 	if (lv_ptr->lv_access & LV_SNAPSHOT) {
 		/*
 		 * Atomically make the the snapshot invisible
@@ -2400,11 +2044,13 @@
 			lv_ptr->lv_snapshot_next->lv_snapshot_prev =
 			    lv_ptr->lv_snapshot_prev;
 		}
-		up(&org->lv_snapshot_sem);
 
 		/* no more snapshots? */
-		if (!org->lv_snapshot_next)
+		if (!org->lv_snapshot_next) {
 			org->lv_access &= ~LV_SNAPSHOT_ORG;
+		}
+		up(&org->lv_snapshot_sem);
+
 		lvm_snapshot_release(lv_ptr);
 
 		/* Update the VG PE(s) used by snapshot reserve space. */
@@ -2447,10 +2093,6 @@
 		vfree(lv_ptr->lv_current_pe);
 	}
 
-	devfs_unregister(lv_devfs_handle[lv_ptr->lv_number]);
-
-	lvm_do_remove_proc_entry_of_lv ( vg_ptr, lv_ptr);
-
 	P_KFREE("%s -- kfree %d\n", lvm_name, __LINE__);
 	kfree(lv_ptr);
 	vg_ptr->lv[l] = NULL;
@@ -2460,204 +2102,212 @@
 
 
 /*
- * character device support function logical volume extend / reduce
+ * logical volume extend / reduce
  */
-static int lvm_do_lv_extend_reduce(int minor, char *lv_name, lv_t *lv)
-{
-	ulong end, l, le, p, size, old_allocated_le;
-	vg_t *vg_ptr = vg[VG_CHR(minor)];
-	lv_t *lv_ptr;
-	pe_t *pe;
+static int __extend_reduce_snapshot(vg_t *vg_ptr, lv_t *old_lv, lv_t *new_lv) {
+	ulong size;
+	lv_block_exception_t *lvbe;
 
-	if ((pep = lv->lv_current_pe) == NULL) return -EINVAL;
+	if (!new_lv->lv_block_exception)
+		return -ENXIO;
 
-	for (l = 0; l < vg_ptr->lv_max; l++) {
-		if (vg_ptr->lv[l] != NULL &&
-		    strcmp(vg_ptr->lv[l]->lv_name, lv_name) == 0)
-			break;
+	size = new_lv->lv_remap_end * sizeof(lv_block_exception_t);
+	if ((lvbe = vmalloc(size)) == NULL) {
+		printk(KERN_CRIT
+		       "%s -- lvm_do_lv_extend_reduce: vmalloc "
+		       "error LV_BLOCK_EXCEPTION of %lu Byte at line %d\n",
+		       lvm_name, size, __LINE__);
+		return -ENOMEM;
 	}
-	if (l == vg_ptr->lv_max) return -ENXIO;
-	lv_ptr = vg_ptr->lv[l];
 
-	/* check for active snapshot */
-	if (lv->lv_access & LV_SNAPSHOT)
-	{
-		ulong e;
-		lv_block_exception_t *lvbe, *lvbe_old;
-		struct list_head * lvs_hash_table_old;
-
-		if (lv->lv_block_exception == NULL) return -ENXIO;
-		size = lv->lv_remap_end * sizeof ( lv_block_exception_t);
-		if ((lvbe = vmalloc(size)) == NULL)
-		{
-			printk(KERN_CRIT
-			"%s -- lvm_do_lv_extend_reduce: vmalloc error LV_BLOCK_EXCEPTION "
-			       "of %lu Byte at line %d\n",
-			       lvm_name, size, __LINE__);
-			return -ENOMEM;
-		}
-		if (lv->lv_remap_end > lv_ptr->lv_remap_end)
-		{
-			if (copy_from_user(lvbe, lv->lv_block_exception, size))
-			{
-				vfree(lvbe);
-				return -EFAULT;
-			}
-		}
-
-		lvbe_old = lv_ptr->lv_block_exception;
-		lvs_hash_table_old = lv_ptr->lv_snapshot_hash_table;
-
-		/* we need to play on the safe side here... */
-		down(&lv_ptr->lv_snapshot_org->lv_snapshot_sem);
-		if (lv_ptr->lv_block_exception == NULL ||
-		    lv_ptr->lv_remap_ptr > lv_ptr->lv_remap_end)
-		{
-			up(&lv_ptr->lv_snapshot_org->lv_snapshot_sem);
-			vfree(lvbe);
-			return -EPERM;
-		}
-		memcpy(lvbe,
-		       lv_ptr->lv_block_exception,
-		       (lv->lv_remap_end > lv_ptr->lv_remap_end ?
-			lv_ptr->lv_remap_ptr : lv->lv_remap_end) * sizeof(lv_block_exception_t));
-
-		lv_ptr->lv_block_exception = lvbe;
-		lv_ptr->lv_remap_end = lv->lv_remap_end;
-		if (lvm_snapshot_alloc_hash_table(lv_ptr) != 0)
-		{
-			lvm_drop_snapshot(lv_ptr, "no memory for hash table");
-			up(&lv_ptr->lv_snapshot_org->lv_snapshot_sem);
-			vfree(lvbe_old);
-			vfree(lvs_hash_table_old);
-			return -ENOMEM;
-		}
-
-		for (e = 0; e < lv_ptr->lv_remap_ptr; e++)
-			lvm_hash_link (lv_ptr->lv_block_exception + e,
-				       lv_ptr->lv_block_exception[e].rdev_org,
-				       lv_ptr->lv_block_exception[e].rsector_org, lv_ptr);
-
-		up(&lv_ptr->lv_snapshot_org->lv_snapshot_sem);
-
-		vfree(lvbe_old);
-		vfree(lvs_hash_table_old);
+	if ((new_lv->lv_remap_end > old_lv->lv_remap_end) &&
+	    (copy_from_user(lvbe, new_lv->lv_block_exception, size))) {
+		vfree(lvbe);
+		return -EFAULT;
+	}
+	new_lv->lv_block_exception = lvbe;
 
-		return 0;
+	if (lvm_snapshot_alloc_hash_table(new_lv)) {
+		vfree(new_lv->lv_block_exception);
+		return -ENOMEM;
 	}
 
+	return 0;
+}
 
-	/* we drop in here in case it is an original logical volume */
-	if ((pe = vmalloc(size = lv->lv_current_le * sizeof(pe_t))) == NULL) {
+static int __extend_reduce(vg_t *vg_ptr, lv_t *old_lv, lv_t *new_lv) {
+	ulong size, l, p, end;
+	pe_t *pe;
+
+	/* allocate space for new pe structures */
+	size = new_lv->lv_current_le * sizeof(pe_t);
+	if ((pe = vmalloc(size)) == NULL) {
 		printk(KERN_CRIT
-		"%s -- lvm_do_lv_extend_reduce: vmalloc error LV_CURRENT_PE "
-		       "of %lu Byte at line %d\n",
+		       "%s -- lvm_do_lv_extend_reduce: "
+		       "vmalloc error LV_CURRENT_PE of %lu Byte at line %d\n",
 		       lvm_name, size, __LINE__);
 		return -ENOMEM;
 	}
+
 	/* get the PE structures from user space */
-	if (copy_from_user(pe, pep, size)) {
+	if (copy_from_user(pe, new_lv->lv_current_pe, size)) {
+		if(old_lv->lv_access & LV_SNAPSHOT)
+			vfree(new_lv->lv_snapshot_hash_table);
 		vfree(pe);
 		return -EFAULT;
 	}
 
+	new_lv->lv_current_pe = pe;
+
 	/* reduce allocation counters on PV(s) */
-	for (le = 0; le < lv_ptr->lv_allocated_le; le++) {
+	for (l = 0; l < old_lv->lv_allocated_le; l++) {
 		vg_ptr->pe_allocated--;
 		for (p = 0; p < vg_ptr->pv_cur; p++) {
 			if (vg_ptr->pv[p]->pv_dev ==
-			lv_ptr->lv_current_pe[le].dev) {
+			    old_lv->lv_current_pe[l].dev) {
 				vg_ptr->pv[p]->pe_allocated--;
 				break;
 			}
 		}
 	}
 
-
-	/* save pointer to "old" lv/pe pointer array */
-	pep1 = lv_ptr->lv_current_pe;
-	end = lv_ptr->lv_current_le;
-
-	/* save open counter... */
-	lv->lv_open = lv_ptr->lv_open;
-	lv->lv_snapshot_prev = lv_ptr->lv_snapshot_prev;
-	lv->lv_snapshot_next = lv_ptr->lv_snapshot_next;
-	lv->lv_snapshot_org  = lv_ptr->lv_snapshot_org;
-
-	lv->lv_current_pe = pe;
-
-	/* save # of old allocated logical extents */
-	old_allocated_le = lv_ptr->lv_allocated_le;
-
-	/* copy preloaded LV */
-	memcpy((char *) lv_ptr, (char *) lv, sizeof(lv_t));
-
-	lvm_gendisk.part[MINOR(lv_ptr->lv_dev)].start_sect = 0;
-	lvm_gendisk.part[MINOR(lv_ptr->lv_dev)].nr_sects = lv_ptr->lv_size;
-	lvm_size[MINOR(lv_ptr->lv_dev)] = lv_ptr->lv_size >> 1;
-	/* vg_lv_map array doesn't have to be changed here */
-
-	LVM_CORRECT_READ_AHEAD(lv_ptr->lv_read_ahead);
+	/* extend the PE count in PVs */
+	for (l = 0; l < new_lv->lv_allocated_le; l++) {
+		vg_ptr->pe_allocated++;
+		for (p = 0; p < vg_ptr->pv_cur; p++) {
+			if (vg_ptr->pv[p]->pv_dev ==
+                            new_lv->lv_current_pe[l].dev) {
+				vg_ptr->pv[p]->pe_allocated++;
+				break;
+			}
+		}
+	}
 
 	/* save availiable i/o statistic data */
-	/* linear logical volume */
-	if (lv_ptr->lv_stripes < 2) {
-		/* Check what last LE shall be used */
-		if (end > lv_ptr->lv_current_le) end = lv_ptr->lv_current_le;
-		for (le = 0; le < end; le++) {
-			lv_ptr->lv_current_pe[le].reads  += pep1[le].reads;
-			lv_ptr->lv_current_pe[le].writes += pep1[le].writes;
+	if (old_lv->lv_stripes < 2) {	/* linear logical volume */
+		end = min(old_lv->lv_current_le, new_lv->lv_current_le);
+		for (l = 0; l < end; l++) {
+			new_lv->lv_current_pe[l].reads +=
+				old_lv->lv_current_pe[l].reads;
+
+			new_lv->lv_current_pe[l].writes +=
+				old_lv->lv_current_pe[l].writes;
 		}
-		/* striped logical volume */
-	} else {
+
+	} else {		/* striped logical volume */
 		uint i, j, source, dest, end, old_stripe_size, new_stripe_size;
 
-		old_stripe_size = old_allocated_le / lv_ptr->lv_stripes;
-		new_stripe_size = lv_ptr->lv_allocated_le / lv_ptr->lv_stripes;
-		end = old_stripe_size;
-		if (end > new_stripe_size) end = new_stripe_size;
+		old_stripe_size = old_lv->lv_allocated_le / old_lv->lv_stripes;
+		new_stripe_size = new_lv->lv_allocated_le / new_lv->lv_stripes;
+		end = min(old_stripe_size, new_stripe_size);
+
 		for (i = source = dest = 0;
-		     i < lv_ptr->lv_stripes; i++) {
+		     i < new_lv->lv_stripes; i++) {
 			for (j = 0; j < end; j++) {
-				lv_ptr->lv_current_pe[dest + j].reads +=
-				    pep1[source + j].reads;
-				lv_ptr->lv_current_pe[dest + j].writes +=
-				    pep1[source + j].writes;
+				new_lv->lv_current_pe[dest + j].reads +=
+				    old_lv->lv_current_pe[source + j].reads;
+				new_lv->lv_current_pe[dest + j].writes +=
+				    old_lv->lv_current_pe[source + j].writes;
 			}
 			source += old_stripe_size;
 			dest += new_stripe_size;
 		}
 	}
 
-	/* extend the PE count in PVs */
-	for (le = 0; le < lv_ptr->lv_allocated_le; le++) {
-		vg_ptr->pe_allocated++;
-		for (p = 0; p < vg_ptr->pv_cur; p++) {
-			if (vg_ptr->pv[p]->pv_dev ==
-                            lv_ptr->lv_current_pe[le].dev) {
-				vg_ptr->pv[p]->pe_allocated++;
-				break;
-			}
-		}
-	}
+	return 0;
+}
+
+static int lvm_do_lv_extend_reduce(int minor, char *lv_name, lv_t *new_lv)
+{
+	int r;
+	ulong l, e, size;
+	vg_t *vg_ptr = vg[VG_CHR(minor)];
+	lv_t *old_lv;
+	pe_t *pe;
 
-	vfree ( pep1);
-	pep1 = NULL;
+	if ((pe = new_lv->lv_current_pe) == NULL)
+		return -EINVAL;
 
-	if (lv->lv_access & LV_SNAPSHOT_ORG)
-	{
-		/* Correct the snapshot size information */
-		while ((lv_ptr = lv_ptr->lv_snapshot_next) != NULL)
-		{
-			lv_ptr->lv_current_pe = lv_ptr->lv_snapshot_org->lv_current_pe;
-			lv_ptr->lv_allocated_le = lv_ptr->lv_snapshot_org->lv_allocated_le;
-			lv_ptr->lv_current_le = lv_ptr->lv_snapshot_org->lv_current_le;
-			lv_ptr->lv_size = lv_ptr->lv_snapshot_org->lv_size;
-			lvm_gendisk.part[MINOR(lv_ptr->lv_dev)].nr_sects = lv_ptr->lv_size;
-			lvm_size[MINOR(lv_ptr->lv_dev)] = lv_ptr->lv_size >> 1;
+	for (l = 0; l < vg_ptr->lv_max; l++)
+		if (vg_ptr->lv[l] && !strcmp(vg_ptr->lv[l]->lv_name, lv_name))
+			break;
+
+	if (l == vg_ptr->lv_max)
+		return -ENXIO;
+
+	old_lv = vg_ptr->lv[l];
+
+	if (old_lv->lv_access & LV_SNAPSHOT)
+		r = __extend_reduce_snapshot(vg_ptr, old_lv, new_lv);
+	else
+		r = __extend_reduce(vg_ptr, old_lv, new_lv);
+
+	if(r)
+		return r;
+
+	/* copy relevent fields */
+	down(&old_lv->lv_snapshot_sem);
+
+	if(new_lv->lv_access & LV_SNAPSHOT) {
+
+		size = (new_lv->lv_remap_end > old_lv->lv_remap_end) ?
+			old_lv->lv_remap_ptr : new_lv->lv_remap_end;
+		size *= sizeof(lv_block_exception_t);
+		memcpy(new_lv->lv_block_exception, 
+		       old_lv->lv_block_exception, size);
+
+		old_lv->lv_remap_end = new_lv->lv_remap_end;
+		old_lv->lv_block_exception = new_lv->lv_block_exception;
+		old_lv->lv_snapshot_hash_table =
+			new_lv->lv_snapshot_hash_table;
+		old_lv->lv_snapshot_hash_table_size =
+			new_lv->lv_snapshot_hash_table_size;
+		old_lv->lv_snapshot_hash_mask =
+			new_lv->lv_snapshot_hash_mask;
+
+		for (e = 0; e < new_lv->lv_remap_ptr; e++)
+			lvm_hash_link(new_lv->lv_block_exception + e,
+				      new_lv->lv_block_exception[e].rdev_org,
+				    new_lv->lv_block_exception[e].rsector_org,
+				      new_lv);
+
+	} else {
+
+		vfree(old_lv->lv_current_pe);
+		vfree(old_lv->lv_snapshot_hash_table);
+
+		old_lv->lv_size = new_lv->lv_size;
+		old_lv->lv_allocated_le = new_lv->lv_allocated_le;
+		old_lv->lv_current_le = new_lv->lv_current_le;
+		old_lv->lv_current_pe = new_lv->lv_current_pe;
+		lvm_gendisk.part[MINOR(old_lv->lv_dev)].nr_sects =
+			old_lv->lv_size;
+		lvm_size[MINOR(old_lv->lv_dev)] = old_lv->lv_size >> 1;
+
+		if (old_lv->lv_access & LV_SNAPSHOT_ORG) {
+			lv_t *snap;
+			for(snap = old_lv->lv_snapshot_next; snap;
+			    snap = snap->lv_snapshot_next) {
+				down(&snap->lv_snapshot_sem);
+				snap->lv_current_pe = old_lv->lv_current_pe;
+				snap->lv_allocated_le =
+					old_lv->lv_allocated_le;
+				snap->lv_current_le = old_lv->lv_current_le;
+				snap->lv_size = old_lv->lv_size;
+
+				lvm_gendisk.part[MINOR(snap->lv_dev)].nr_sects
+					= old_lv->lv_size;
+				lvm_size[MINOR(snap->lv_dev)] =
+					old_lv->lv_size >> 1;
+				__update_hardblocksize(snap);
+				up(&snap->lv_snapshot_sem);
+			}
 		}
 	}
 
+	__update_hardblocksize(old_lv);
+	up(&old_lv->lv_snapshot_sem);
+
 	return 0;
 } /* lvm_do_lv_extend_reduce() */
 
@@ -2668,10 +2318,10 @@
 static int lvm_do_lv_status_byname(vg_t *vg_ptr, void *arg)
 {
 	uint l;
-	ulong size;
-	lv_t lv;
-	lv_t *lv_ptr;
 	lv_status_byname_req_t lv_status_byname_req;
+	void *saved_ptr1;
+	void *saved_ptr2;
+	lv_t *lv_ptr;
 
 	if (vg_ptr == NULL) return -ENXIO;
 	if (copy_from_user(&lv_status_byname_req, arg,
@@ -2679,28 +2329,27 @@
 		return -EFAULT;
 
 	if (lv_status_byname_req.lv == NULL) return -EINVAL;
-	if (copy_from_user(&lv, lv_status_byname_req.lv,
-			   sizeof(lv_t)) != 0)
-		return -EFAULT;
 
 	for (l = 0; l < vg_ptr->lv_max; l++) {
-		lv_ptr = vg_ptr->lv[l];
-		if (lv_ptr != NULL &&
+		if ((lv_ptr = vg_ptr->lv[l]) != NULL &&
 		    strcmp(lv_ptr->lv_name,
-			    lv_status_byname_req.lv_name) == 0) {
-			if (copy_to_user(lv_status_byname_req.lv,
+			   lv_status_byname_req.lv_name) == 0) {
+		        /* Save usermode pointers */
+		        saved_ptr1 = lv_status_byname_req.lv->lv_current_pe;
+			saved_ptr2 = lv_status_byname_req.lv->lv_block_exception;
+		        if (copy_to_user(lv_status_byname_req.lv,
 					 lv_ptr,
 					 sizeof(lv_t)) != 0)
 				return -EFAULT;
-
-			if (lv.lv_current_pe != NULL) {
-				size = lv_ptr->lv_allocated_le *
-				       sizeof(pe_t);
-				if (copy_to_user(lv.lv_current_pe,
+			if (saved_ptr1 != NULL) {
+				if (copy_to_user(saved_ptr1,
 						 lv_ptr->lv_current_pe,
-						 size) != 0)
+						 lv_ptr->lv_allocated_le *
+				       		 sizeof(pe_t)) != 0)
 					return -EFAULT;
 			}
+			/* Restore usermode pointers */
+			lv_status_byname_req.lv->lv_current_pe = saved_ptr1;
 			return 0;
 		}
 	}
@@ -2713,34 +2362,37 @@
  */
 static int lvm_do_lv_status_byindex(vg_t *vg_ptr,void *arg)
 {
-	ulong size;
-	lv_t lv;
-	lv_t *lv_ptr;
 	lv_status_byindex_req_t lv_status_byindex_req;
+	void *saved_ptr1;
+	void *saved_ptr2;
+	lv_t *lv_ptr;
 
 	if (vg_ptr == NULL) return -ENXIO;
 	if (copy_from_user(&lv_status_byindex_req, arg,
 			   sizeof(lv_status_byindex_req)) != 0)
 		return -EFAULT;
 
-	if ((lvp = lv_status_byindex_req.lv) == NULL)
+	if (lv_status_byindex_req.lv == NULL)
 		return -EINVAL;
 	if ( ( lv_ptr = vg_ptr->lv[lv_status_byindex_req.lv_index]) == NULL)
 		return -ENXIO;
 
-	if (copy_from_user(&lv, lvp, sizeof(lv_t)) != 0)
-		return -EFAULT;
-
-	if (copy_to_user(lvp, lv_ptr, sizeof(lv_t)) != 0)
+	/* Save usermode pointers */
+	saved_ptr1 = lv_status_byindex_req.lv->lv_current_pe;
+	saved_ptr2 = lv_status_byindex_req.lv->lv_block_exception;
+	if (copy_to_user(lv_status_byindex_req.lv, lv_ptr, sizeof(lv_t)) != 0)
 		return -EFAULT;
-
-	if (lv.lv_current_pe != NULL) {
-		size = lv_ptr->lv_allocated_le * sizeof(pe_t);
-		if (copy_to_user(lv.lv_current_pe,
-			 	 lv_ptr->lv_current_pe,
-				 size) != 0)
+	if (saved_ptr1 != NULL) {
+		if (copy_to_user(saved_ptr1,
+				 lv_ptr->lv_current_pe,
+				 lv_ptr->lv_allocated_le *
+		       		 sizeof(pe_t)) != 0)
 			return -EFAULT;
 	}
+
+	/* Restore usermode pointers */
+	lv_status_byindex_req.lv->lv_current_pe = saved_ptr1;
+
 	return 0;
 } /* lvm_do_lv_status_byindex() */
 
@@ -2751,6 +2403,9 @@
 static int lvm_do_lv_status_bydev(vg_t * vg_ptr, void * arg) {
 	int l;
 	lv_status_bydev_req_t lv_status_bydev_req;
+	void *saved_ptr1;
+	void *saved_ptr2;
+	lv_t *lv_ptr;
 
 	if (vg_ptr == NULL) return -ENXIO;
 	if (copy_from_user(&lv_status_bydev_req, arg,
@@ -2763,10 +2418,23 @@
 	}
 
 	if ( l == vg_ptr->lv_max) return -ENXIO;
+	lv_ptr = vg_ptr->lv[l];
+
+	/* Save usermode pointers */
+	saved_ptr1 = lv_status_bydev_req.lv->lv_current_pe;
+	saved_ptr2 = lv_status_bydev_req.lv->lv_block_exception;
 
-	if (copy_to_user(lv_status_bydev_req.lv,
-			 vg_ptr->lv[l], sizeof(lv_t)) != 0)
+	if (copy_to_user(lv_status_bydev_req.lv, lv_ptr, sizeof(lv_t)) != 0)
 		return -EFAULT;
+	if (saved_ptr1 != NULL) {
+		if (copy_to_user(saved_ptr1,
+				 lv_ptr->lv_current_pe,
+				 lv_ptr->lv_allocated_le *
+		       		 sizeof(pe_t)) != 0)
+			return -EFAULT;
+	}
+	/* Restore usermode pointers */
+	lv_status_bydev_req.lv->lv_current_pe = saved_ptr1;
 
 	return 0;
 } /* lvm_do_lv_status_bydev() */
@@ -2786,11 +2454,11 @@
 		if ( (lv_ptr = vg_ptr->lv[l]) == NULL) continue;
 		if (lv_ptr->lv_dev == lv->lv_dev)
 		{
-			lvm_do_remove_proc_entry_of_lv ( vg_ptr, lv_ptr);
+			lvm_fs_remove_lv(vg_ptr, lv_ptr);
 			strncpy(lv_ptr->lv_name,
 				lv_req->lv_name,
 				NAME_LEN);
-			lvm_do_create_proc_entry_of_lv ( vg_ptr, lv_ptr);
+			lvm_fs_create_lv(vg_ptr, lv_ptr);
 			break;
 		}
 	}
@@ -2870,160 +2538,28 @@
 } /* lvm_do_pv_status() */
 
 
-
 /*
- * create a devfs entry for a volume group
+ * character device support function flush and invalidate all buffers of a PV
  */
-void lvm_do_create_devfs_entry_of_vg ( vg_t *vg_ptr) {
-	vg_devfs_handle[vg_ptr->vg_number] = devfs_mk_dir(0, vg_ptr->vg_name, NULL);
-	ch_devfs_handle[vg_ptr->vg_number] = devfs_register(
-		vg_devfs_handle[vg_ptr->vg_number] , "group",
-		DEVFS_FL_DEFAULT, LVM_CHAR_MAJOR, vg_ptr->vg_number,
-		S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP,
-		&lvm_chr_fops, NULL);
-}
-
-
-/*
- * create a /proc entry for a logical volume
- */
-void lvm_do_create_proc_entry_of_lv ( vg_t *vg_ptr, lv_t *lv_ptr) {
-	char *basename;
-
-	if ( vg_ptr->lv_subdir_pde != NULL) {
-		basename = strrchr(lv_ptr->lv_name, '/');
-		if (basename == NULL) basename = lv_ptr->lv_name;
-		else		      basename++;
-		pde = create_proc_entry(basename, S_IFREG,
-					vg_ptr->lv_subdir_pde);
-		if ( pde != NULL) {
-			pde->read_proc = lvm_proc_read_lv_info;
-			pde->data = lv_ptr;
-		}
-	}
-}
-
-
-/*
- * remove a /proc entry for a logical volume
- */
-void lvm_do_remove_proc_entry_of_lv ( vg_t *vg_ptr, lv_t *lv_ptr) {
-	char *basename;
-
-	if ( vg_ptr->lv_subdir_pde != NULL) {
-		basename = strrchr(lv_ptr->lv_name, '/');
-		if (basename == NULL) basename = lv_ptr->lv_name;
-		else		      basename++;
-		remove_proc_entry(basename, vg_ptr->lv_subdir_pde);
-	}
-}
-
-
-/*
- * create a /proc entry for a physical volume
- */
-void lvm_do_create_proc_entry_of_pv ( vg_t *vg_ptr, pv_t *pv_ptr) {
-	int offset = 0;
-	char *basename;
-	char buffer[NAME_LEN];
-
-	basename = pv_ptr->pv_name;
-	if (strncmp(basename, "/dev/", 5) == 0) offset = 5;
-	strncpy(buffer, basename + offset, sizeof(buffer));
-	basename = buffer;
-	while ( ( basename = strchr ( basename, '/')) != NULL) *basename = '_';
-	pde = create_proc_entry(buffer, S_IFREG, vg_ptr->pv_subdir_pde);
-	if ( pde != NULL) {
-		pde->read_proc = lvm_proc_read_pv_info;
-		pde->data = pv_ptr;
-	}
-}
-
-
-/*
- * remove a /proc entry for a physical volume
- */
-void lvm_do_remove_proc_entry_of_pv ( vg_t *vg_ptr, pv_t *pv_ptr) {
-	char *basename;
-
-	basename = strrchr(pv_ptr->pv_name, '/');
-	if ( vg_ptr->pv_subdir_pde != NULL) {
-		basename = strrchr(pv_ptr->pv_name, '/');
-		if (basename == NULL) basename = pv_ptr->pv_name;
-		else		      basename++;
-		remove_proc_entry(basename, vg_ptr->pv_subdir_pde);
-	}
-}
-
-
-/*
- * create a /proc entry for a volume group
- */
-void lvm_do_create_proc_entry_of_vg ( vg_t *vg_ptr) {
-	int l, p;
-	pv_t *pv_ptr;
-	lv_t *lv_ptr;
+static int lvm_do_pv_flush(void *arg)
+{
+	pv_flush_req_t pv_flush_req;
 
-	pde = create_proc_entry(vg_ptr->vg_name, S_IFDIR,
-				lvm_proc_vg_subdir);
-	if ( pde != NULL) {
-		vg_ptr->vg_dir_pde = pde;
-		pde = create_proc_entry("group", S_IFREG,
-					vg_ptr->vg_dir_pde);
-		if ( pde != NULL) {
-			pde->read_proc = lvm_proc_read_vg_info;
-			pde->data = vg_ptr;
-		}
-                pde = create_proc_entry(LVM_LV_SUBDIR, S_IFDIR,
-                                        vg_ptr->vg_dir_pde);
-                if ( pde != NULL) {
-                        vg_ptr->lv_subdir_pde = pde;
-                        for ( l = 0; l < vg_ptr->lv_max; l++) {
-				if ( ( lv_ptr = vg_ptr->lv[l]) == NULL) continue;
-				lvm_do_create_proc_entry_of_lv ( vg_ptr, lv_ptr);
-                        }
-                }
-                pde = create_proc_entry(LVM_PV_SUBDIR, S_IFDIR,
-                                        vg_ptr->vg_dir_pde);
-                if ( pde != NULL) {
-                        vg_ptr->pv_subdir_pde = pde;
-			for ( p = 0; p < vg_ptr->pv_max; p++) {
-				if ( ( pv_ptr = vg_ptr->pv[p]) == NULL) continue;
-				lvm_do_create_proc_entry_of_pv ( vg_ptr, pv_ptr);
-                        }
-                }
-        }
-}
+	if (copy_from_user(&pv_flush_req, arg,
+			   sizeof(pv_flush_req)) != 0)
+		return -EFAULT;
 
-/*
- * remove a /proc entry for a volume group
- */
-void lvm_do_remove_proc_entry_of_vg ( vg_t *vg_ptr) {
-	int l, p;
-	lv_t *lv_ptr;
-	pv_t *pv_ptr;
+	fsync_dev(pv_flush_req.pv_dev);
+	invalidate_buffers(pv_flush_req.pv_dev);
 
-	for ( l = 0; l < vg_ptr->lv_max; l++) {
-		if ( ( lv_ptr = vg_ptr->lv[l]) == NULL) continue;
-		lvm_do_remove_proc_entry_of_lv ( vg_ptr, vg_ptr->lv[l]);
-	}
-	for ( p = 0; p < vg_ptr->pv_max; p++) {
-		if ( ( pv_ptr = vg_ptr->pv[p]) == NULL) continue;
-		lvm_do_remove_proc_entry_of_pv ( vg_ptr, vg_ptr->pv[p]);
-	}
-	if ( vg_ptr->vg_dir_pde != NULL) {
-		remove_proc_entry(LVM_LV_SUBDIR, vg_ptr->vg_dir_pde);
-		remove_proc_entry(LVM_PV_SUBDIR, vg_ptr->vg_dir_pde);
-		remove_proc_entry("group", vg_ptr->vg_dir_pde);
-		remove_proc_entry(vg_ptr->vg_name, lvm_proc_vg_subdir);
-	}
+	return 0;
 }
 
 
 /*
  * support function initialize gendisk variables
  */
-void __init lvm_geninit(struct gendisk *lvm_gdisk)
+static void __init lvm_geninit(struct gendisk *lvm_gdisk)
 {
 	int i = 0;
 
@@ -3039,36 +2575,11 @@
 
 	blk_size[MAJOR_NR] = lvm_size;
 	blksize_size[MAJOR_NR] = lvm_blocksizes;
-	hardsect_size[MAJOR_NR] = lvm_blocksizes;
+	hardsect_size[MAJOR_NR] = lvm_hardsectsizes;
 
 	return;
 } /* lvm_gen_init() */
 
-
-/*
- * return a pointer to a '-' padded uuid
- */
-static char *lvm_show_uuid ( char *uuidstr) {
-	int i, j;
-	static char uuid[NAME_LEN] = { 0, };
-
-	memset ( uuid, 0, NAME_LEN);
-
-	i = 6;
-	memcpy ( uuid, uuidstr, i);
-	uuidstr += i;
-
-	for ( j = 0; j < 6; j++) {
-		uuid[i++] = '-';
-		memcpy ( &uuid[i], uuidstr, 4);
-		uuidstr += 4;
-		i += 4;
-	}
-
-	memcpy ( &uuid[i], uuidstr, 2 );
-
-	return uuid;
-}
 
 module_init(lvm_init);
 module_exit(lvm_cleanup);
--- 2.4.3aa/drivers/md/lvm-internal.h.orig Fri Apr  6 02:37:24 2001
+++ 2.4.3aa/drivers/md/lvm-internal.h Fri Apr  6 02:37:24 2001
@@ -0,0 +1,104 @@
+/*
+ * kernel/lvm_internal.h
+ *
+ * Copyright (C) 2001 Sistina Software
+ *
+ *
+ * LVM driver 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.
+ *
+ * LVM driver 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 GNU CC; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+/*
+ * Changelog
+ *
+ *    05/01/2001 - Factored this file out of lvm.c (Joe Thornber)
+ *    11/01/2001 - Renamed lvm_internal and added declarations
+ *                 for lvm_fs.c stuff
+ *
+ */
+
+#ifndef LVM_INTERNAL_H
+#define LVM_INTERNAL_H
+
+#include <linux/lvm.h>
+
+#define	_LVM_INTERNAL_H_VERSION	"LVM "LVM_RELEASE_NAME" ("LVM_RELEASE_DATE")"
+
+/* global variables, defined in lvm.c */
+extern char *lvm_version;
+extern char *lvm_short_version;
+extern ushort lvm_iop_version;
+extern int loadtime;
+extern const char *const lvm_name;
+
+
+extern vg_t *vg[];
+extern struct file_operations lvm_chr_fops;
+
+extern struct block_device_operations lvm_blk_dops;
+
+
+/* debug macros */
+#ifdef DEBUG_IOCTL
+#define P_IOCTL(fmt, args...) printk(KERN_DEBUG "lvm ioctl: " fmt, ## args)
+#else
+#define P_IOCTL(fmt, args...)
+#endif
+
+#ifdef DEBUG_MAP
+#define P_MAP(fmt, args...) printk(KERN_DEBUG "lvm map: " fmt, ## args)
+#else
+#define P_MAP(fmt, args...)
+#endif
+
+#ifdef DEBUG_KFREE
+#define P_KFREE(fmt, args...) printk(KERN_DEBUG "lvm kfree: " fmt, ## args)
+#else
+#define P_KFREE(fmt, args...)
+#endif
+
+#ifdef DEBUG_DEVICE
+#define P_DEV(fmt, args...) printk(KERN_DEBUG "lvm device: " fmt, ## args)
+#else
+#define P_DEV(fmt, args...)
+#endif
+
+
+/* lvm-snap.c */
+int lvm_get_blksize(kdev_t);
+int lvm_snapshot_alloc(lv_t *);
+int lvm_snapshot_fill_COW_page(vg_t *, lv_t *);
+int lvm_snapshot_COW(kdev_t, ulong, ulong, ulong, vg_t *vg, lv_t *);
+int lvm_snapshot_remap_block(kdev_t *, ulong *, ulong, lv_t *);
+void lvm_snapshot_release(lv_t *);
+int lvm_write_COW_table_block(vg_t *, lv_t *);
+void lvm_hash_link(lv_block_exception_t *, kdev_t, ulong, lv_t *);
+int lvm_snapshot_alloc_hash_table(lv_t *);
+void lvm_drop_snapshot(vg_t *vg, lv_t *, const char *);
+
+
+/* lvm_fs.c */
+void lvm_init_fs(void);
+void lvm_fin_fs(void);
+
+void lvm_fs_create_vg(vg_t *vg_ptr);
+void lvm_fs_remove_vg(vg_t *vg_ptr);
+void lvm_fs_create_lv(vg_t *vg_ptr, lv_t *lv);
+void lvm_fs_remove_lv(vg_t *vg_ptr, lv_t *lv);
+void lvm_fs_create_pv(vg_t *vg_ptr, pv_t *pv);
+void lvm_fs_remove_pv(vg_t *vg_ptr, pv_t *pv);
+
+#endif
--- 2.4.3aa/drivers/md/lvm-snap.c.orig	Sat Feb 10 02:34:09 2001
+++ 2.4.3aa/drivers/md/lvm-snap.c	Fri Apr  6 04:37:24 2001
@@ -2,22 +2,22 @@
  * kernel/lvm-snap.c
  *
  * Copyright (C) 2000 Andrea Arcangeli <andrea@suse.de> SuSE
- *                    Heinz Mauelshagen, Sistina Software (persistent snapshots)
+ *               2000 - 2001 Heinz Mauelshagen, Sistina Software
  *
  * LVM snapshot driver 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.
- * 
+ *
  * LVM snapshot driver 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 GNU CC; see the file COPYING.  If not, write to
  * the Free Software Foundation, 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA. 
+ * Boston, MA 02111-1307, USA.
  *
  */
 
@@ -26,6 +26,19 @@
  *
  *    05/07/2000 - implemented persistent snapshot support
  *    23/11/2000 - used cpu_to_le64 rather than my own macro
+ *    25/01/2001 - Put LockPage back in
+ *    01/02/2001 - A dropped snapshot is now set as inactive
+ *    14/02/2001 - tidied debug statements
+ *    19/02/2001 - changed rawio calls to pass in preallocated buffer_heads
+ *    26/02/2001 - introduced __brw_kiovec to remove a lot of conditional
+ *                 compiles.
+ *    07/03/2001 - fixed COW exception table not persistent on 2.2 (HM)
+ *    12/03/2001 - lvm_pv_get_number changes:
+ *                 o made it static
+ *                 o renamed it to _pv_get_number
+ *                 o pv number is returned in new uint * arg
+ *                 o -1 returned on error
+ *                 lvm_snapshot_fill_COW_table has a return value too.
  *
  */
 
@@ -38,26 +51,48 @@
 #include <linux/lvm.h>
 
 
-#include "lvm-snap.h"
+#include "lvm-internal.h"
+
+static char *lvm_snap_version __attribute__ ((unused)) = "LVM "LVM_RELEASE_NAME" snapshot code ("LVM_RELEASE_DATE")\n";
 
-static char *lvm_snap_version __attribute__ ((unused)) = "LVM 0.9.1_beta2 snapshot code (18/01/2001)\n";
 
 extern const char *const lvm_name;
 extern int lvm_blocksizes[];
 
 void lvm_snapshot_release(lv_t *);
 
-uint lvm_pv_get_number(vg_t * vg, kdev_t rdev)
-{
+static int _write_COW_table_block(vg_t *vg, lv_t *lv, int idx,
+				  const char **reason);
+static void _disable_snapshot(vg_t *vg, lv_t *lv);
+
+
+static inline int __brw_kiovec(int rw, int nr, struct kiobuf *iovec[],
+			       kdev_t dev, unsigned long b[], int size,
+			       lv_t *lv) {
+	return brw_kiovec(rw, nr, iovec, dev, b, size);
+}
+
+
+static int _pv_get_number(vg_t * vg, kdev_t rdev, uint *pvn) {
 	uint p;
+	for(p = 0; p < vg->pv_max; p++) {
+		if(vg->pv[p] == NULL)
+			continue;
 
-	for ( p = 0; p < vg->pv_max; p++)
-	{
-		if ( vg->pv[p] == NULL) continue;
-		if ( vg->pv[p]->pv_dev == rdev) break;
+		if(vg->pv[p]->pv_dev == rdev)
+			break;
+	}
+
+	if(p >= vg->pv_max) {
+		/* bad news, the snapshot COW table is probably corrupt */
+		printk(KERN_ERR
+		       "%s -- _pv_get_number failed for rdev = %u\n",
+		      lvm_name, rdev);
+		return -1;
 	}
 
-	return vg->pv[p]->pv_number;
+	*pvn = vg->pv[p]->pv_number;
+	return 0;
 }
 
 
@@ -133,7 +168,7 @@
 	return ret;
 }
 
-void lvm_drop_snapshot(lv_t * lv_snap, const char * reason)
+void lvm_drop_snapshot(vg_t *vg, lv_t *lv_snap, const char *reason)
 {
 	kdev_t last_dev;
 	int i;
@@ -142,6 +177,9 @@
 	   or error on this snapshot --> release it */
 	invalidate_buffers(lv_snap->lv_dev);
 
+	/* wipe the snapshot since it's inconsistent now */
+	_disable_snapshot(vg, lv_snap);
+
 	for (i = last_dev = 0; i < lv_snap->lv_remap_ptr; i++) {
 		if ( lv_snap->lv_block_exception[i].rdev_new != last_dev) {
 			last_dev = lv_snap->lv_block_exception[i].rdev_new;
@@ -150,9 +188,10 @@
 	}
 
 	lvm_snapshot_release(lv_snap);
+	lv_snap->lv_status &= ~LV_ACTIVE;
 
 	printk(KERN_INFO
-	       "%s -- giving up to snapshot %s on %s due %s\n",
+	       "%s -- giving up to snapshot %s on %s: %s\n",
 	       lvm_name, lv_snap->lv_snapshot_org->lv_name, lv_snap->lv_name,
 	       reason);
 }
@@ -209,129 +248,58 @@
 #endif
 
 
-void lvm_snapshot_fill_COW_page(vg_t * vg, lv_t * lv_snap)
+int lvm_snapshot_fill_COW_page(vg_t * vg, lv_t * lv_snap)
 {
-	int 	id = 0, is = lv_snap->lv_remap_ptr;
-	ulong	blksize_snap;
-	lv_COW_table_disk_t * lv_COW_table =
-	   ( lv_COW_table_disk_t *) page_address(lv_snap->lv_COW_table_page);
+	uint pvn;
+	int id = 0, is = lv_snap->lv_remap_ptr;
+	ulong blksize_snap;
+	lv_COW_table_disk_t * lv_COW_table = (lv_COW_table_disk_t *)
+		page_address(lv_snap->lv_COW_table_iobuf->maplist[0]);
+
+	if (is == 0)
+		return 0;
 
-	if (is == 0) return;
 	is--;
-        blksize_snap = lvm_get_blksize(lv_snap->lv_block_exception[is].rdev_new);
+        blksize_snap =
+		lvm_get_blksize(lv_snap->lv_block_exception[is].rdev_new);
         is -= is % (blksize_snap / sizeof(lv_COW_table_disk_t));
 
 	memset(lv_COW_table, 0, blksize_snap);
 	for ( ; is < lv_snap->lv_remap_ptr; is++, id++) {
 		/* store new COW_table entry */
-		lv_COW_table[id].pv_org_number = cpu_to_le64(lvm_pv_get_number(vg, lv_snap->lv_block_exception[is].rdev_org));
-		lv_COW_table[id].pv_org_rsector = cpu_to_le64(lv_snap->lv_block_exception[is].rsector_org);
-		lv_COW_table[id].pv_snap_number = cpu_to_le64(lvm_pv_get_number(vg, lv_snap->lv_block_exception[is].rdev_new));
-		lv_COW_table[id].pv_snap_rsector = cpu_to_le64(lv_snap->lv_block_exception[is].rsector_new);
+		lv_block_exception_t *be = lv_snap->lv_block_exception + is;
+		if(_pv_get_number(vg, be->rdev_org, &pvn))
+			goto bad;
+
+		lv_COW_table[id].pv_org_number = cpu_to_le64(pvn);
+		lv_COW_table[id].pv_org_rsector = cpu_to_le64(be->rsector_org);
+		if(_pv_get_number(vg, be->rdev_new, &pvn))
+			goto bad;
+
+		lv_COW_table[id].pv_snap_number = cpu_to_le64(pvn);
+		lv_COW_table[id].pv_snap_rsector =
+			cpu_to_le64(be->rsector_new);
 	}
+
+	return 0;
+
+ bad:
+	printk(KERN_ERR "%s -- lvm_snapshot_fill_COW_page failed", lvm_name);
+	return -1;
 }
 
 
 /*
  * writes a COW exception table sector to disk (HM)
- *
  */
-
-int lvm_write_COW_table_block(vg_t * vg, lv_t * lv_snap)
+int lvm_write_COW_table_block(vg_t * vg, lv_t *lv_snap)
 {
-	int blksize_snap;
-	int end_of_table;
-	int idx = lv_snap->lv_remap_ptr, idx_COW_table;
-	int nr_pages_tmp;
-	int length_tmp;
-	ulong snap_pe_start, COW_table_sector_offset,
-	      COW_entries_per_pe, COW_chunks_per_pe, COW_entries_per_block;
-	ulong blocks[1];
-	const char * reason;
-	kdev_t snap_phys_dev;
-	struct kiobuf * iobuf = lv_snap->lv_iobuf;
-	struct page * page_tmp;
-	lv_COW_table_disk_t * lv_COW_table =
-	   ( lv_COW_table_disk_t *) page_address(lv_snap->lv_COW_table_page);
-
-	idx--;
-
-	COW_chunks_per_pe = LVM_GET_COW_TABLE_CHUNKS_PER_PE(vg, lv_snap);
-	COW_entries_per_pe = LVM_GET_COW_TABLE_ENTRIES_PER_PE(vg, lv_snap);
-
-	/* get physical addresse of destination chunk */
-	snap_phys_dev = lv_snap->lv_block_exception[idx].rdev_new;
-	snap_pe_start = lv_snap->lv_block_exception[idx - (idx % COW_entries_per_pe)].rsector_new - lv_snap->lv_chunk_size;
-
-	blksize_snap = lvm_get_blksize(snap_phys_dev);
-
-        COW_entries_per_block = blksize_snap / sizeof(lv_COW_table_disk_t);
-        idx_COW_table = idx % COW_entries_per_pe % COW_entries_per_block;
-
-	if ( idx_COW_table == 0) memset(lv_COW_table, 0, blksize_snap);
-
-	/* sector offset into the on disk COW table */
-	COW_table_sector_offset = (idx % COW_entries_per_pe) / (SECTOR_SIZE / sizeof(lv_COW_table_disk_t));
-
-        /* COW table block to write next */
-	blocks[0] = (snap_pe_start + COW_table_sector_offset) >> (blksize_snap >> 10);
-
-	/* store new COW_table entry */
-	lv_COW_table[idx_COW_table].pv_org_number = cpu_to_le64(lvm_pv_get_number(vg, lv_snap->lv_block_exception[idx].rdev_org));
-	lv_COW_table[idx_COW_table].pv_org_rsector = cpu_to_le64(lv_snap->lv_block_exception[idx].rsector_org);
-	lv_COW_table[idx_COW_table].pv_snap_number = cpu_to_le64(lvm_pv_get_number(vg, snap_phys_dev));
-	lv_COW_table[idx_COW_table].pv_snap_rsector = cpu_to_le64(lv_snap->lv_block_exception[idx].rsector_new);
-
-	length_tmp = iobuf->length;
-	iobuf->length = blksize_snap;
-	page_tmp = iobuf->maplist[0];
-        iobuf->maplist[0] = lv_snap->lv_COW_table_page;
-	nr_pages_tmp = iobuf->nr_pages;
-	iobuf->nr_pages = 1;
-
-	if (brw_kiovec(WRITE, 1, &iobuf, snap_phys_dev,
-		       blocks, blksize_snap) != blksize_snap)
-		goto fail_raw_write;
-
-
-	/* initialization of next COW exception table block with zeroes */
-	end_of_table = idx % COW_entries_per_pe == COW_entries_per_pe - 1;
-	if (idx_COW_table % COW_entries_per_block == COW_entries_per_block - 1 || end_of_table)
-	{
-		/* don't go beyond the end */
-		if (idx + 1 >= lv_snap->lv_remap_end) goto good_out;
-
-		memset(lv_COW_table, 0, blksize_snap);
-
-		if (end_of_table)
-		{
-			idx++;
-			snap_phys_dev = lv_snap->lv_block_exception[idx].rdev_new;
-			snap_pe_start = lv_snap->lv_block_exception[idx - (idx % COW_entries_per_pe)].rsector_new - lv_snap->lv_chunk_size;
-			blksize_snap = lvm_get_blksize(snap_phys_dev);
-			blocks[0] = snap_pe_start >> (blksize_snap >> 10);
-		} else blocks[0]++;
-
-		if (brw_kiovec(WRITE, 1, &iobuf, snap_phys_dev,
-			       blocks, blksize_snap) != blksize_snap)
-			goto fail_raw_write;
-	}
-
-
- good_out:
-	iobuf->length = length_tmp;
-        iobuf->maplist[0] = page_tmp;
-	iobuf->nr_pages = nr_pages_tmp;
-	return 0;
-
-	/* slow path */
- out:
-	lvm_drop_snapshot(lv_snap, reason);
-	return 1;
-
- fail_raw_write:
-	reason = "write error";
-	goto out;
+	int r;
+	const char *err;
+	if((r = _write_COW_table_block(vg, lv_snap,
+				       lv_snap->lv_remap_ptr - 1, &err)))
+		lvm_drop_snapshot(vg, lv_snap, err);
+	return r;
 }
 
 /*
@@ -346,7 +314,7 @@
 		     unsigned long org_phys_sector,
 		     unsigned long org_pe_start,
 		     unsigned long org_virt_sector,
-		     lv_t * lv_snap)
+		     vg_t *vg, lv_t* lv_snap)
 {
 	const char * reason;
 	unsigned long org_start, snap_start, snap_phys_dev, virt_start, pe_off;
@@ -372,13 +340,11 @@
 #ifdef DEBUG_SNAPSHOT
 	printk(KERN_INFO
 	       "%s -- COW: "
-	       "org %02d:%02d faulting %lu start %lu, "
-	       "snap %02d:%02d start %lu, "
+	       "org %s faulting %lu start %lu, snap %s start %lu, "
 	       "size %d, pe_start %lu pe_off %lu, virt_sec %lu\n",
 	       lvm_name,
-	       MAJOR(org_phys_dev), MINOR(org_phys_dev), org_phys_sector,
-	       org_start,
-	       MAJOR(snap_phys_dev), MINOR(snap_phys_dev), snap_start,
+	       kdevname(org_phys_dev), org_phys_sector, org_start,
+	       kdevname(snap_phys_dev), snap_start,
 	       chunk_size,
 	       org_pe_start, pe_off,
 	       org_virt_sector);
@@ -404,14 +370,14 @@
 
 		lvm_snapshot_prepare_blocks(blocks, org_start,
 					    nr_sectors, blksize_org);
-		if (brw_kiovec(READ, 1, &iobuf, org_phys_dev,
-			       blocks, blksize_org) != (nr_sectors<<9))
+		if (__brw_kiovec(READ, 1, &iobuf, org_phys_dev, blocks,
+				 blksize_org, lv_snap) != (nr_sectors<<9))
 			goto fail_raw_read;
 
 		lvm_snapshot_prepare_blocks(blocks, snap_start,
 					    nr_sectors, blksize_snap);
-		if (brw_kiovec(WRITE, 1, &iobuf, snap_phys_dev,
-			       blocks, blksize_snap) != (nr_sectors<<9))
+		if (__brw_kiovec(WRITE, 1, &iobuf, snap_phys_dev, blocks,
+				 blksize_snap, lv_snap) != (nr_sectors<<9))
 			goto fail_raw_write;
 	}
 
@@ -436,20 +402,20 @@
 	return 0;
 
 	/* slow path */
- out:
-	lvm_drop_snapshot(lv_snap, reason);
+out:
+	lvm_drop_snapshot(vg, lv_snap, reason);
 	return 1;
 
- fail_out_of_space:
+fail_out_of_space:
 	reason = "out of space";
 	goto out;
- fail_raw_read:
+fail_raw_read:
 	reason = "read error";
 	goto out;
- fail_raw_write:
+fail_raw_write:
 	reason = "write error";
 	goto out;
- fail_blksize:
+fail_blksize:
 	reason = "blocksize error";
 	goto out;
 }
@@ -458,30 +424,30 @@
 {
 	int bytes, nr_pages, err, i;
 
-	bytes = sectors << 9;
+	bytes = sectors * SECTOR_SIZE;
 	nr_pages = (bytes + ~PAGE_MASK) >> PAGE_SHIFT;
 	err = expand_kiobuf(iobuf, nr_pages);
-	if (err)
-		goto out;
+	if (err) goto out;
 
 	err = -ENOMEM;
-	iobuf->locked = 0;
+	iobuf->locked = 1;
 	iobuf->nr_pages = 0;
 	for (i = 0; i < nr_pages; i++)
 	{
 		struct page * page;
 
 		page = alloc_page(GFP_KERNEL);
-		if (!page)
-			goto out;
+		if (!page) goto out;
 
 		iobuf->maplist[i] = page;
+		LockPage(page);
 		iobuf->nr_pages++;
 	}
 	iobuf->offset = 0;
 
 	err = 0;
- out:
+
+out:
 	return err;
 }
 
@@ -523,41 +489,49 @@
 	while (buckets--)
 		INIT_LIST_HEAD(hash+buckets);
 	err = 0;
- out:
+out:
 	return err;
 }
 
 int lvm_snapshot_alloc(lv_t * lv_snap)
 {
-	int err, blocksize, max_sectors;
+	int ret, max_sectors;
 
-	err = alloc_kiovec(1, &lv_snap->lv_iobuf);
-	if (err)
-		goto out;
+	/* allocate kiovec to do chunk io */
+	ret = alloc_kiovec(1, &lv_snap->lv_iobuf);
+	if (ret) goto out;
 
-	blocksize = lvm_blocksizes[MINOR(lv_snap->lv_dev)];
 	max_sectors = KIO_MAX_SECTORS << (PAGE_SHIFT-9);
 
-	err = lvm_snapshot_alloc_iobuf_pages(lv_snap->lv_iobuf, max_sectors);
-	if (err)
-		goto out_free_kiovec;
+	ret = lvm_snapshot_alloc_iobuf_pages(lv_snap->lv_iobuf, max_sectors);
+	if (ret) goto out_free_kiovec;
 
-	err = lvm_snapshot_alloc_hash_table(lv_snap);
-	if (err)
-		goto out_free_kiovec;
+	/* allocate kiovec to do exception table io */
+	ret = alloc_kiovec(1, &lv_snap->lv_COW_table_iobuf);
+	if (ret) goto out_free_kiovec;
 
+	ret = lvm_snapshot_alloc_iobuf_pages(lv_snap->lv_COW_table_iobuf,
+					     PAGE_SIZE/SECTOR_SIZE);
+	if (ret) goto out_free_both_kiovecs;
 
-		lv_snap->lv_COW_table_page = alloc_page(GFP_KERNEL);
-		if (!lv_snap->lv_COW_table_page)
-			goto out_free_kiovec;
+	ret = lvm_snapshot_alloc_hash_table(lv_snap);
+	if (ret) goto out_free_both_kiovecs;
 
- out:
-	return err;
 
- out_free_kiovec:
+out:
+	return ret;
+
+out_free_both_kiovecs:
+	unmap_kiobuf(lv_snap->lv_COW_table_iobuf);
+	free_kiovec(1, &lv_snap->lv_COW_table_iobuf);
+	lv_snap->lv_COW_table_iobuf = NULL;
+
+out_free_kiovec:
 	unmap_kiobuf(lv_snap->lv_iobuf);
 	free_kiovec(1, &lv_snap->lv_iobuf);
-	vfree(lv_snap->lv_snapshot_hash_table);
+	lv_snap->lv_iobuf = NULL;
+	if (lv_snap->lv_snapshot_hash_table != NULL)
+		vfree(lv_snap->lv_snapshot_hash_table);
 	lv_snap->lv_snapshot_hash_table = NULL;
 	goto out;
 }
@@ -582,9 +556,125 @@
 		free_kiovec(1, &lv->lv_iobuf);
 		lv->lv_iobuf = NULL;
 	}
-	if (lv->lv_COW_table_page)
+	if (lv->lv_COW_table_iobuf)
 	{
-		free_page((ulong)lv->lv_COW_table_page);
-		lv->lv_COW_table_page = NULL;
+	        kiobuf_wait_for_io(lv->lv_COW_table_iobuf);
+		unmap_kiobuf(lv->lv_COW_table_iobuf);
+		free_kiovec(1, &lv->lv_COW_table_iobuf);
+		lv->lv_COW_table_iobuf = NULL;
+	}
+}
+
+
+static int _write_COW_table_block(vg_t *vg, lv_t *lv_snap,
+				  int idx, const char **reason) {
+	int blksize_snap;
+	int end_of_table;
+	int idx_COW_table;
+	uint pvn;
+	ulong snap_pe_start, COW_table_sector_offset,
+	      COW_entries_per_pe, COW_chunks_per_pe, COW_entries_per_block;
+	ulong blocks[1];
+	kdev_t snap_phys_dev;
+	lv_block_exception_t *be;
+	struct kiobuf *COW_table_iobuf = lv_snap->lv_COW_table_iobuf;
+	lv_COW_table_disk_t * lv_COW_table =
+	   ( lv_COW_table_disk_t *) page_address(lv_snap->lv_COW_table_iobuf->maplist[0]);
+
+	COW_chunks_per_pe = LVM_GET_COW_TABLE_CHUNKS_PER_PE(vg, lv_snap);
+	COW_entries_per_pe = LVM_GET_COW_TABLE_ENTRIES_PER_PE(vg, lv_snap);
+
+	/* get physical addresse of destination chunk */
+	snap_phys_dev = lv_snap->lv_block_exception[idx].rdev_new;
+	snap_pe_start = lv_snap->lv_block_exception[idx - (idx % COW_entries_per_pe)].rsector_new - lv_snap->lv_chunk_size;
+
+	blksize_snap = lvm_get_blksize(snap_phys_dev);
+
+        COW_entries_per_block = blksize_snap / sizeof(lv_COW_table_disk_t);
+        idx_COW_table = idx % COW_entries_per_pe % COW_entries_per_block;
+
+	if ( idx_COW_table == 0) memset(lv_COW_table, 0, blksize_snap);
+
+	/* sector offset into the on disk COW table */
+	COW_table_sector_offset = (idx % COW_entries_per_pe) / (SECTOR_SIZE / sizeof(lv_COW_table_disk_t));
+
+        /* COW table block to write next */
+	blocks[0] = (snap_pe_start + COW_table_sector_offset) >> (blksize_snap >> 10);
+
+	/* store new COW_table entry */
+	be = lv_snap->lv_block_exception + idx;
+	if(_pv_get_number(vg, be->rdev_org, &pvn))
+		goto fail_pv_get_number;
+
+	lv_COW_table[idx_COW_table].pv_org_number = cpu_to_le64(pvn);
+	lv_COW_table[idx_COW_table].pv_org_rsector =
+		cpu_to_le64(be->rsector_org);
+	if(_pv_get_number(vg, snap_phys_dev, &pvn))
+		goto fail_pv_get_number;
+
+	lv_COW_table[idx_COW_table].pv_snap_number = cpu_to_le64(pvn);
+	lv_COW_table[idx_COW_table].pv_snap_rsector =
+		cpu_to_le64(be->rsector_new);
+
+	COW_table_iobuf->length = blksize_snap;
+	/* COW_table_iobuf->nr_pages = 1; */
+
+	if (__brw_kiovec(WRITE, 1, &COW_table_iobuf, snap_phys_dev,
+			 blocks, blksize_snap, lv_snap) != blksize_snap)
+		goto fail_raw_write;
+
+	/* initialization of next COW exception table block with zeroes */
+	end_of_table = idx % COW_entries_per_pe == COW_entries_per_pe - 1;
+	if (idx_COW_table % COW_entries_per_block == COW_entries_per_block - 1 || end_of_table)
+	{
+		/* don't go beyond the end */
+		if (idx + 1 >= lv_snap->lv_remap_end) goto out;
+
+		memset(lv_COW_table, 0, blksize_snap);
+
+		if (end_of_table)
+		{
+			idx++;
+			snap_phys_dev = lv_snap->lv_block_exception[idx].rdev_new;
+			snap_pe_start = lv_snap->lv_block_exception[idx - (idx % COW_entries_per_pe)].rsector_new - lv_snap->lv_chunk_size;
+			blksize_snap = lvm_get_blksize(snap_phys_dev);
+			blocks[0] = snap_pe_start >> (blksize_snap >> 10);
+		} else blocks[0]++;
+
+		if (__brw_kiovec(WRITE, 1, &COW_table_iobuf, snap_phys_dev,
+                                 blocks, blksize_snap, lv_snap) !=
+                    blksize_snap)
+			goto fail_raw_write;
+	}
+
+out:
+	return 0;
+
+fail_raw_write:
+	*reason = "write error";
+	return 1;
+
+fail_pv_get_number:
+	*reason = "_pv_get_number failed";
+	return 1;
+}
+
+/*
+ * FIXME_1.2
+ * This function is a bit of a hack; we need to ensure that the
+ * snapshot is never made active again, because it will surely be
+ * corrupt.  At the moment we do not have access to the LVM metadata
+ * from within the kernel.  So we set the first exception to point to
+ * sector 1 (which will always be within the metadata, and as such
+ * invalid).  User land tools will check for this when they are asked
+ * to activate the snapshot and prevent this from happening.
+ */
+
+static void _disable_snapshot(vg_t *vg, lv_t *lv) {
+	const char *err;
+	lv->lv_block_exception[0].rsector_org = LVM_SNAPSHOT_DROPPED_SECTOR;
+	if(_write_COW_table_block(vg, lv, 0, &err) < 0) {
+		printk(KERN_ERR "%s -- couldn't disable snapshot: %s\n",
+		       lvm_name, err);
 	}
 }
--- 2.4.3aa/drivers/md/lvm-fs.c.orig Fri Apr  6 02:37:24 2001
+++ 2.4.3aa/drivers/md/lvm-fs.c Fri Apr  6 02:37:24 2001
@@ -0,0 +1,596 @@
+/*
+ * kernel/lvm-fs.c
+ *
+ * Copyright (C) 2001 Sistina Software
+ *
+ * January,February 2001
+ *
+ * LVM driver 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.
+ *
+ * LVM driver 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 GNU CC; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+/*
+ * Changelog
+ *
+ *    11/01/2001 - First version (Joe Thornber)
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/version.h>
+
+#include <linux/kernel.h>
+#include <linux/vmalloc.h>
+#include <linux/smp_lock.h>
+
+#include <linux/devfs_fs_kernel.h>
+#include <linux/proc_fs.h>
+#include <linux/lvm.h>
+
+#include "lvm-internal.h"
+
+
+static int _proc_read_vg(char *page, char **start, off_t off,
+			 int count, int *eof, void *data);
+static int _proc_read_lv(char *page, char **start, off_t off,
+			 int count, int *eof, void *data);
+static int _proc_read_pv(char *page, char **start, off_t off,
+			 int count, int *eof, void *data);
+static int _proc_read_global(char *page, char **start, off_t off,
+			     int count, int *eof, void *data);
+
+static int _vg_info(vg_t *vg_ptr, char *buf);
+static int _lv_info(vg_t *vg_ptr, lv_t *lv_ptr, char *buf);
+static int _pv_info(pv_t *pv_ptr, char *buf);
+
+static void _show_uuid(const char *src, char *b, char *e);
+
+static devfs_handle_t lvm_devfs_handle;
+static devfs_handle_t vg_devfs_handle[MAX_VG];
+static devfs_handle_t ch_devfs_handle[MAX_VG];
+static devfs_handle_t lv_devfs_handle[MAX_LV];
+
+static struct proc_dir_entry *lvm_proc_dir = NULL;
+static struct proc_dir_entry *lvm_proc_vg_subdir = NULL;
+
+/* inline functions */
+
+/* public interface */
+void lvm_init_fs() {
+	struct proc_dir_entry *pde;
+
+	lvm_devfs_handle = devfs_register(
+		0 , "lvm", 0, 0, LVM_CHAR_MAJOR,
+		S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP,
+		&lvm_chr_fops, NULL);
+
+	lvm_proc_dir = create_proc_entry(LVM_DIR, S_IFDIR, &proc_root);
+	if (lvm_proc_dir) {
+		lvm_proc_vg_subdir = create_proc_entry(LVM_VG_SUBDIR, S_IFDIR,
+						       lvm_proc_dir);
+		pde = create_proc_entry(LVM_GLOBAL, S_IFREG, lvm_proc_dir);
+		if ( pde != NULL) pde->read_proc = _proc_read_global;
+	}
+}
+
+void lvm_fin_fs() {
+	devfs_unregister (lvm_devfs_handle);
+
+	remove_proc_entry(LVM_GLOBAL, lvm_proc_dir);
+	remove_proc_entry(LVM_VG_SUBDIR, lvm_proc_dir);
+	remove_proc_entry(LVM_DIR, &proc_root);
+}
+
+void lvm_fs_create_vg(vg_t *vg_ptr) {
+	struct proc_dir_entry *pde;
+
+	vg_devfs_handle[vg_ptr->vg_number] =
+		devfs_mk_dir(0, vg_ptr->vg_name, NULL);
+
+	ch_devfs_handle[vg_ptr->vg_number] = devfs_register(
+		vg_devfs_handle[vg_ptr->vg_number] , "group",
+		DEVFS_FL_DEFAULT, LVM_CHAR_MAJOR, vg_ptr->vg_number,
+		S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP,
+		&lvm_chr_fops, NULL);
+
+	vg_ptr->vg_dir_pde = create_proc_entry(vg_ptr->vg_name, S_IFDIR,
+					       lvm_proc_vg_subdir);
+
+	if((pde = create_proc_entry("group", S_IFREG, vg_ptr->vg_dir_pde))) {
+		pde->read_proc = _proc_read_vg;
+		pde->data = vg_ptr;
+	}
+
+	vg_ptr->lv_subdir_pde =
+		create_proc_entry(LVM_LV_SUBDIR, S_IFDIR, vg_ptr->vg_dir_pde);
+
+	vg_ptr->pv_subdir_pde =
+		create_proc_entry(LVM_PV_SUBDIR, S_IFDIR, vg_ptr->vg_dir_pde);
+}
+
+void lvm_fs_remove_vg(vg_t *vg_ptr) {
+	int i;
+
+	devfs_unregister(ch_devfs_handle[vg_ptr->vg_number]);
+	devfs_unregister(vg_devfs_handle[vg_ptr->vg_number]);
+
+	/* remove lv's */
+	for(i = 0; i < vg_ptr->lv_max; i++)
+		if(vg_ptr->lv[i]) lvm_fs_remove_lv(vg_ptr, vg_ptr->lv[i]);
+
+	/* remove pv's */
+	for(i = 0; i < vg_ptr->pv_max; i++)
+		if(vg_ptr->pv[i]) lvm_fs_remove_pv(vg_ptr, vg_ptr->pv[i]);
+
+	if(vg_ptr->vg_dir_pde) {
+		remove_proc_entry(LVM_LV_SUBDIR, vg_ptr->vg_dir_pde);
+		remove_proc_entry(LVM_PV_SUBDIR, vg_ptr->vg_dir_pde);
+		remove_proc_entry("group", vg_ptr->vg_dir_pde);
+		remove_proc_entry(vg_ptr->vg_name, lvm_proc_vg_subdir);
+	}
+}
+
+
+static inline const char *_basename(const char *str) {
+	const char *name = strrchr(str, '/');
+	name = name ? name + 1 : str;
+	return name;
+}
+
+void lvm_fs_create_lv(vg_t *vg_ptr, lv_t *lv) {
+	struct proc_dir_entry *pde;
+	const char *name = _basename(lv->lv_name);
+
+	lv_devfs_handle[MINOR(lv->lv_dev)] = devfs_register(
+		vg_devfs_handle[vg_ptr->vg_number], name,
+		DEVFS_FL_DEFAULT, LVM_BLK_MAJOR, MINOR(lv->lv_dev),
+		S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP,
+		&lvm_blk_dops, NULL);
+
+	if(vg_ptr->lv_subdir_pde &&
+	   (pde = create_proc_entry(name, S_IFREG, vg_ptr->lv_subdir_pde))) {
+		pde->read_proc = _proc_read_lv;
+		pde->data = lv;
+	}
+}
+
+void lvm_fs_remove_lv(vg_t *vg_ptr, lv_t *lv) {
+	devfs_unregister(lv_devfs_handle[MINOR(lv->lv_dev)]);
+
+	if(vg_ptr->lv_subdir_pde) {
+		const char *name = _basename(lv->lv_name);
+		remove_proc_entry(name, vg_ptr->lv_subdir_pde);
+	}
+}
+
+
+static inline void _make_pv_name(const char *src, char *b, char *e) {
+	int offset = strlen(LVM_DIR_PREFIX);
+	if(strncmp(src, LVM_DIR_PREFIX, offset))
+		offset = 0;
+
+	e--;
+	src += offset;
+	while(*src && (b != e)) {
+		*b++ = (*src == '/') ? '_' : *src;
+		src++;
+	}
+	*b = '\0';
+}
+
+void lvm_fs_create_pv(vg_t *vg_ptr, pv_t *pv) {
+	struct proc_dir_entry *pde;
+	char name[NAME_LEN];
+
+	if(!vg_ptr->pv_subdir_pde)
+		return;
+
+	_make_pv_name(pv->pv_name, name, name + sizeof(name));
+	if((pde = create_proc_entry(name, S_IFREG, vg_ptr->pv_subdir_pde))) {
+		pde->read_proc = _proc_read_pv;
+		pde->data = pv;
+	}
+}
+
+void lvm_fs_remove_pv(vg_t *vg_ptr, pv_t *pv) {
+	char name[NAME_LEN];
+
+	if(!vg_ptr->pv_subdir_pde)
+		return;
+
+	_make_pv_name(pv->pv_name, name, name + sizeof(name));
+	remove_proc_entry(name, vg_ptr->pv_subdir_pde);
+}
+
+
+static int _proc_read_vg(char *page, char **start, off_t off,
+			  int count, int *eof, void *data) {
+	int sz = 0;
+	vg_t *vg_ptr = data;
+	char uuid[NAME_LEN];
+
+	sz += sprintf(page + sz, "name:         %s\n", vg_ptr->vg_name);
+	sz += sprintf(page + sz, "size:         %u\n",
+		      vg_ptr->pe_total * vg_ptr->pe_size / 2);
+	sz += sprintf(page + sz, "access:       %u\n", vg_ptr->vg_access);
+	sz += sprintf(page + sz, "status:       %u\n", vg_ptr->vg_status);
+	sz += sprintf(page + sz, "number:       %u\n", vg_ptr->vg_number);
+	sz += sprintf(page + sz, "LV max:       %u\n", vg_ptr->lv_max);
+	sz += sprintf(page + sz, "LV current:   %u\n", vg_ptr->lv_cur);
+	sz += sprintf(page + sz, "LV open:      %u\n", vg_ptr->lv_open);
+	sz += sprintf(page + sz, "PV max:       %u\n", vg_ptr->pv_max);
+	sz += sprintf(page + sz, "PV current:   %u\n", vg_ptr->pv_cur);
+	sz += sprintf(page + sz, "PV active:    %u\n", vg_ptr->pv_act);
+	sz += sprintf(page + sz, "PE size:      %u\n", vg_ptr->pe_size / 2);
+	sz += sprintf(page + sz, "PE total:     %u\n", vg_ptr->pe_total);
+	sz += sprintf(page + sz, "PE allocated: %u\n", vg_ptr->pe_allocated);
+
+	_show_uuid(vg_ptr->vg_uuid, uuid, uuid + sizeof(uuid));
+	sz += sprintf(page + sz, "uuid:         %s\n", uuid);
+
+	return sz;
+}
+
+static int _proc_read_lv(char *page, char **start, off_t off,
+			  int count, int *eof, void *data) {
+	int sz = 0;
+	lv_t *lv = data;
+
+	sz += sprintf(page + sz, "name:         %s\n", lv->lv_name);
+	sz += sprintf(page + sz, "size:         %u\n", lv->lv_size);
+	sz += sprintf(page + sz, "access:       %u\n", lv->lv_access);
+	sz += sprintf(page + sz, "status:       %u\n", lv->lv_status);
+	sz += sprintf(page + sz, "number:       %u\n", lv->lv_number);
+	sz += sprintf(page + sz, "open:         %u\n", lv->lv_open);
+	sz += sprintf(page + sz, "allocation:   %u\n", lv->lv_allocation);
+	sz += sprintf(page + sz, "device:       %02u:%02u\n",
+		      MAJOR(lv->lv_dev), MINOR(lv->lv_dev));
+
+	return sz;
+}
+
+static int _proc_read_pv(char *page, char **start, off_t off,
+			 int count, int *eof, void *data) {
+	int sz = 0;
+	pv_t *pv = data;
+	char uuid[NAME_LEN];
+
+	sz += sprintf(page + sz, "name:         %s\n", pv->pv_name);
+	sz += sprintf(page + sz, "size:         %u\n", pv->pv_size);
+	sz += sprintf(page + sz, "status:       %u\n", pv->pv_status);
+	sz += sprintf(page + sz, "number:       %u\n", pv->pv_number);
+	sz += sprintf(page + sz, "allocatable:  %u\n", pv->pv_allocatable);
+	sz += sprintf(page + sz, "LV current:   %u\n", pv->lv_cur);
+	sz += sprintf(page + sz, "PE size:      %u\n", pv->pe_size / 2);
+	sz += sprintf(page + sz, "PE total:     %u\n", pv->pe_total);
+	sz += sprintf(page + sz, "PE allocated: %u\n", pv->pe_allocated);
+	sz += sprintf(page + sz, "device:       %02u:%02u\n",
+                      MAJOR(pv->pv_dev), MINOR(pv->pv_dev));
+
+	_show_uuid(pv->pv_uuid, uuid, uuid + sizeof(uuid));
+	sz += sprintf(page + sz, "uuid:         %s\n", uuid);
+
+	return sz;
+}
+
+static int _proc_read_global(char *page, char **start, off_t pos, int count,
+			     int *eof, void *data) {
+
+#define  LVM_PROC_BUF   ( i == 0 ? dummy_buf : &buf[sz])
+
+	int c, i, l, p, v, vg_counter, pv_counter, lv_counter, lv_open_counter,
+		lv_open_total, pe_t_bytes, hash_table_bytes, lv_block_exception_t_bytes, seconds;
+	static off_t sz;
+	off_t sz_last;
+	static char *buf = NULL;
+	static char dummy_buf[160];	/* sized for 2 lines */
+	vg_t *vg_ptr;
+	lv_t *lv_ptr;
+	pv_t *pv_ptr;
+
+
+#ifdef DEBUG_LVM_PROC_GET_INFO
+	printk(KERN_DEBUG
+	       "%s - lvm_proc_get_global_info CALLED  pos: %lu  count: %d  whence: %d\n",
+	       lvm_name, pos, count, whence);
+#endif
+
+	if(pos != 0 && buf != NULL)
+		goto out;
+
+	sz_last = vg_counter = pv_counter = lv_counter = lv_open_counter = \
+		lv_open_total = pe_t_bytes = hash_table_bytes = \
+		lv_block_exception_t_bytes = 0;
+
+	/* get some statistics */
+	for (v = 0; v < ABS_MAX_VG; v++) {
+		if ((vg_ptr = vg[v]) != NULL) {
+			vg_counter++;
+			pv_counter += vg_ptr->pv_cur;
+			lv_counter += vg_ptr->lv_cur;
+			if (vg_ptr->lv_cur > 0) {
+				for (l = 0; l < vg[v]->lv_max; l++) {
+					if ((lv_ptr = vg_ptr->lv[l]) != NULL) {
+						pe_t_bytes += lv_ptr->lv_allocated_le;
+						hash_table_bytes += lv_ptr->lv_snapshot_hash_table_size;
+						if (lv_ptr->lv_block_exception != NULL)
+							lv_block_exception_t_bytes += lv_ptr->lv_remap_end;
+						if (lv_ptr->lv_open > 0) {
+							lv_open_counter++;
+							lv_open_total += lv_ptr->lv_open;
+						}
+					}
+				}
+			}
+		}
+	}
+
+	pe_t_bytes *= sizeof(pe_t);
+	lv_block_exception_t_bytes *= sizeof(lv_block_exception_t);
+
+	if (buf != NULL) {
+		P_KFREE("%s -- vfree %d\n", lvm_name, __LINE__);
+		lock_kernel();
+		vfree(buf);
+		unlock_kernel();
+		buf = NULL;
+	}
+	/* 2 times: first to get size to allocate buffer,
+	   2nd to fill the malloced buffer */
+	for (i = 0; i < 2; i++) {
+		sz = 0;
+		sz += sprintf(LVM_PROC_BUF,
+			      "LVM "
+#ifdef MODULE
+			      "module"
+#else
+			      "driver"
+#endif
+			      " %s\n\n"
+			      "Total:  %d VG%s  %d PV%s  %d LV%s ",
+			      lvm_short_version,
+			      vg_counter, vg_counter == 1 ? "" : "s",
+			      pv_counter, pv_counter == 1 ? "" : "s",
+			      lv_counter, lv_counter == 1 ? "" : "s");
+		sz += sprintf(LVM_PROC_BUF,
+			      "(%d LV%s open",
+			      lv_open_counter,
+			      lv_open_counter == 1 ? "" : "s");
+		if (lv_open_total > 0)
+			sz += sprintf(LVM_PROC_BUF,
+				      " %d times)\n",
+				      lv_open_total);
+		else
+			sz += sprintf(LVM_PROC_BUF, ")");
+		sz += sprintf(LVM_PROC_BUF,
+			      "\nGlobal: %lu bytes malloced   IOP version: %d   ",
+			      vg_counter * sizeof(vg_t) +
+			      pv_counter * sizeof(pv_t) +
+			      lv_counter * sizeof(lv_t) +
+			      pe_t_bytes + hash_table_bytes + lv_block_exception_t_bytes + sz_last,
+			      lvm_iop_version);
+
+		seconds = CURRENT_TIME - loadtime;
+		if (seconds < 0)
+			loadtime = CURRENT_TIME + seconds;
+		if (seconds / 86400 > 0) {
+			sz += sprintf(LVM_PROC_BUF, "%d day%s ",
+				      seconds / 86400,
+				      seconds / 86400 == 0 ||
+				      seconds / 86400 > 1 ? "s" : "");
+		}
+		sz += sprintf(LVM_PROC_BUF, "%d:%02d:%02d active\n",
+			      (seconds % 86400) / 3600,
+			      (seconds % 3600) / 60,
+			      seconds % 60);
+
+		if (vg_counter > 0) {
+			for (v = 0; v < ABS_MAX_VG; v++) {
+				/* volume group */
+				if ((vg_ptr = vg[v]) != NULL) {
+					sz += _vg_info(vg_ptr, LVM_PROC_BUF);
+
+					/* physical volumes */
+					sz += sprintf(LVM_PROC_BUF,
+						      "\n  PV%s ",
+						      vg_ptr->pv_cur == 1 ? ": " : "s:");
+					c = 0;
+					for (p = 0; p < vg_ptr->pv_max; p++) {
+						if ((pv_ptr = vg_ptr->pv[p]) != NULL) {
+							sz += _pv_info(pv_ptr, LVM_PROC_BUF);
+
+							c++;
+							if (c < vg_ptr->pv_cur)
+								sz += sprintf(LVM_PROC_BUF,
+									      "\n       ");
+						}
+					}
+
+					/* logical volumes */
+					sz += sprintf(LVM_PROC_BUF,
+						      "\n    LV%s ",
+						      vg_ptr->lv_cur == 1 ? ": " : "s:");
+					c = 0;
+					for (l = 0; l < vg_ptr->lv_max; l++) {
+						if ((lv_ptr = vg_ptr->lv[l]) != NULL) {
+							sz += _lv_info(vg_ptr, lv_ptr, LVM_PROC_BUF);
+							c++;
+							if (c < vg_ptr->lv_cur)
+								sz += sprintf(LVM_PROC_BUF,
+									      "\n         ");
+						}
+					}
+					if (vg_ptr->lv_cur == 0) sz += sprintf(LVM_PROC_BUF, "none");
+					sz += sprintf(LVM_PROC_BUF, "\n");
+				}
+			}
+		}
+		if (buf == NULL) {
+			lock_kernel();
+			buf = vmalloc(sz);
+			unlock_kernel();
+			if (buf == NULL) {
+				sz = 0;
+				return sprintf(page, "%s - vmalloc error at line %d\n",
+					       lvm_name, __LINE__);
+			}
+		}
+		sz_last = sz;
+	}
+
+ out:
+	if (pos > sz - 1) {
+		lock_kernel();
+		vfree(buf);
+		unlock_kernel();
+		buf = NULL;
+		return 0;
+	}
+	*start = &buf[pos];
+	if (sz - pos < count)
+		return sz - pos;
+	else
+		return count;
+
+#undef LVM_PROC_BUF
+}
+
+/*
+ * provide VG info for proc filesystem use (global)
+ */
+static int _vg_info(vg_t *vg_ptr, char *buf) {
+	int sz = 0;
+	char inactive_flag = ' ';
+
+	if (!(vg_ptr->vg_status & VG_ACTIVE)) inactive_flag = 'I';
+	sz = sprintf(buf,
+		     "\nVG: %c%s  [%d PV, %d LV/%d open] "
+		     " PE Size: %d KB\n"
+		     "  Usage [KB/PE]: %d /%d total  "
+		     "%d /%d used  %d /%d free",
+		     inactive_flag,
+		     vg_ptr->vg_name,
+		     vg_ptr->pv_cur,
+		     vg_ptr->lv_cur,
+		     vg_ptr->lv_open,
+	     	     vg_ptr->pe_size >> 1,
+		     vg_ptr->pe_size * vg_ptr->pe_total >> 1,
+		     vg_ptr->pe_total,
+		     vg_ptr->pe_allocated * vg_ptr->pe_size >> 1,
+	     	     vg_ptr->pe_allocated,
+		     (vg_ptr->pe_total - vg_ptr->pe_allocated) *
+	     	     vg_ptr->pe_size >> 1,
+		     vg_ptr->pe_total - vg_ptr->pe_allocated);
+	return sz;
+}
+
+
+/*
+ * provide LV info for proc filesystem use (global)
+ */
+static int _lv_info(vg_t *vg_ptr, lv_t *lv_ptr, char *buf) {
+	int sz = 0;
+	char inactive_flag = 'A', allocation_flag = ' ',
+		stripes_flag = ' ', rw_flag = ' ', *basename;
+
+	if (!(lv_ptr->lv_status & LV_ACTIVE))
+		inactive_flag = 'I';
+	rw_flag = 'R';
+	if (lv_ptr->lv_access & LV_WRITE)
+		rw_flag = 'W';
+	allocation_flag = 'D';
+	if (lv_ptr->lv_allocation & LV_CONTIGUOUS)
+		allocation_flag = 'C';
+	stripes_flag = 'L';
+	if (lv_ptr->lv_stripes > 1)
+		stripes_flag = 'S';
+	sz += sprintf(buf+sz,
+		      "[%c%c%c%c",
+		      inactive_flag,
+	 rw_flag,
+		      allocation_flag,
+		      stripes_flag);
+	if (lv_ptr->lv_stripes > 1)
+		sz += sprintf(buf+sz, "%-2d",
+			      lv_ptr->lv_stripes);
+	else
+		sz += sprintf(buf+sz, "  ");
+
+	/* FIXME: use _basename */
+	basename = strrchr(lv_ptr->lv_name, '/');
+	if ( basename == 0) basename = lv_ptr->lv_name;
+	else                basename++;
+	sz += sprintf(buf+sz, "] %-25s", basename);
+	if (strlen(basename) > 25)
+		sz += sprintf(buf+sz,
+			      "\n                              ");
+	sz += sprintf(buf+sz, "%9d /%-6d   ",
+		      lv_ptr->lv_size >> 1,
+		      lv_ptr->lv_size / vg_ptr->pe_size);
+
+	if (lv_ptr->lv_open == 0)
+		sz += sprintf(buf+sz, "close");
+	else
+		sz += sprintf(buf+sz, "%dx open",
+			      lv_ptr->lv_open);
+
+	return sz;
+}
+
+
+/*
+ * provide PV info for proc filesystem use (global)
+ */
+static int _pv_info(pv_t *pv, char *buf) {
+	int sz = 0;
+	char inactive_flag = 'A', allocation_flag = ' ';
+	char *pv_name = NULL;
+
+	if (!(pv->pv_status & PV_ACTIVE))
+		inactive_flag = 'I';
+	allocation_flag = 'A';
+	if (!(pv->pv_allocatable & PV_ALLOCATABLE))
+		allocation_flag = 'N';
+	pv_name = strrchr(pv->pv_name+1,'/');
+	if ( pv_name == 0) pv_name = pv->pv_name;
+	else               pv_name++;
+	sz = sprintf(buf,
+		     "[%c%c] %-21s %8d /%-6d  "
+		     "%8d /%-6d  %8d /%-6d",
+		     inactive_flag,
+		     allocation_flag,
+		     pv_name,
+		     pv->pe_total * pv->pe_size >> 1,
+		     pv->pe_total,
+		     pv->pe_allocated * pv->pe_size >> 1,
+		     pv->pe_allocated,
+		     (pv->pe_total - pv->pe_allocated) *
+		     pv->pe_size >> 1,
+		     pv->pe_total - pv->pe_allocated);
+	return sz;
+}
+
+static void _show_uuid(const char *src, char *b, char *e) {
+	int i;
+
+	e--;
+	for(i = 0; *src && (b != e); i++) {
+		if(i && !(i & 0x3))
+			*b++ = '-';
+		*b++ = *src++;
+	}
+	*b = '\0';
+}
--- linux/drivers/md/Makefile.orig	Fri Feb 16 13:56:28 2001
+++ linux/drivers/md/Makefile	Fri Feb 16 13:56:39 2001
@@ -6,7 +6,7 @@
 
 export-objs	:= md.o xor.o
 list-multi	:= lvm-mod.o
-lvm-mod-objs	:= lvm.o lvm-snap.o
+lvm-mod-objs	:= lvm.o lvm-snap.o lvm-fs.o
 
 # Note: link order is important.  All raid personalities
 # and xor.o must come before md.o, as they each initialise 
diff -ruN -X /home/joe/packages/dontdiff linux_2.4.1/drivers/md/lvm-snap.h linux/drivers/md/lvm-snap.h
--- linux_2.4.1/drivers/md/lvm-snap.h	Fri Feb 16 14:51:26 2001
+++ linux/drivers/md/lvm-snap.h	Thu Jan  1 01:00:00 1970
@@ -1,47 +0,0 @@
-/*
- * kernel/lvm-snap.h
- *
- * Copyright (C) 2001 Sistina Software
- *
- *
- * LVM driver 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.
- *
- * LVM driver 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 GNU CC; see the file COPYING.  If not, write to
- * the Free Software Foundation, 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- */
-
-/*
- * Changelog
- *
- *    05/01/2001:Joe Thornber - Factored this file out of lvm.c
- *
- */
-
-#ifndef LVM_SNAP_H
-#define LVM_SNAP_H
-
-/* external snapshot calls */
-extern inline int lvm_get_blksize(kdev_t);
-extern int lvm_snapshot_alloc(lv_t *);
-extern void lvm_snapshot_fill_COW_page(vg_t *, lv_t *);
-extern int lvm_snapshot_COW(kdev_t, ulong, ulong, ulong, lv_t *);
-extern int lvm_snapshot_remap_block(kdev_t *, ulong *, ulong, lv_t *);
-extern void lvm_snapshot_release(lv_t *); 
-extern int lvm_write_COW_table_block(vg_t *, lv_t *);
-extern inline void lvm_hash_link(lv_block_exception_t *, 
-				 kdev_t, ulong, lv_t *);
-extern int lvm_snapshot_alloc_hash_table(lv_t *);
-extern void lvm_drop_snapshot(lv_t *, const char *);
-
-#endif
--- linux/Documentation/ioctl-number.txt.orig	Mon Mar 12 10:16:51 2001
+++ linux/Documentation/ioctl-number.txt	Mon Mar 12 10:17:36 2001
@@ -186,3 +186,6 @@
 0xB1	00-1F	PPPoX			<mailto:mostrows@styx.uwaterloo.ca>
 0xCB	00-1F	CBM serial IEC bus	in development:
 					<mailto:michael.klein@puffin.lb.shuttle.de>
+
+0xFE	00-9F	Logical Volume Manager  <mailto:linux-lvm@sistina.com>
+
