diff -u --recursive --new-file v1.1.34/linux/CREDITS linux/CREDITS
--- v1.1.34/linux/CREDITS	Mon May 23 12:14:21 1994
+++ linux/CREDITS	Sat Jul 23 12:58:36 1994
@@ -53,7 +53,6 @@
 
 N: Hennus Bergman
 E: hennus@sky.nl.mugnet.org  [My uucp-fed Linux box at home]
-E: csg279@wing.rug.nl        [Alternate address]
 D: Author and maintainer of the QIC-02 tape driver
 S: The Netherlands
 
diff -u --recursive --new-file v1.1.34/linux/Makefile linux/Makefile
--- v1.1.34/linux/Makefile	Sat Jul 23 17:38:36 1994
+++ linux/Makefile	Sat Jul 23 13:03:17 1994
@@ -1,6 +1,6 @@
 VERSION = 1
 PATCHLEVEL = 1
-SUBLEVEL = 34
+SUBLEVEL = 35
 
 all:	Version zImage
 
diff -u --recursive --new-file v1.1.34/linux/config.in linux/config.in
--- v1.1.34/linux/config.in	Thu Jul 21 08:18:00 1994
+++ linux/config.in	Sun Jul 24 17:53:38 1994
@@ -150,6 +150,7 @@
 bool 'Second extended fs support' CONFIG_EXT2_FS y
 bool 'xiafs filesystem support' CONFIG_XIA_FS n
 bool 'msdos fs support' CONFIG_MSDOS_FS y
+bool 'umsdos: Unix like fs on top of std MSDOS FAT fs' CONFIG_UMSDOS_FS n
 bool '/proc filesystem support' CONFIG_PROC_FS y
 if [ "$CONFIG_INET" = "y" ]; then
 bool 'NFS filesystem support' CONFIG_NFS_FS y
@@ -169,7 +170,22 @@
 bool 'Microsoft busmouse support' CONFIG_MS_BUSMOUSE n
 bool 'ATIXL busmouse support' CONFIG_ATIXL_BUSMOUSE n
 bool 'Selection (cut and paste for virtual consoles)' CONFIG_SELECTION n
-bool 'QIC-02 tape support' CONFIG_TAPE_QIC02 n
+
+bool 'QIC-02 tape support' CONFIG_QIC02_TAPE n
+if [ "$CONFIG_QIC02_TAPE" = "y" ]; then
+bool 'Do you want runtime configuration for QIC-02' CONFIG_QIC02_DYNCONF y
+if [ "$CONFIG_QIC02_DYNCONF" != "y" ]; then
+
+comment '>>> Edit configuration parameters in ./include/linux/tpqic02.h!'
+
+else
+
+comment '>>> Setting runtime QIC-02 configuration is done with qic02conf'
+comment '>>> Which is available from ftp://ftp.funet.fi/pub/OS/Linux/BETA/QIC-02/'
+
+fi
+fi
+
 bool 'QIC-117 tape support' CONFIG_FTAPE n
 if [ "$CONFIG_FTAPE" = "y" ]; then
 int ' number of ftape buffers' NR_FTAPE_BUFFERS 3
diff -u --recursive --new-file v1.1.34/linux/drivers/char/Makefile linux/drivers/char/Makefile
--- v1.1.34/linux/drivers/char/Makefile	Tue May 24 00:34:46 1994
+++ linux/drivers/char/Makefile	Sat Jul 23 12:58:36 1994
@@ -58,7 +58,7 @@
 SRCS := $(SRCS) psaux.c
 endif
 
-ifdef CONFIG_TAPE_QIC02
+ifdef CONFIG_QIC02_TAPE
 OBJS := $(OBJS) tpqic02.o 
 SRCS := $(SRCS) tpqic02.c
 endif
diff -u --recursive --new-file v1.1.34/linux/drivers/char/console.c linux/drivers/char/console.c
--- v1.1.34/linux/drivers/char/console.c	Fri Jul 22 16:41:01 1994
+++ linux/drivers/char/console.c	Sun Jul 24 17:39:43 1994
@@ -156,7 +156,7 @@
 	/* misc */
 	unsigned long	vc_ques		: 1;
 	unsigned long	vc_need_wrap	: 1;
-	unsigned long	vc_report_mouse : 1;
+	unsigned long	vc_report_mouse : 2;
 	unsigned long	vc_tab_stop[5];		/* Tab stops. 160 columns. */
 	unsigned char * vc_translate;
 	unsigned char *	vc_G0_charset;
@@ -848,12 +848,15 @@
 					clr_kbd(decarm);
 				break;
 			case 9:
-				report_mouse = on_off;
+				report_mouse = on_off ? 1 : 0;
 				break;
 			case 25:		/* Cursor on/off */
 				deccm = on_off;
 				set_cursor(currcons);
 				break;
+			case 1000:
+				report_mouse = on_off ? 2 : 0;
+				break;
 		} else switch(par[i]) {		/* ANSI modes set/reset */
 			case 4:			/* Insert Mode on/off */
 				decim = on_off;
@@ -1858,11 +1861,11 @@
 }
 
 /* invoked via ioctl(TIOCLINUX) */
-int mouse_reporting_p(void)
+int mouse_reporting(void)
 {
 	int currcons = fg_console;
 
-	return ((report_mouse) ? 0 : -EINVAL);
+	return report_mouse;
 }
 
 /* set the current selection. Invoked by ioctl(). */
diff -u --recursive --new-file v1.1.34/linux/drivers/char/mem.c linux/drivers/char/mem.c
--- v1.1.34/linux/drivers/char/mem.c	Thu Jul 21 08:18:05 1994
+++ linux/drivers/char/mem.c	Sat Jul 23 12:58:36 1994
@@ -411,8 +411,8 @@
 #ifdef CONFIG_SOUND
 	mem_start = soundcard_init(mem_start);
 #endif
-#if CONFIG_TAPE_QIC02
-	mem_start = tape_qic02_init(mem_start);
+#if CONFIG_QIC02_TAPE
+	mem_start = qic02_tape_init(mem_start);
 #endif
 /*
  *      Rude way to allocate kernel memory buffer for tape device
diff -u --recursive --new-file v1.1.34/linux/drivers/char/tpqic02.c linux/drivers/char/tpqic02.c
--- v1.1.34/linux/drivers/char/tpqic02.c	Tue May 31 12:48:16 1994
+++ linux/drivers/char/tpqic02.c	Sat Jul 23 12:58:37 1994
@@ -1,9 +1,10 @@
-/* $Id: tpqic02.c,v 0.2.1.21 1993/06/18 19:04:33 root Exp root $
+/* $Id: tpqic02.c,v 0.4.1.4 1994/07/21 02:15:45 root Exp root $
  *
- * Driver for tape drive support for Linux-i386 0.99.12.
+ * Driver for tape drive support for Linux-i386 1.1.30
  *
- * Copyright (c) 1993 by H. H. Bergman. All rights reserved.
- * Current e-mail address: csg279@wing.rug.nl
+ * Copyright (c) 1992, 1993, 1994 by H. H. Bergman. All rights reserved.
+ * Current e-mail address: hennus@sky.nl.mugnet.org [This is a UUCP link.]
+ * Secondary e-mail address: csg279@wing.rug.nl [IP connected, but flaky]
  * [If you are unable to reach me directly, try the TAPE mailing list
  * channel on linux-activists@niksula.hut.fi using "X-Mn-Key: TAPE" as
  * the first line in your message.]
@@ -18,6 +19,13 @@
  * Use this code at your own risk. Don't blame me if it destroys your data!
  * Make sure you have a backup before you try this code.
  *
+ * If you make changes to my code and redistribute it in source or binary
+ * form you must make it clear to even casual users of your code that you
+ * have modified my code, clearly point out what the changes exactly are
+ * (preferably in the form of a context diff file), how to undo your changes,
+ * where the original can be obtained, and that complaints/requests about the
+ * modified code should be directed to you instead of me.
+ *
  * This driver was partially inspired by the 'wt' driver in the 386BSD
  * source distribution, which carries the following copyright notice:
  *
@@ -27,6 +35,46 @@
  * You are not allowed to change this line nor the text above.
  *
  * $Log: tpqic02.c,v $
+ * Revision 0.4.1.4  1994/07/21  02:15:45  root
+ * ifdef'd DDI. Exception masks.
+ *
+ * Revision 0.4.1.3  1994/05/03  01:49:09  root
+ * Initial attempt at Mountain support for the Mountain 7150.
+ * Based on patches provided by Erik Jacobson.
+ *
+ * Revision 0.4.1.2  1994/03/18  21:16:50  root
+ * Many driver messages can now be turned off (runtime selectable).
+ *
+ * Revision 0.4.1.1  1994/02/16  19:47:22  root
+ * First stab at runtime debug-variable.
+ *
+ * Revision 0.4  1994/02/15  01:53:16  root
+ * DYNCONF mark II.
+ * Minor cleanups.
+ *
+ * Revision 0.3  1994/02/07  01:23:16  root
+ * More improved DYNCONF.
+ * Archive changes & some cleanups by Eddy Olk.
+ * Removed status_open, more cleanups, misc other.
+ *
+ * Revision 0.2.1.25  1994/01/24  02:01:33  root
+ * Changed tape_qic02 to QIC02_TAPE.
+ * Changes to prepare for DYNCONF.
+ *
+ * Revision 0.2.1.24  1994/01/23  07:27:18  root
+ * Attempt to remove compilation warnings, G++ bug,
+ * Linus changed TAPE_QIC02 to QIC02_TAPE.
+ *
+ * Revision 0.2.1.23  1994/01/20  23:49:28  root
+ * Changed some exception decoding stuff.
+ * TP_HAVE_SEEK, TP_HAVE_DENS. byte_swap_w() on arg, not global.
+ * Attempt to fix cartridge-changed-problem for 2150L.
+ * Release irq and dma reservations if initial reset fails.
+ *
+ * Revision 0.2.1.22  1994/01/19  20:56:55  root
+ * Speed measuring stuff moved from aperf.h to delay.h.
+ * BogoMips (tm) introduced by Linus.
+ *
  * Revision 0.2.1.21  1993/06/18  19:04:33  root
  * minor fixes for 0.99.10.
  *
@@ -146,7 +194,7 @@
 #include <linux/config.h>
 
 /* skip this driver if not required for this configuration */
-#if CONFIG_TAPE_QIC02
+#if CONFIG_QIC02_TAPE
 
 /*
 #define TDEBUG
@@ -171,29 +219,45 @@
 #include <asm/segment.h>
 
 /* check existence of required configuration parameters */
-#if !defined(TAPE_QIC02_PORT) || \
-    !defined(TAPE_QIC02_IRQ) || \
-    !defined(TAPE_QIC02_DMA)
-#error tape_qic02 configuration error
+#if !defined(QIC02_CMD_PORT) || \
+    !defined(QIC02_TAPE_IRQ) || \
+    !defined(QIC02_TAPE_DMA)
+#error qic02_tape configuration error
 #endif
 
 
-#define TPQIC_NAME	"tpqic02"
+#define TPQIC02_NAME	"tpqic02"
 
 /* Linux outb() commands have (value,port) as parameters.
  * One might expect (port,value) instead, so beware!
  */
 
+#ifdef CONFIG_QIC02_DYNCONF
+/* This may hold the dynamic configuration info for the interface
+ * card+drive info in future versions.
+ */
+struct mtconfiginfo qic02_tape_dynconf = { 0, };	/* user settable */
+struct qic02_ccb qic02_tape_ccb = { 0, };		/* private stuff */
+
+#else
+
+unsigned long qic02_tape_debug;
+
+# if ((QIC02_TAPE_IFC!=WANGTEK) && (QIC02_TAPE_IFC!=ARCHIVE) && (QIC02_TAPE_IFC!=MOUNTAIN))
+#  error No valid interface card specified
+# endif
+#endif
+
 static volatile int ctlbits = 0;     /* control reg bits for tape interface */
 
-static struct wait_queue *tape_qic02_transfer = NULL; /* sync rw with interrupts */
+static struct wait_queue *qic02_tape_transfer = NULL; /* sync rw with interrupts */
 
 static volatile struct mtget ioctl_status;	/* current generic status */
 
 static volatile struct tpstatus tperror;	/* last drive status */
 
-static char rcs_revision[] = "$Revision: 0.2.1.21 $";
-static char rcs_date[] = "$Date: 1993/06/18 19:04:33 $";
+static char rcs_revision[] = "$Revision: 0.4.1.4 $";
+static char rcs_date[] = "$Date: 1994/07/21 02:15:45 $";
 
 /* Flag bits for status and outstanding requests.
  * (Could all be put in one bit-field-struct.)
@@ -201,7 +265,7 @@
  * by an interrupt.
  */
 static volatile flag status_dead = YES;	/* device is legally dead until proven alive */
-static 		flag status_open = NO;	/* in use or not */
+static 		flag status_zombie = YES; /* it's `zombie' until irq/dma allocated */
 
 static volatile flag status_bytes_wr = NO;	/* write FM at close or not */
 static volatile flag status_bytes_rd = NO;	/* (rd|wr) used for rewinding */
@@ -243,10 +307,8 @@
 static flag reported_write_eof = NO;
 
 
-#ifdef TP_HAVE_SEEK
 /* This is for doing `mt seek <blocknr>' */
-static char seek_addr_buf[SEEK_BUF_SIZE];
-#endif
+static char seek_addr_buf[AR_SEEK_BUF_SIZE];
 
 
 /* In write mode, we have to write a File Mark after the last block written, 
@@ -259,8 +321,8 @@
 
 /* This is the actual kernel buffer where the interrupt routines read
  * from/write to. It is needed because the DMA channels 1 and 3 cannot
- * access the user buffers. [The kernel buffer must reside in the lower
- * 1MBytes of system memory because of the DMA controller.]
+ * always access the user buffers. [The kernel buffer must reside in the
+ * lower 16MBytes of system memory because of the DMA controller.]
  * The user must ensure that a large enough buffer is passed to the
  * kernel, in order to reduce tape repositioning.
  *
@@ -268,7 +330,7 @@
  * at 512 bytes, to prevent problems with 64k boundaries.
  */
 
-static volatile char tape_qic02_buf[TPQBUF_SIZE+TAPE_BLKSIZE];
+static volatile char qic02_tape_buf[TPQBUF_SIZE+TAPE_BLKSIZE];
 /* A really good compiler would be able to align this at 512 bytes... :-( */
 
 static unsigned long buffaddr;	/* aligned physical address of buffer */
@@ -295,29 +357,35 @@
  * "Exception Status Summary" in QIC-02 rev F, but some changes
  * were required to make it work with real-world drives.
  *
- * Exception 1 (CNI) is changed to also cover status 0x00e0 (mask USL),
+ * Exception 2 (CNI) is changed to also cover status 0x00e0 (mask USL),
  * Exception 4 (EOM) is changed to also cover status 0x8288 (mask EOR),
  * Exception 11 (FIL) is changed to also cover status 0x0089 (mask EOM).
  * Exception 15 (EOR) is added for seek-to-end-of-data (catch EOR),
  * Exception 16 (BOM) is added for beginning-of-media (catch BOM).
+ *
+ * Had to swap EXC_NDRV and EXC_NCART to ensure that extended EXC_NCART
+ * (because of the incorrect Wangtek status code) doesn't catch the
+ * EXC_NDRV first.
  */
 static struct exception_list_type {
 	unsigned short mask, code;
 	char *msg;
+	/* EXC_nr attribute should match with tpqic02.h */
 } exception_list[] = {
 	{0, 0,
 		"Unknown exception status code",		/* extra: 0 */},
+	{~(0), TP_ST0|TP_CNI|TP_USL|TP_WRP,
+		"Drive not online"				/* 1 */},
+		/* Drive presence goes before cartridge presence. */
 	{~(TP_WRP|TP_USL), TP_ST0|TP_CNI,
 		/* My Wangtek 5150EQ sometimes reports a status code
 		 * of 0x00e0, which is not a valid exception code, but
 		 * I think it should be recognized as "NO CARTRIDGE".
 		 */
-		"Cartridge not in place"			/* 1 */},
-	{-1, TP_ST0|TP_CNI|TP_USL|TP_WRP,
-		"Drive not online"				/* 2 */},
-	{~(TP_ST1|TP_BOM), TP_ST0|TP_WRP,
+		"Cartridge not in place"			/* 2 */},
+	{(unsigned short) ~(TP_ST1|TP_BOM), (TP_ST0|TP_WRP),
 		"Write protected cartridge"			/* 3 */},
-	{~(TP_ST1|TP_EOR), TP_ST0|TP_EOM,
+	{(unsigned short) ~(TP_ST1|TP_EOR), (TP_ST0|TP_EOM),
 		"End of media"					/* 4 */},
 	{~TP_WRP, TP_ST0|TP_UDA| TP_ST1|TP_BOM,
 		"Read or Write abort. Rewind tape."		/* 5 */},
@@ -349,26 +417,20 @@
 		/* 15 is returned when SEEKEOD completes successfully */
 	{~(TP_WRP|TP_ST0), TP_ST1|TP_BOM,
 		"Beginning of media"				/* extra: 16 */}
-#ifdef CYPHER_BUG
-	/* Perhaps the Cypher driver clears the TP_BOM bit after the
-	 * status has been read? The QIC-02 specs explicitly state that
-	 * the BOM bit should remain set as long as the tape is logically
-	 * at the beginning of the tape.
-	 */
-	,{-1, TP_ST1,
-		"Hmm, this must be Cypher drive... Aaargh"	/* extra */}
-#endif
 };
 #define NR_OF_EXC	(sizeof(exception_list)/sizeof(struct exception_list_type))
 
 
 
-static void tpqputs(char *s)
+static void tpqputs(unsigned long flags, char *s)
 {
-	printk(TPQIC_NAME ": %s\n", s);
+	if ((flags & TPQD_ALWAYS) || (flags & QIC02_TAPE_DEBUG))
+		printk(TPQIC02_NAME ": %s\n", s);
 } /* tpqputs */
 
 
+
+
 /* Perform byte order swapping for a 16-bit word.
  *
  * [FIXME] This should probably be in include/asm/
@@ -390,24 +452,27 @@
  */
 static void ifc_init(void)
 {
-#if TAPE_QIC02_IFC == WANGTEK
-	ctlbits = WT_CTL_ONLINE;	/* online */
-	outb_p(ctlbits, QIC_CTL_PORT);
-
-#elif TAPE_QIC02_IFC == ARCHIVE
-	ctlbits = 0;			/* no interrupts yet */
-	outb_p(ctlbits, QIC_CTL_PORT);
-	outb_p(0, AR_RESET_DMA_PORT);	/* dummy write to reset DMA */
-#else
-# error No valid interface card specified
-#endif
+	if (QIC02_TAPE_IFC == WANGTEK) /* || (QIC02_TAPE_IFC == EVEREX) */ {
+		ctlbits = WT_CTL_ONLINE;	/* online */
+		outb_p(ctlbits, QIC02_CTL_PORT);
+
+	} else if (QIC02_TAPE_IFC == ARCHIVE) {
+		ctlbits = 0;			/* no interrupts yet */
+		outb_p(ctlbits, QIC02_CTL_PORT);
+		outb_p(0, AR_RESET_DMA_PORT);	/* dummy write to reset DMA */
+
+	} else /* MOUNTAIN */ {
+		ctlbits = MTN_CTL_ONLINE;       /* online, and logic enabled */
+		outb_p(ctlbits, QIC02_CTL_PORT);
+	}
 } /* ifc_init */
 
 
 static void report_exception(unsigned n)
 {
-	if (n >= NR_OF_EXC) { tpqputs("Oops -- report_exception"); n = 0; }
-	printk(TPQIC_NAME ": sense: %s\n", exception_list[n].msg);
+	if (n >= NR_OF_EXC) { tpqputs(TPQD_ALWAYS, "Oops -- report_exception"); n = 0; }
+	if (TPQDBG(SENSE_TEXT) || n==0)
+		printk(TPQIC02_NAME ": sense: %s\n", exception_list[n].msg);
 } /* report_exception */
 
 
@@ -416,7 +481,7 @@
  * exception table (`exception_list[]').
  * It is assumed that s!=0.
  */
-static int decode_exception_nr(short s)	/* s must be short, because of sign-extension */
+static int decode_exception_nr(unsigned s)
 {
 	int i;
 
@@ -424,7 +489,7 @@
 		if ((s & exception_list[i].mask)==exception_list[i].code)
 			return i;
 	}
-	tpqputs("decode_exception_nr: exception not recognized");
+	printk(TPQIC02_NAME ": decode_exception_nr: exception(%x) not recognized\n", s);
 	return 0;
 } /* decode_exception_nr */
 
@@ -472,7 +537,7 @@
 					else			/* 8: Read error. No data detected. CONTINUABLE */
 						n = 8;
 				} else { /* 7: Read error. Cannot recover block, filler substituted. CONTINUABLE */
-					tpqputs("[Bad block -- filler data transferred.]");
+					tpqputs(TPQD_ALWAYS, "[Bad block -- filler data transferred.]");
 					n = 7;
 				}
 			}
@@ -484,14 +549,14 @@
 					 * This is why some people prefer not to
 					 * use compression on backups...
 					 */
-					tpqputs("[CRC failed!]");
+					tpqputs(TPQD_ALWAYS, "[CRC failed!]");
 					n = 6;
 				}
 			}
 		}
 		else if (s & TP_FIL) {
 			if (s & TP_MBD) {	/* 14: Marginal block detected. CONTINUABLE */
-				tpqputs("[Marginal block]");
+				tpqputs(TPQD_ALWAYS, "[Marginal block]");
 				n = 14;
 			} else			/* 11: File mark detected. CONTINUABLE */
 				n = 11;
@@ -519,14 +584,14 @@
 		status_eom_detected = NO;
 	}
 	else if (exnr==EXC_XFILLER)
-		tpqputs("[Bad block -- filler data transferred.]");
+		tpqputs(TPQD_ALWAYS, "[Bad block -- filler data transferred.]");
 	else if (exnr==EXC_XBAD)
-		tpqputs("[CRC failed!]");
+		tpqputs(TPQD_ALWAYS, "[CRC failed!]");
 	else if (exnr==EXC_MARGINAL) {
 		/* A marginal block behaves much like a FM.
 		 * User may continue reading, if desired.
 		 */
-		tpqputs("[Marginal block]");
+		tpqputs(TPQD_ALWAYS, "[Marginal block]");
 		doing_read = NO;
 	} else if (exnr==EXC_FM)
 		doing_read = NO;
@@ -535,7 +600,7 @@
 
 static inline int is_exception(void)
 {
-	return (inb(QIC_STAT_PORT) & QIC_STAT_EXCEPTION) == 0;
+	return (inb(QIC02_STAT_PORT) & QIC02_STAT_EXCEPTION) == 0;
 } /* is_exception */
 
 
@@ -547,7 +612,11 @@
 {
 	ifc_init();				/* reset interface card */
 
-	outb_p(ctlbits | QIC_CTL_RESET, QIC_CTL_PORT);	/* assert reset */
+	/* assert reset */
+	if (QIC02_TAPE_IFC == MOUNTAIN)
+		outb_p(ctlbits & ~MTN_QIC02_CTL_RESET_NOT, QIC02_CTL_PORT);
+	else /* WANGTEK, ARCHIVE */
+ 		outb_p(ctlbits | QIC02_CTL_RESET, QIC02_CTL_PORT);
 
 	/* Next, we need to wait >=25 usec. */
 	udelay(30);
@@ -560,17 +629,22 @@
 	doing_read = doing_write = NO;
 	ioctl_status.mt_fileno = ioctl_status.mt_blkno = 0;
 
-	outb_p(ctlbits & ~QIC_CTL_RESET, QIC_CTL_PORT);	/* de-assert reset */
+	/* de-assert reset */
+	if (QIC02_TAPE_IFC == MOUNTAIN)
+		outb_p(ctlbits | MTN_QIC02_CTL_RESET_NOT, QIC02_CTL_PORT);
+	else
+		outb_p(ctlbits & ~QIC02_CTL_RESET, QIC02_CTL_PORT);
+
 	/* KLUDGE FOR G++ BUG */
-	{ int stat = inb_p(QIC_STAT_PORT);
-	  status_dead = ((stat & QIC_STAT_RESETMASK) != QIC_STAT_RESETVAL); }
+	{ int stat = inb_p(QIC02_STAT_PORT);
+	  status_dead = ((stat & QIC02_STAT_RESETMASK) != QIC02_STAT_RESETVAL); }
 	/* if successful, inb(STAT) returned RESETVAL */
-	if (status_dead)
-		printk(TPQIC_NAME ": reset failed!\n");
+	if (status_dead == YES)
+		printk(TPQIC02_NAME ": reset failed!\n");
 	else if (verbose)
-		printk(TPQIC_NAME ": reset successful\n");
+		printk(TPQIC02_NAME ": reset successful\n");
 
-	return (status_dead)? TE_DEAD : TE_OK;
+	return (status_dead == YES)? TE_DEAD : TE_OK;
 } /* tape_reset */
 
 
@@ -578,7 +652,7 @@
 /* Notify tape drive of a new command. It only waits for the
  * command to be accepted, not for the actual command to complete.
  *
- * Before calling this routine, QIC_CMD_PORT must have been loaded
+ * Before calling this routine, QIC02_CMD_PORT must have been loaded
  * with the command to be executed.
  * After this routine, the exception bit must be checked.
  * This routine is also used by rdstatus(), so in that case, any exception
@@ -588,43 +662,43 @@
 {
 	int i;
 
-	outb_p(cmd, QIC_CMD_PORT);    /* output the command */
+	outb_p(cmd, QIC02_CMD_PORT);    /* output the command */
 
 	/* wait 1 usec before asserting /REQUEST */
 	udelay(1);
 
 	if ((!ignore_ex) && is_exception()) {
-		tpqputs("*** exception detected in notify_cmd");
+		tpqputs(TPQD_ALWAYS, "*** exception detected in notify_cmd");
 		/** force a reset here **/
 		if (tape_reset(1)==TE_DEAD)
 			return TE_DEAD;
 		if (is_exception()) {
-			tpqputs("exception persists after reset.");
-			tpqputs(" ^ exception ignored.");
+			tpqputs(TPQD_ALWAYS, "exception persists after reset.");
+			tpqputs(TPQD_ALWAYS, " ^ exception ignored.");
 		}
 	}
 
-	outb_p(ctlbits | QIC_CTL_REQUEST, QIC_CTL_PORT);  /* set request bit */
+	outb_p(ctlbits | QIC02_CTL_REQUEST, QIC02_CTL_PORT);  /* set request bit */
 	i = TAPE_NOTIFY_TIMEOUT;
 	/* The specs say this takes about 500 usec, but there is no upper limit!
 	 * If the drive were busy retensioning or something like that,
 	 * it could be *much* longer!
 	 */
-	while ((inb_p(QIC_STAT_PORT) & QIC_STAT_READY) && (--i>0))
+	while ((inb_p(QIC02_STAT_PORT) & QIC02_STAT_READY) && (--i>0))
 		/*skip*/;			  /* wait for ready */
 	if (i==0) {
-		tpqputs("timed out waiting for ready in notify_cmd");
+		tpqputs(TPQD_ALWAYS, "timed out waiting for ready in notify_cmd");
 		status_dead = YES;
 		return TE_TIM;
 	}
 
-	outb_p(ctlbits & ~QIC_CTL_REQUEST, QIC_CTL_PORT); /* reset request bit */
+	outb_p(ctlbits & ~QIC02_CTL_REQUEST, QIC02_CTL_PORT); /* reset request bit */
 	i = TAPE_NOTIFY_TIMEOUT;
 	/* according to the specs this one should never time-out */
-	while (((inb_p(QIC_STAT_PORT) & QIC_STAT_READY) == 0) && (--i>0))
+	while (((inb_p(QIC02_STAT_PORT) & QIC02_STAT_READY) == 0) && (--i>0))
 		/*skip*/;			  /* wait for not ready */
 	if (i==0) {
-		tpqputs("timed out waiting for !ready in notify_cmd");
+		tpqputs(TPQD_ALWAYS, "timed out waiting for !ready in notify_cmd");
 		status_dead = YES;
 		return TE_TIM;
 	}
@@ -647,9 +721,9 @@
 	 * First, busy wait a few usec:
 	 */
 	spin_t = 50;
-	while (((stat = inb_p(QIC_STAT_PORT) & QIC_STAT_MASK) == QIC_STAT_MASK) && (--spin_t>0))
+	while (((stat = inb_p(QIC02_STAT_PORT) & QIC02_STAT_MASK) == QIC02_STAT_MASK) && (--spin_t>0))
 		/*SKIP*/;
-	if ((stat & QIC_STAT_READY) == 0)
+	if ((stat & QIC02_STAT_READY) == 0)
 		return TE_OK;			/* covers 99.99% of all calls */
 
 	/* Then use schedule() a few times */
@@ -659,9 +733,9 @@
 	timeout -= spin_t;
 	spin_t += jiffies;
 
-	while (((stat = inb_p(QIC_STAT_PORT) & QIC_STAT_MASK) == QIC_STAT_MASK) && (jiffies<spin_t))
+	while (((stat = inb_p(QIC02_STAT_PORT) & QIC02_STAT_MASK) == QIC02_STAT_MASK) && (jiffies<spin_t))
 		schedule();		/* don't waste all the CPU time */
-	if ((stat & QIC_STAT_READY) == 0)
+	if ((stat & QIC02_STAT_READY) == 0)
 		return TE_OK;
 
 	/* If we reach this point, we probably need to wait much longer, or
@@ -674,7 +748,7 @@
 	TPQDEB({printk("wait_for_ready: additional timeout: %d\n", spin_t);})
 
 		/* not ready and no exception && timeout not expired yet */
-	while (((stat = inb_p(QIC_STAT_PORT) & QIC_STAT_MASK) == QIC_STAT_MASK) && (jiffies<spin_t)) {
+	while (((stat = inb_p(QIC02_STAT_PORT) & QIC02_STAT_MASK) == QIC02_STAT_MASK) && (jiffies<spin_t)) {
 		/* be `nice` to other processes on long operations... */
 		current->timeout = jiffies + 30;	/* nap 0.30 sec between checks, */
 		current->state = TASK_INTERRUPTIBLE;
@@ -682,13 +756,13 @@
 	}
 
 	/* don't use jiffies for this test because it may have changed by now */
-	if ((stat & QIC_STAT_MASK) == QIC_STAT_MASK) {
-		tpqputs("wait_for_ready() timed out");
+	if ((stat & QIC02_STAT_MASK) == QIC02_STAT_MASK) {
+		tpqputs(TPQD_ALWAYS, "wait_for_ready() timed out");
 		return TE_TIM;
 	}
 
-	if ((stat & QIC_STAT_EXCEPTION) == 0) {
-		tpqputs("exception detected after waiting_for_ready");
+	if ((stat & QIC02_STAT_EXCEPTION) == 0) {
+		tpqputs(TPQD_ALWAYS, "exception detected after waiting_for_ready");
 		return TE_EX;
 	} else {
 		return TE_OK;
@@ -696,6 +770,7 @@
 } /* wait_for_ready */
 
 
+
 /* Send some data to the drive */
 static int send_qic02_data(char sb[], unsigned size, int ignore_ex)
 {
@@ -727,13 +802,13 @@
 {
 	int stat;
 
-	stat = inb_p(QIC_STAT_PORT);
-	if ((stat & QIC_STAT_EXCEPTION) == 0) {	/* if exception */
-		tpqputs("send_qic02_cmd: Exception!");
+	stat = inb_p(QIC02_STAT_PORT);
+	if ((stat & QIC02_STAT_EXCEPTION) == 0) {	/* if exception */
+		tpqputs(TPQD_ALWAYS, "send_qic02_cmd: Exception!");
 		return TE_EX;
 	}
-	if (stat & QIC_STAT_READY) {			/* if not ready */
-		tpqputs("send_qic02_cmd: not Ready!");
+	if (stat & QIC02_STAT_READY) {			/* if not ready */
+		tpqputs(TPQD_ALWAYS, "send_qic02_cmd: not Ready!");
 		return TE_ERR;
 	}
 
@@ -746,12 +821,13 @@
 
 	stat = notify_cmd(cmd, ignore_ex); /* tell drive new command was loaded, */
 					   /* inherit exception check. */
-	if (cmd == QCMDV_SEEK_BLK) {
+	if (TP_HAVE_SEEK && (cmd == AR_QCMDV_SEEK_BLK)) {
 		/* This one needs to send 3 more bytes, MSB first */
 		stat = send_qic02_data(seek_addr_buf, sizeof(seek_addr_buf), ignore_ex);
 	}
+
 	if (stat != TE_OK) {
-		tpqputs("send_qic02_cmd failed");
+		tpqputs(TPQD_ALWAYS, "send_qic02_cmd failed");
 	}
 	return stat;
 } /* send_qic02_cmd */
@@ -775,17 +851,17 @@
 	 * task switch is much longer than we usually have to wait here.
 	 */
 	n = 1000;	/* 500 is not enough on a 486/33 */
-	while ((n>0) && ((inb_p(QIC_STAT_PORT) & QIC_STAT_MASK) == QIC_STAT_MASK))
+	while ((n>0) && ((inb_p(QIC02_STAT_PORT) & QIC02_STAT_MASK) == QIC02_STAT_MASK))
 		n--;  /* wait for ready or exception or timeout */
 	if (n==0) {
 		/* n (above) should be chosen such that on your machine
 		 * you rarely ever see the message below, and it should
 		 * be small enough to give reasonable response time.]
 		 */
-		tpqputs("waiting looong in rdstatus() -- drive dead?");
-		while ((inb_p(QIC_STAT_PORT) & QIC_STAT_MASK) == QIC_STAT_MASK)
+		tpqputs(TPQD_ALWAYS, "waiting looong in rdstatus() -- drive dead?");
+		while ((inb_p(QIC02_STAT_PORT) & QIC02_STAT_MASK) == QIC02_STAT_MASK)
 			schedule();
-		tpqputs("finished waiting in rdstatus()");
+		tpqputs(TPQD_ALWAYS, "finished waiting in rdstatus()");
 	}
 
 	(void) notify_cmd(qcmd, 1);			/* send read status command */
@@ -794,38 +870,38 @@
 	 */
 
 	if (TP_DIAGS(current_tape_dev))
-		printk(TPQIC_NAME ": reading status bytes: ");
+		printk(TPQIC02_NAME ": reading status bytes: ");
 
 	for (q=stp; q<stp+size; q++)
 	{
-		do s = inb_p(QIC_STAT_PORT);
-		while ((s & QIC_STAT_MASK) == QIC_STAT_MASK);	/* wait for ready or exception */
+		do s = inb_p(QIC02_STAT_PORT);
+		while ((s & QIC02_STAT_MASK) == QIC02_STAT_MASK);	/* wait for ready or exception */
 
-		if ((s & QIC_STAT_EXCEPTION) == 0) {		/* if exception */
-			tpqputs("rdstatus: exception error");
+		if ((s & QIC02_STAT_EXCEPTION) == 0) {		/* if exception */
+			tpqputs(TPQD_ALWAYS, "rdstatus: exception error");
 			ioctl_status.mt_erreg = 0;		/* dunno... */
 			return TE_NS;				/* error, shouldn't happen... */
 		}
 
-		*q = inb_p(QIC_DATA_PORT);			/* read status byte */
+		*q = inb_p(QIC02_DATA_PORT);			/* read status byte */
 
 		if (TP_DIAGS(current_tape_dev))
 			printk("[%1d]=0x%x  ", q-stp, (unsigned) (*q) & 0xff);
 
-		outb_p(ctlbits | QIC_CTL_REQUEST, QIC_CTL_PORT);	/* set request */
+		outb_p(ctlbits | QIC02_CTL_REQUEST, QIC02_CTL_PORT);	/* set request */
 
-		while ((inb_p(QIC_STAT_PORT) & QIC_STAT_READY) == 0);	/* wait for not ready */
+		while ((inb_p(QIC02_STAT_PORT) & QIC02_STAT_READY) == 0);	/* wait for not ready */
 
 		udelay(22);	/* delay >20 usec */
 
-		outb_p(ctlbits & ~QIC_CTL_REQUEST, QIC_CTL_PORT);	/* un-set request */
+		outb_p(ctlbits & ~QIC02_CTL_REQUEST, QIC02_CTL_PORT);	/* un-set request */
 
 	}
 
 	/* Specs say we should wait for READY here.
 	 * My drive doesn't seem to need it here yet, but others do?
 	 */
-	while (inb_p(QIC_STAT_PORT) & QIC_STAT_READY)
+	while (inb_p(QIC02_STAT_PORT) & QIC02_STAT_READY)
 		/*skip*/;			  /* wait for ready */
 
 	if (TP_DIAGS(current_tape_dev))
@@ -840,12 +916,12 @@
  * The `.dec' and `.urc' fields are in MSB-first byte-order,
  * so they have to be swapped first.
  */
-static int get_status(char *stp)
+static int get_status(volatile struct tpstatus *stp)
 {
-	int stat = rdstatus(stp, TPSTATSIZE, QCMD_RD_STAT);
+	int stat = rdstatus((char *) stp, TPSTATSIZE, QCMD_RD_STAT);
 #if defined(i386) || defined(i486)
-	byte_swap_w(&tperror.dec);
-	byte_swap_w(&tperror.urc);
+	byte_swap_w(&(stp->dec));
+	byte_swap_w(&(stp->urc));
 #else
 	/* should probably swap status bytes #definition */
 #endif
@@ -866,15 +942,15 @@
 	char vus[64];	/* vendor unique status */
 	int stat, i;
 
-	tpqputs("Attempting to read Extended Status 3...");
+	tpqputs(TPQD_ALWAYS, "Attempting to read Extended Status 3...");
 	stat = rdstatus(vus, sizeof(vus), QCMD_RD_STAT_X3);
 	if (stat != TE_OK)
 		return stat;
 
-	tpqputs("Returned status bytes:");
+	tpqputs(TPQD_ALWAYS, "Returned status bytes:");
 	for (i=0; i<sizeof(vus); i++) {
 		if ( i % 8 == 0 )
-			printk("\n" TPQIC_NAME ": %2d:");
+			printk("\n" TPQIC02_NAME ": %2d:");
 		printk(" %2x", vus[i] & 0xff);
 	}
 	printk("\n");
@@ -892,26 +968,27 @@
 	unsigned err = 0, exnr = 0, gs = 0;
 	static void finish_rw(int cmd);
 
-	printk(TPQIC_NAME ": tp_sense(ignore=0x%x) enter\n", ignore);
+	if (TPQDBG(SENSE_TEXT))
+		printk(TPQIC02_NAME ": tp_sense(ignore=0x%x) enter\n", ignore);
 
 	/* sense() is not allowed during a read or write cycle */
 	if (doing_write == YES)
-		tpqputs("Warning: File Mark inserted because of sense() request");
+		tpqputs(TPQD_ALWAYS, "Warning: File Mark inserted because of sense() request");
 	/* The extra test is to avoid calling finish_rw during booting */
 	if ((doing_read!=NO) || (doing_write!=NO))
 		finish_rw(QCMD_RD_STAT);
 
-	if (get_status((char *) &tperror) != TE_OK) {
-		tpqputs("tp_sense: could not read tape drive status");
+	if (get_status(&tperror) != TE_OK) {
+		tpqputs(TPQD_ALWAYS, "tp_sense: could not read tape drive status");
 		return TE_ERR;
 	}
 
 	err = tperror.exs;	/* get exception status bits */
 	if (err & (TP_ST0|TP_ST1))
-		printk(TPQIC_NAME ": tp_sense: status: %x, error count: %d, underruns: %d\n",
+		printk(TPQIC02_NAME ": tp_sense: status: %x, error count: %d, underruns: %d\n",
 			tperror.exs, tperror.dec, tperror.urc);
-	else
-		printk(TPQIC_NAME ": tp_sense: no errors at all, soft error count: %d, underruns: %d\n",
+	else if ((tperror.dec!=0) || (tperror.urc!=0) || TPQDBG(SENSE_CNTS))
+		printk(TPQIC02_NAME ": tp_sense: no hard errors, soft error count: %d, underruns: %d\n",
 			tperror.dec, tperror.urc);
 
 	/* Set generic status. HP-UX defines these, but some extra would 
@@ -951,7 +1028,8 @@
 	ioctl_status.mt_dsreg = tperror.exs;	/* "drive status" */
 	ioctl_status.mt_erreg = tperror.dec;	/* "sense key error" */
 
-	if (err!=0) {
+	if (err & (TP_ST0|TP_ST1)) {
+		/* My Wangtek occasionally reports `status' 1212 which should be ignored. */
 		exnr = decode_exception_nr(err);
 		handle_exception(exnr, err);		/* update driver state wrt drive status */
 		report_exception(exnr);
@@ -972,13 +1050,14 @@
 {
 	int stat;
 
-	stat = inb(QIC_STAT_PORT) & QIC_STAT_MASK;
-	printk(TPQIC_NAME ": Waiting for (re-)wind to finish: stat=0x%x\n", stat);
+	stat = inb(QIC02_STAT_PORT) & QIC02_STAT_MASK;
+	if (TPQDBG(REWIND))
+		printk(TPQIC02_NAME ": Waiting for (re-)wind to finish: stat=0x%x\n", stat);
 
 	stat = wait_for_ready(timeout);
 
 	if (stat != TE_OK) {
-			tpqputs("(re-) winding failed\n");
+			tpqputs(TPQD_ALWAYS, "(re-) winding failed\n");
 	}
 	return stat;
 } /* wait_for_rewind */
@@ -995,8 +1074,8 @@
 {
 	int stat;
 
-	if (status_dead) {
-		tpqputs("Drive is dead. Do a `mt reset`.");
+	if (status_dead == YES) {
+		tpqputs(TPQD_ALWAYS, "Drive is dead. Do a `mt reset`.");
 		return -ENXIO;			/* User should do an MTRESET. */
 	}
 
@@ -1008,14 +1087,14 @@
 		stat = TE_OK;
 	}
 	if (stat != TE_OK) {
-		printk(TPQIC_NAME ": ll_do_qic_cmd(%x, %ld) failed\n", cmd, timeout);
+		printk(TPQIC02_NAME ": ll_do_qic_cmd(%x, %ld) failed\n", cmd, (long) timeout);
 		return -EIO;
 	}
 
 
 #if OBSOLETE
 	/* wait for ready since it may not be active immediately after reading status */
-	while ((inb_p(QIC_STAT_PORT) & QIC_STAT_READY) != 0);
+	while ((inb_p(QIC02_STAT_PORT) & QIC02_STAT_READY) != 0);
 #endif
 
 	stat = send_qic02_cmd(cmd, timeout, 0);	/* (checks for exceptions) */
@@ -1044,14 +1123,14 @@
 	/* sense() will set eof/eom as required */
 	if (stat==TE_EX) {
 		if (tp_sense(TP_WRP|TP_BOM|TP_EOM|TP_FIL)!=TE_OK) {
-			printk(TPQIC_NAME ": Exception persist in ll_do_qic_cmd[1](%x, %ld)", cmd, timeout);
+			printk(TPQIC02_NAME ": Exception persist in ll_do_qic_cmd[1](%x, %ld)", cmd, (long) timeout);
 			status_dead = YES;
 			return -ENXIO;
 			/* if rdstatus fails too, we're in trouble */
 		}
 	}
 	else if (stat!=TE_OK) {
-		printk(TPQIC_NAME ": ll_do_qic_cmd: send_qic02_cmd failed, stat = 0x%x\n", stat);
+		printk(TPQIC02_NAME ": ll_do_qic_cmd: send_qic02_cmd failed, stat = 0x%x\n", stat);
 		return -EIO;	/*** -EIO is probably not always appropriate */
 	}
 
@@ -1065,7 +1144,7 @@
 		if (tp_sense((cmd==QCMD_SEEK_EOD ?		/*****************************/
 		      TP_EOR|TP_NDT|TP_UDA|TP_BNL|TP_WRP|TP_BOM|TP_EOM|TP_FIL :
 		      TP_WRP|TP_BOM|TP_EOM|TP_FIL))!=TE_OK) {
-			printk(TPQIC_NAME ": Exception persist in ll_do_qic_cmd[2](%x, %ld)\n", cmd, timeout);
+			printk(TPQIC02_NAME ": Exception persist in ll_do_qic_cmd[2](%x, %ld)\n", cmd, (long) timeout);
 			if (cmd!=QCMD_RD_FM)
 				status_dead = YES;
 			return -ENXIO;
@@ -1073,7 +1152,7 @@
 		}
 	}
 	else if (stat!=TE_OK) {
-		printk(TPQIC_NAME ": ll_do_qic_cmd %x: wait failed, stat == 0x%x\n", cmd, stat);
+		printk(TPQIC02_NAME ": ll_do_qic_cmd %x: wait failed, stat == 0x%x\n", cmd, stat);
 		return -EIO;
 	}
 	return 0;
@@ -1113,12 +1192,19 @@
 			/* if the command is a RFM, there is no need to do this
 			 * because a RFM will legally terminate the read-cycle.
 			 */
-			tpqputs("terminating pending read-cycle");
+			tpqputs(TPQD_ALWAYS, "terminating pending read-cycle");
+
+			/* I'm not too sure about this part  -- hhb */
+			if (QIC02_TAPE_IFC == MOUNTAIN) {
+				/* Mountain reference says can terminate by de-asserting online */
+				ctlbits &= ~MTN_QIC02_CTL_ONLINE;
+			}
+ 
 			if (tp_sense(TP_FIL|TP_EOM|TP_WRP) != TE_OK) {
-				tpqputs("finish_rw[read1]: ignore the 2 lines above");
+				tpqputs(TPQD_ALWAYS, "finish_rw[read1]: ignore the 2 lines above");
 				if (is_exception()) {
 					if (tp_sense(TP_ILL|TP_FIL|TP_EOM|TP_WRP) != TE_OK)
-						tpqputs("finish_rw[read2]: read cycle error");
+						tpqputs(TPQD_ALWAYS, "finish_rw[read2]: read cycle error");
 				}
 			}
 		}
@@ -1137,7 +1223,7 @@
 			/* finish off write cycle */
 			stat = ll_do_qic_cmd(QCMD_WRT_FM, TIM_M);
 			if (stat != TE_OK)
-				tpqputs("Couldn't finish write cycle properly");
+				tpqputs(TPQD_ALWAYS, "Couldn't finish write cycle properly");
 			(void) tp_sense(0);
 		}
 		/* If there is an EOF token waiting to be returned to
@@ -1153,7 +1239,7 @@
 static void finish_rw(int cmd)
 {
 	if (wait_for_ready(TIM_S) != TE_OK) {
-		tpqputs("error: drive not ready in finish_rw() !");
+		tpqputs(TPQD_ALWAYS, "error: drive not ready in finish_rw() !");
 		return;
 	}
 	terminate_read(cmd);
@@ -1173,10 +1259,10 @@
 	finish_rw(cmd);
 
 	if (need_rewind) {
-		tpqputs("Rewinding tape...");
+		tpqputs(TPQD_REWIND, "Rewinding tape...");
 		stat = ll_do_qic_cmd(QCMD_REWIND, TIM_R);
 		if (stat != 0) {
-			printk(TPQIC_NAME ": rewind failed in do_qic_cmd(). stat=0x%2x", stat);
+			printk(TPQIC02_NAME ": rewind failed in do_qic_cmd(). stat=0x%2x", stat);
 			return stat;
 		}
 		need_rewind = NO;
@@ -1208,53 +1294,52 @@
 			return (tape_reset(1)==TE_OK)? 0 : -EIO;
 
 		case MTFSF:
-			tpqputs("MTFSF forward searching filemark");
+			tpqputs(TPQD_IOCTLS, "MTFSF forward searching filemark");
 			if ((mode_access==WRITE) && status_bytes_wr)
 				return -EACCES;
 			return do_qic_cmd(QCMD_RD_FM, TIM_F);
 
 		case MTBSF:
-#ifdef TP_HAVE_BSF
-			tpqputs("MTBSF backward searching filemark -- optional command");
-			if ((mode_access==WRITE) && status_bytes_wr)
-				return -EACCES;
-			stat = do_qic_cmd(QCMD_RD_FM_BCK, TIM_F);
-#else
-			tpqputs("MTBSF not supported");
-			stat = -ENXIO;
-#endif
+			if (TP_HAVE_BSF) {
+				tpqputs(TPQD_IOCTLS, "MTBSF backward searching filemark -- optional command");
+				if ((mode_access==WRITE) && status_bytes_wr)
+					return -EACCES;
+				stat = do_qic_cmd(QCMD_RD_FM_BCK, TIM_F);
+			} else {
+				stat = -ENXIO;
+			}
 			status_eom_detected = status_eof_detected = NO;
 			return stat;
 
 		case MTFSR:
-#ifdef TP_HAVE_FSR	/* This is an optional QIC-02 command */
-			tpqputs("MTFSR forward space record");
-			if ((mode_access==WRITE) && status_bytes_wr)
-				return -EACCES;
-			stat = do_qic_cmd(QCMD_SPACE_FWD, TIM_F);
-#else
-			/**** fake it by doing a read data block command? ******/
-			tpqputs("MTFSR not supported");
-			stat = -ENXIO;
-#endif
+			if (TP_HAVE_FSR) { /* This is an optional QIC-02 command */
+				tpqputs(TPQD_IOCTLS, "MTFSR forward space record");
+				if ((mode_access==WRITE) && status_bytes_wr)
+					return -EACCES;
+				stat = do_qic_cmd(QCMD_SPACE_FWD, TIM_F);
+			} else {
+				/**** fake it by doing a read data block command? ******/
+				tpqputs(TPQD_IOCTLS, "MTFSR not supported");
+				stat = -ENXIO;
+			}
 			return stat;
 
 		case MTBSR:
-#ifdef TP_HAVE_BSR	/* This is an optional QIC-02 command */
-			/* we need this for appending files with GNU tar!! */
-			tpqputs("MTFSR backward space record");
-			if ((mode_access==WRITE) && status_bytes_wr)
-				return -EACCES;
-			stat = do_qic_cmd(QCMD_SPACE_BCK, TIM_F);
-#else
-			tpqputs("MTBSR not supported");
-			stat = -ENXIO;
-#endif
+			if (TP_HAVE_BSR) { /* This is an optional QIC-02 command */
+				/* we need this for appending files with GNU tar!! */
+				tpqputs(TPQD_IOCTLS, "MTFSR backward space record");
+				if ((mode_access==WRITE) && status_bytes_wr)
+					return -EACCES;
+				stat = do_qic_cmd(QCMD_SPACE_BCK, TIM_F);
+			} else {
+				tpqputs(TPQD_IOCTLS, "MTBSR not supported");
+				stat = -ENXIO;
+			}
 			status_eom_detected = status_eof_detected = NO;
 			return stat;
 
 		case MTWEOF:
-			tpqputs("MTWEOF write eof mark");
+			tpqputs(TPQD_IOCTLS, "MTWEOF write eof mark");
 			/* Plain GNU mt(1) 2.2 uses read-only mode for writing FM. :-( */
 			if (mode_access==READ)
 				return -EACCES;
@@ -1266,14 +1351,14 @@
 			/* not sure what to do with status_bytes when WFM should fail */
 
 		case MTREW:
-			tpqputs("MTREW rewinding tape");
+			tpqputs(TPQD_IOCTLS, "MTREW rewinding tape");
 			if ((mode_access==WRITE) && status_bytes_wr)
 				return -EACCES;
 			status_eom_detected = status_eof_detected = NO;
 			return do_qic_cmd(QCMD_REWIND, TIM_R);
 
 		case MTOFFL:
-			tpqputs("MTOFFL rewinding & going offline"); /*---*/
+			tpqputs(TPQD_IOCTLS, "MTOFFL rewinding & going offline"); /*---*/
 			/******* What exactly are we supposed to do, to take it offline????
 			 *****/
 			/* Doing a drive select will clear (unlock) the current drive.
@@ -1287,12 +1372,12 @@
 			return stat;
 
 		case MTNOP:
-			tpqputs("MTNOP setting status only");
+			tpqputs(TPQD_IOCTLS, "MTNOP setting status only");
 			/********** should do `read position' for drives that support it **********/
 			return (tp_sense(-1)==TE_OK)? 0 : -EIO;	/**** check return codes ****/
 
 		case MTRETEN:
-			tpqputs("MTRETEN retension tape");
+			tpqputs(TPQD_IOCTLS, "MTRETEN retension tape");
 			if ((mode_access==WRITE) && status_bytes_wr)
 				return -EACCES;
 			status_eom_detected = status_eof_detected = NO;
@@ -1303,7 +1388,7 @@
 			 * we shouldn't skip the FM. Tricky.
 			 * Maybe use RD_FM_BCK, then do a SPACE_FWD?
 			 */
-			tpqputs("MTBSFM not supported");
+			tpqputs(TPQD_IOCTLS, "MTBSFM not supported");
 			if ((mode_access==WRITE) && status_bytes_wr)
 				return -EACCES;
 			return -ENXIO;
@@ -1316,7 +1401,7 @@
 			 * Maybe use RD_FM, then RD_FM_BCK, but not all
 			 * drives will support that!
 			 */
-			tpqputs("MTFSFM not supported");
+			tpqputs(TPQD_IOCTLS, "MTFSFM not supported");
 			if ((mode_access==WRITE) && status_bytes_wr)
 				return -EACCES;
 			return -ENXIO;
@@ -1326,96 +1411,92 @@
 			 * another file to the end, such that it would append
 			 * after the last FM on tape.
 			 */
-			tpqputs("MTEOM search for End Of recorded Media");
+			tpqputs(TPQD_IOCTLS, "MTEOM search for End Of recorded Media");
 			if ((mode_access==WRITE) && status_bytes_wr)
 				return -EACCES;
-#ifdef TP_HAVE_EOD
-			/* Use faster seeking when possible.
-			 * This requires the absence of data beyond the EOM.
-			 */
-# if TAPE_QIC02_DRIVE == MT_ISWT5150
-			/* It seems that my drive does not always perform the
-			 * SEEK_EOD correctly, unless it is preceded by a
-			 * rewind command.
-			 */
+			if (TP_HAVE_EOD) {
+				/* Use faster seeking when possible.
+				 * This requires the absence of data beyond the EOM.
+				 * It seems that my drive does not always perform the
+				 * SEEK_EOD correctly, unless it is preceded by a
+				 * rewind command.
+				 */
 # if 0
-			status_eom_detected = status_eof_detected = NO;
-# endif
-			stat = do_qic_cmd(QCMD_REWIND, TIM_R);
-			if (stat)
-				return stat;
-			
+				status_eom_detected = status_eof_detected = NO;
 # endif
-			stat = do_qic_cmd(QCMD_SEEK_EOD, TIM_F);
-			/* After a successful seek, TP_EOR should be returned */
-#else
-			/* else just seek until the drive returns exception "No Data" */
-			stat = 0;
-			while ((stat==0) && (!status_eom_detected)) {
-				stat = do_qic_cmd(QCMD_RD_FM, TIM_F); /***** should use MTFSFM here???? ******/
+				stat = do_qic_cmd(QCMD_REWIND, TIM_R);
+				if (stat)
+					return stat;
+				stat = do_qic_cmd(QCMD_SEEK_EOD, TIM_F);
+				/* After a successful seek, TP_EOR should be returned */
+			} else {
+				/* else just seek until the drive returns exception "No Data" */
+				stat = 0;
+				while ((stat==0) && (!status_eom_detected)) {
+					stat = do_qic_cmd(QCMD_RD_FM, TIM_F); /***** should use MTFSFM here???? ******/
+				}
+				if (tperror.exs & TP_NDT)
+					return 0;
 			}
-			if (tperror.exs & TP_NDT)
-				return 0;
-#endif
 			return stat;
 
 		case MTERASE:
-			tpqputs("MTERASE -- ERASE TAPE !");
+			tpqputs(TPQD_IOCTLS, "MTERASE -- ERASE TAPE !");
 			if  ((tperror.exs & TP_ST0) && (tperror.exs & TP_WRP)) {
-				tpqputs("Cartridge is write-protected.");
+				tpqputs(TPQD_ALWAYS, "Cartridge is write-protected.");
 				return -EACCES;
 			} else {
 				time_t t = jiffies;
+
+				/* Plain GNU mt(1) 2.2 erases a tape in O_RDONLY. :-( */
+				if (mode_access==READ) 
+					return -EACCES;
+
 				/* give user a few seconds to pull out tape */
-				while (jiffies - t < 3*HZ)
+				while (jiffies - t < 4*HZ)
 					schedule();
 			}
 
-			/* Plain GNU mt(1) 2.2 erases a tape in O_RDONLY. :-( */
-			if (mode_access==READ) 
-				return -EACCES;
-
-			/* don't bother writing filemark */
+			/* don't bother writing filemark first */
 			status_eom_detected = status_eof_detected = NO;
 			return do_qic_cmd(QCMD_ERASE, TIM_R);
 
-
 		case MTRAS1:
-#ifdef TP_HAVE_RAS1
-			tpqputs("MTRAS1: non-destructive self test");
-			stat = do_qic_cmd(QCMD_SELF_TST1, TIM_R);
-			if (stat != 0) {
-				tpqputs("RAS1 failed");
-				return stat;
+			if (TP_HAVE_RAS1) {
+				tpqputs(TPQD_IOCTLS, "MTRAS1: non-destructive self test");
+				stat = do_qic_cmd(QCMD_SELF_TST1, TIM_R);
+				if (stat != 0) {
+					tpqputs(TPQD_ALWAYS, "RAS1 failed");
+					return stat;
+				}
+				return (tp_sense(0)==TE_OK)? 0 : -EIO; /* get_ext_status3(); */
 			}
-			return (tp_sense(0)==TE_OK)? 0 : -EIO; /* get_ext_status3(); */
-#else
-			tpqputs("RAS1 not supported");
+			tpqputs(TPQD_IOCTLS, "RAS1 not supported");
 			return -ENXIO;
-#endif
 
 		case MTRAS2:
-#ifdef TP_HAVE_RAS2
-			tpqputs("MTRAS2: destructive self test");
-			stat = do_qic_cmd(QCMD_SELF_TST2, TIM_R);
-			if (stat != 0) {
-				tpqputs("RAS2 failed");
-				return stat;
+			if (TP_HAVE_RAS2) {
+				tpqputs(TPQD_IOCTLS, "MTRAS2: destructive self test");
+				stat = do_qic_cmd(QCMD_SELF_TST2, TIM_R);
+				if (stat != 0) {
+					tpqputs(TPQD_ALWAYS, "RAS2 failed");
+					return stat;
+				}
+				return (tp_sense(0)==TE_OK)? 0 : -EIO; /* get_ext_status3(); */
 			}
-			return (tp_sense(0)==TE_OK)? 0 : -EIO; /* get_ext_status3(); */
-#else
-			tpqputs("RAS2 not supported");
+			tpqputs(TPQD_IOCTLS, "RAS2 not supported");
 			return -ENXIO;
-#endif
 
-#ifdef TP_HAVE_SEEK
 		case MTSEEK:
-			tpqputs("MTSEEK seeking block");
-			if ((mode_access==WRITE) && status_bytes_wr)
-				return -EACCES;
-			/* NOTE: address (24 bits) is in seek_addr_buf[] */
-			return do_qic_cmd(QCMDV_SEEK_BLK, TIM_F);
-#endif
+			if (TP_HAVE_SEEK && (QIC02_TAPE_IFC==ARCHIVE)) {
+				tpqputs(TPQD_IOCTLS, "MTSEEK seeking block");
+				if ((mode_access==WRITE) && status_bytes_wr)
+					return -EACCES;
+				/* NOTE: address (24 bits) is in seek_addr_buf[] */
+				return do_qic_cmd(AR_QCMDV_SEEK_BLK, TIM_F);
+			}
+			else
+				return -ENOTTY;
 
 		default:
 			return -ENOTTY;
@@ -1444,28 +1525,38 @@
 static inline void dma_transfer(void)
 {
 
-#if TAPE_QIC02_IFC == WANGTEK
-	outb_p(WT_CTL_ONLINE, QIC_CTL_PORT);	/* back to normal */
-#elif TAPE_QIC02_IFC == ARCHIVE
-	outb_p(0, AR_RESET_DMA_PORT);
-#endif
+	if (QIC02_TAPE_IFC == WANGTEK) /* or EVEREX */
+		outb_p(WT_CTL_ONLINE, QIC02_CTL_PORT);	/* back to normal */
+	else if (QIC02_TAPE_IFC == ARCHIVE)
+		outb_p(0, AR_RESET_DMA_PORT);
+	else /* QIC02_TAPE_IFC == MOUNTAIN */
+		outb_p(ctlbits, QIC02_CTL_PORT);
 
-	clear_dma_ff(TAPE_QIC02_DMA);
-	set_dma_mode(TAPE_QIC02_DMA, dma_mode);
-	set_dma_addr(TAPE_QIC02_DMA, buffaddr+dma_bytes_done);	/* full address */
-	set_dma_count(TAPE_QIC02_DMA, TAPE_BLKSIZE);
 
+	clear_dma_ff(QIC02_TAPE_DMA);
+	set_dma_mode(QIC02_TAPE_DMA, dma_mode);
+	set_dma_addr(QIC02_TAPE_DMA, buffaddr+dma_bytes_done);	/* full address */
+	set_dma_count(QIC02_TAPE_DMA, TAPE_BLKSIZE);
+
 	/* start tape DMA controller */
-#if TAPE_QIC02_IFC == WANGTEK
-	outb_p(WT_CTL_DMA | WT_CTL_ONLINE, QIC_CTL_PORT); /* trigger DMA transfer */
-#elif TAPE_QIC02_IFC == ARCHIVE
-	outb_p(AR_CTL_IEN | AR_CTL_DNIEN, QIC_CTL_PORT);  /* enable interrupts again */
-	outb_p(0, AR_START_DMA_PORT);			  /* start DMA transfer */
-	/* In dma_end() AR_RESET_DMA_PORT is written too. */
-#endif
+	if (QIC02_TAPE_IFC == WANGTEK) /* or EVEREX */
+		outb_p(WT_CTL_DMA | WT_CTL_ONLINE, QIC02_CTL_PORT); /* trigger DMA transfer */
+
+	else if (QIC02_TAPE_IFC == ARCHIVE) {
+		outb_p(AR_CTL_IEN | AR_CTL_DNIEN, QIC02_CTL_PORT);  /* enable interrupts again */
+		outb_p(0, AR_START_DMA_PORT);			  /* start DMA transfer */
+		/* In dma_end() AR_RESET_DMA_PORT is written too. */
+
+	} else /* QIC02_TAPE_IFC == MOUNTAIN */ {
+		inb(MTN_R_DESELECT_DMA_PORT);
+		outb_p(ctlbits | (MTN_CTL_EXC_IEN | MTN_CTL_DNIEN), QIC02_CTL_PORT);
+		outb_p(0, MTN_W_SELECT_DMA_PORT);	 /* start DMA transfer */
+		if (dma_mode == DMA_MODE_WRITE)
+			outb_p(0, MTN_W_DMA_WRITE_PORT); /* start DMA transfer */
+	}
 
 	/* start computer DMA controller */
-	enable_dma(TAPE_QIC02_DMA);
+	enable_dma(QIC02_TAPE_DMA);
 	/* block transfer should start now, jumping to the 
 	 * interrupt routine when done or an exception was detected.
 	 */
@@ -1473,7 +1564,7 @@
 
 
 /* start_dma() sets a DMA transfer up between the tape controller and
- * the kernel tape_qic02_buf buffer.
+ * the kernel qic02_tape_buf buffer.
  * Normally bytes_todo==dma_bytes_done at the end of a DMA transfer. If not,
  * a filemark was read, or an attempt to write beyond the End Of Tape 
  * was made. [Or some other bad thing happened.]
@@ -1484,8 +1575,8 @@
 {
 	int stat;
 	
-	TPQPUTS("start_dma() enter");
-	TPQDEB({printk(TPQIC_NAME ": doing_read==%d, doing_write==%d\n", doing_read, doing_write);})
+	tpqputs(TPQD_DEBUG, "start_dma() enter");
+	TPQDEB({printk(TPQIC02_NAME ": doing_read==%d, doing_write==%d\n", doing_read, doing_write);})
 
 	dma_bytes_done = 0;
 	dma_bytes_todo = bytes_todo;
@@ -1508,7 +1599,7 @@
                    since we're only just starting a read/write it doesn't
                    matter some exceptions are cleared by reading the status;
                    we're only interested in CNI and WRP. -Eddy */
-		get_status((char *) &tperror);
+		get_status(&tperror);
 #else
 		/* TP_CNI should now be handled in open(). -Hennus */
 #endif
@@ -1520,17 +1611,22 @@
 #if OBSOLETE
 		/************* not needed iff rd_status() would wait for ready!!!!!! **********/
 		if (wait_for_ready(TIM_S) != TE_OK) {	/*** not sure this is needed ***/
-			tpqputs("wait_for_ready failed in start_dma");
+			tpqputs(TPQD_ALWAYS, "wait_for_ready failed in start_dma");
 			return -EIO;
 		}
 #endif
 
+		if (QIC02_TAPE_IFC == MOUNTAIN) {
+			/* Set control bits to select ONLINE during command */
+			ctlbits |= MTN_QIC02_CTL_ONLINE;
+		}
+
 		/* Tell the controller the data direction */
 
 		/* r/w, timeout medium, check exceptions, sets status_cmd_pending. */
 		stat = send_qic02_cmd((mode == WRITE)? QCMD_WRT_DATA : QCMD_RD_DATA, TIM_M, 0);
 		if (stat!=TE_OK) {
-			printk(TPQIC_NAME ": start_dma: init %s failed\n",
+			printk(TPQIC02_NAME ": start_dma: init %s failed\n",
 				(mode == WRITE)? "write" : "read");
 			(void) tp_sense(0);
 			return stat;
@@ -1549,8 +1645,8 @@
 				doing_write = YES;
 				break;
 			default:
-				printk(TPQIC_NAME ": requested unknown mode %d\n", mode);
-				panic(TPQIC_NAME ": invalid mode in start_dma()");
+				printk(TPQIC02_NAME ": requested unknown mode %d\n", mode);
+				panic(TPQIC02_NAME ": invalid mode in start_dma()");
 		}
 
 	} else if (is_exception()) {
@@ -1559,7 +1655,7 @@
 		 *
 		 * ******** this also affects EOF/EOT handling! ************
 		 */
-		tpqputs("detected exception in start_dma() while transfer in progress");
+		tpqputs(TPQD_ALWAYS, "detected exception in start_dma() while transfer in progress");
 		status_error = YES;
 		return TE_END;
 	}
@@ -1597,23 +1693,32 @@
 
 	TPQPUTS("end_dma() enter");
 
-	disable_dma(TAPE_QIC02_DMA);
-	clear_dma_ff(TAPE_QIC02_DMA);
+	disable_dma(QIC02_TAPE_DMA);
+	clear_dma_ff(QIC02_TAPE_DMA);
 
-#if TAPE_QIC02_IFC == WANGTEK
-	outb_p(WT_CTL_ONLINE, QIC_CTL_PORT);	/* back to normal */
-#elif TAPE_QIC02_IFC == ARCHIVE
-	outb_p(0, AR_RESET_DMA_PORT);
-#endif
+	if (QIC02_TAPE_IFC == WANGTEK) /* or EVEREX */
+		outb_p(WT_CTL_ONLINE, QIC02_CTL_PORT);	/* back to normal */
+	else if (QIC02_TAPE_IFC == ARCHIVE)
+		outb_p(0, AR_RESET_DMA_PORT);
+	else /* QIC02_TAPE_IFC == MOUNTAIN */ {
+		/* Clear control bits, de-select ONLINE during tp_sense */
+		ctlbits &= ~MTN_QIC02_CTL_ONLINE;
+	}
 
 	stat = wait_for_ready(TIM_M);
 	if (status_error || (stat!=TE_OK)) {
-		tpqputs("DMA transfer exception");
+		tpqputs(TPQD_DMAX, "DMA transfer exception");
 		stat = tp_sense((dma_mode==READ)? TP_WRP : 0);
 		/* no return here -- got to clean up first! */
+	} else /* if (QIC02_TAPE_IFC == MOUNTAIN) */ {
+		outb_p(ctlbits, QIC02_CTL_PORT);
 	}
-	/* take the tape controller offline */
+
+	if (QIC02_TAPE_IFC == MOUNTAIN)
+		inb(MTN_R_DESELECT_DMA_PORT);
 
+ 	/* take the tape controller offline */
+
 	/* finish off DMA stuff */
 
 
@@ -1635,15 +1740,15 @@
 /*********** Below are the (public) OS-interface procedures ***********/
 
 
-/* tape_qic02_times_out() is called when a DMA transfer doesn't complete
+/* qic02_tape_times_out() is called when a DMA transfer doesn't complete
  * quickly enough. Usually this means there is something seriously wrong
  * with the hardware/software, but it could just be that the controller
  * has decided to do a long rewind, just when I didn't expect it.
  * Just try again.
  */
-static void tape_qic02_times_out(void)
+static void qic02_tape_times_out(void)
 {
-	printk("time-out in %s driver\n", TPQIC_NAME);
+	printk("time-out in %s driver\n", TPQIC02_NAME);
 	if ((status_cmd_pending>0) || dma_mode) {
 		/* takes tooo long, shut it down */
 		status_dead = YES;
@@ -1653,10 +1758,10 @@
 		status_error = YES;
 		if (dma_mode) {
 			dma_mode = 0;	/* signal end to read/write routine */
-			wake_up(&tape_qic02_transfer);
+			wake_up(&qic02_tape_transfer);
 		}
 	}
-} /* tape_qic02_times_out */
+} /* qic02_tape_times_out */
 
 /*
  * Interrupt handling:
@@ -1679,7 +1784,7 @@
  */
 
 
-/* tape_qic02_interrupt() is called when the tape controller completes 
+/* qic02_tape_interrupt() is called when the tape controller completes 
  * a DMA transfer.
  * We are not allowed to sleep here! 
  *
@@ -1689,32 +1794,34 @@
  * When we are finished, set flags to indicate end, disable timer.
  * NOTE: This *must* be fast! 
  */
-static void tape_qic02_interrupt(int unused)
+static void qic02_tape_interrupt(int unused)
 {
 	int stat, r, i;
 
 	TIMEROFF;
 
 	if (status_expect_int) {
+#ifdef WANT_EXTRA_FULL_DEBUGGING
 		if (TP_DIAGS(current_tape_dev))
 			printk("@");
-	
-		stat = inb(QIC_STAT_PORT);	/* Knock, knock */
-#if TAPE_QIC02_IFC == ARCHIVE			/* "Who's there?" */
-		if (((stat & (AR_STAT_DMADONE)) == 0) &&
-                      ((stat & (QIC_STAT_EXCEPTION)) != 0)) {
-			TIMERCONT;
-			return;			/* "Linux with IRQ sharing" */
-		}
 #endif
-		if ((stat & QIC_STAT_EXCEPTION) == 0) {	/* exception occurred */
+		stat = inb(QIC02_STAT_PORT);	/* Knock, knock */
+		if (QIC02_TAPE_IFC == ARCHIVE) {	/* "Who's there?" */
+			if (((stat & (AR_STAT_DMADONE)) == 0) &&
+        	              ((stat & (QIC02_STAT_EXCEPTION)) != 0)) {
+				TIMERCONT;
+				return;			/* "Linux with IRQ sharing" */
+			}
+		}
+
+		if ((stat & QIC02_STAT_EXCEPTION) == 0) {	/* exception occurred */
 			/* Possible causes for an exception during a transfer:
 			 * 	- during a write-cycle: end of tape (EW) hole detected.
 			 *	- during a read-cycle: filemark or EOD detected.
 			 *	- something went wrong
 			 * So don't continue with the next block.
 			 */
-			tpqputs("isr: exception on tape controller");
+			tpqputs(TPQD_ALWAYS, "isr: exception on tape controller");
 			printk("      status %02x\n", stat);
 			status_error = TE_EX;
 
@@ -1722,7 +1829,7 @@
 
 			dma_mode = 0;	/* wake up rw() */
 			status_expect_int = NO;
-			wake_up(&tape_qic02_transfer);
+			wake_up(&qic02_tape_transfer);
 			return;
 		}
 		/* return if tape controller not ready, or
@@ -1732,15 +1839,14 @@
 /* Skip next ready check for Archive controller because
  * it may be busy reading ahead. Weird. --hhb
  */
-#if TAPE_QIC02_IFC != ARCHIVE		/* I think this is a drive-dependency, not IFC -- hhb */
-		if (stat & QIC_STAT_READY) {		/* not ready */
-			tpqputs("isr: ? Tape controller not ready");
-			r = 1;
-		}
-#endif
+		if (QIC02_TAPE_IFC == WANGTEK)	/* I think this is a drive-dependency, not IFC -- hhb */
+			if (stat & QIC02_STAT_READY) {		/* not ready */
+				tpqputs(TPQD_ALWAYS, "isr: ? Tape controller not ready");
+				r = 1;
+			}
 
-		if ( (i = get_dma_residue(TAPE_QIC02_DMA)) != 0 ) {
-			printk(TPQIC_NAME ": dma_residue == %x !!!\n", i);
+		if ( (i = get_dma_residue(QIC02_TAPE_DMA)) != 0 ) {
+			printk(TPQIC02_NAME ": dma_residue == %x !!!\n", i);
 			r = 1;	/* big trouble, but can't do much about it... */
 		}
 
@@ -1756,23 +1862,23 @@
 			dma_mode = 0;
 			status_expect_int = NO;
 			TPQPUTS("isr: dma_bytes_done");
-			wake_up(&tape_qic02_transfer);
+			wake_up(&qic02_tape_transfer);
 		} else {
 			/* start next transfer, account for track-switching time */
-			timer_table[TAPE_QIC02_TIMER].expires = jiffies + 6*HZ;
+			timer_table[QIC02_TAPE_TIMER].expires = jiffies + 6*HZ;
 			dma_transfer();
 		}
 	} else {
-		printk(TPQIC_NAME ": Unexpected interrupt, stat == %x\n",
-		       inb(QIC_STAT_PORT));
+		printk(TPQIC02_NAME ": Unexpected interrupt, stat == %x\n",
+		       inb(QIC02_STAT_PORT));
 	}
-} /* tape_qic02_interrupt */
+} /* qic02_tape_interrupt */
 
 
-static int tape_qic02_lseek(struct inode * inode, struct file * file, off_t offset, int origin)
+static int qic02_tape_lseek(struct inode * inode, struct file * file, off_t offset, int origin)
 {
 	return -EINVAL;	/* not supported */
-} /* tape_qic02_lseek */
+} /* qic02_tape_lseek */
 
 
 /* read/write routines:
@@ -1807,7 +1913,7 @@
  * request would return the EOF flag for the previous file.
  */
 
-static int tape_qic02_read(struct inode * inode, struct file * filp, char * buf, int count)
+static int qic02_tape_read(struct inode * inode, struct file * filp, char * buf, int count)
 {
 	int error;
 	dev_t dev = inode->i_rdev;
@@ -1815,12 +1921,17 @@
 	unsigned long bytes_todo, bytes_done, total_bytes_done = 0;
 	int stat;
 
+	if (status_zombie==YES) {
+		tpqputs(TPQD_ALWAYS, "configs not set");		
+		return -ENXIO;
+	}
+
 	if (TP_DIAGS(current_tape_dev))
-		printk(TPQIC_NAME ": request READ, minor=%x, buf=%p, count=%x, pos=%lx, flags=%x\n",
+		printk(TPQIC02_NAME ": request READ, minor=%x, buf=%p, count=%x, pos=%lx, flags=%x\n",
 			MINOR(dev), buf, count, filp->f_pos, flags);
 
 	if (count % TAPE_BLKSIZE) {	/* Only allow mod 512 bytes at a time. */
-		tpqputs("Wrong block size");
+		tpqputs(TPQD_BLKSZ, "Wrong block size");
 		return -EINVAL;
 	}
 
@@ -1832,10 +1943,8 @@
 
 	/* Make sure buffer is safe to write into. */
 	error = verify_area(VERIFY_WRITE, buf, count);
-	if (error) {
-		printk(TPQIC_NAME ": read: verify_area(WRITE, %p, %x) failed\n", buf, count);
+	if (error)
 		return error;
-	}
 
 	/* This is rather ugly because it has to implement a finite state
 	 * machine in order to handle the EOF situations properly.
@@ -1849,7 +1958,8 @@
 
 		/* Must ensure that user program sees exactly one EOF token (==0) */
 		if (return_read_eof==YES) {
-			printk("read: return_read_eof==%d, reported_read_eof==%d, total_bytes_done==%ld\n", return_read_eof, reported_read_eof, total_bytes_done);
+			if (TPQDBG(DEBUG))
+				printk("read: return_read_eof==%d, reported_read_eof==%d, total_bytes_done==%lu\n", return_read_eof, reported_read_eof, total_bytes_done);
 
 			if (reported_read_eof==NO) {
 				/* have not yet returned EOF to user program */
@@ -1891,7 +2001,7 @@
 		if (bytes_todo>0) {
 			/* start reading data */
 			if (is_exception())	/****************************************/
-				tpqputs("is_exception() before start_dma()!");
+				tpqputs(TPQD_DMAX, "is_exception() before start_dma()!");
 /******************************************************************
  ***** if start_dma() fails because the head is positioned 0 bytes
  ***** before the FM, (causing EXCEPTION to be set) return_read_eof should
@@ -1902,7 +2012,7 @@
 			if (stat == TE_OK) {
 				/* Wait for transfer to complete, interrupt should wake us */
 				while (dma_mode != 0) {
-					sleep_on(&tape_qic02_transfer);
+					sleep_on(&qic02_tape_transfer);
 				}
 				if (status_error)
 					return_read_eof = YES;
@@ -1918,7 +2028,7 @@
 			}
 			end_dma(&bytes_done);
 			if (bytes_done>bytes_todo) {
-				tpqputs("read: Oops, read more bytes than requested");
+				tpqputs(TPQD_ALWAYS, "read: Oops, read more bytes than requested");
 				return -EIO;
 			}
 			/* copy buffer to user-space in one go */
@@ -1927,7 +2037,7 @@
 #if 1
 			/* Checks Ton's patch below */
 			if ((return_read_eof == NO) && (status_eof_detected == YES)) {
-				printk(TPQIC_NAME ": read(): return_read_eof=%d, status_eof_detected=YES. return_read_eof:=YES\n", return_read_eof);
+				printk(TPQIC02_NAME ": read(): return_read_eof=%d, status_eof_detected=YES. return_read_eof:=YES\n", return_read_eof);
 			}
 #endif
 			if ((bytes_todo != bytes_done) || (status_eof_detected == YES))
@@ -1943,9 +2053,9 @@
 			count -= bytes_done;
 		}
 	}
-	tpqputs("read request for <0 bytes");
+	tpqputs(TPQD_ALWAYS, "read request for <0 bytes");
 	return -EINVAL;
-} /* tape_qic02_read */
+} /* qic02_tape_read */
 
 
 
@@ -1977,24 +2087,29 @@
  * tape device again. The driver will detect an exception status in (No Cartridge)
  * and force a rewind. After that tar may continue writing.
  */
-static int tape_qic02_write(struct inode * inode, struct file * filp, char * buf, int count)
+static int qic02_tape_write(struct inode * inode, struct file * filp, char * buf, int count)
 {
 	int error;
 	dev_t dev = inode->i_rdev;
 	unsigned short flags = filp->f_flags;
 	unsigned long bytes_todo, bytes_done, total_bytes_done = 0;
 
+	if (status_zombie==YES) {
+		tpqputs(TPQD_ALWAYS, "configs not set");		
+		return -ENXIO;
+	}
+
 	if (TP_DIAGS(current_tape_dev))
-		printk(TPQIC_NAME ": request WRITE, minor=%x, buf=%p, count=%x, pos=%lx, flags=%x\n",
+		printk(TPQIC02_NAME ": request WRITE, minor=%x, buf=%p, count=%x, pos=%lx, flags=%x\n",
 			MINOR(dev), buf, count, filp->f_pos, flags);
 
 	if (count % TAPE_BLKSIZE) {	/* only allow mod 512 bytes at a time */
-		tpqputs("Wrong block size");
+		tpqputs(TPQD_BLKSZ, "Wrong block size");
 		return -EINVAL;
 	}
 
 	if (mode_access==READ) {
-		tpqputs("Not in write mode");
+		tpqputs(TPQD_ALWAYS, "Not in write mode");
 		return -EACCES;
 	}
 
@@ -2003,16 +2118,14 @@
 	 * be valid.
 	 */
 	if ((tperror.exs & TP_ST0) && (tperror.exs & TP_WRP)) {
-		tpqputs("Cartridge is write-protected.");
+		tpqputs(TPQD_ALWAYS, "Cartridge is write-protected.");
 		return -EACCES;	/* don't even try when write protected */
 	}
 
 	/* Make sure buffer is safe to read from. */
 	error = verify_area(VERIFY_READ, buf, count);
-	if (error) {
-		printk(TPQIC_NAME ": write: verify_area(READ, %p, %x) failed\n", buf, count);
+	if (error)
 		return error;
-	}
 
 	if (doing_read == YES)
 		terminate_read(0);
@@ -2029,7 +2142,7 @@
 
 			if (reported_write_eof==NO) {
 				if (bytes_todo>0) {
-					tpqputs("partial write");
+					tpqputs(TPQD_ALWAYS, "partial write");
 					/* partial write signals EOF to user program */
 				}
 				reported_write_eof = YES;
@@ -2055,19 +2168,19 @@
  ***** fail and write() will return ENXIO error
  *****/
 			if (start_dma(WRITE, bytes_todo) != TE_OK) {
-				tpqputs("write: start_dma() failed");
+				tpqputs(TPQD_ALWAYS, "write: start_dma() failed");
 				/* should do sense() on error here */
 				return -ENXIO;	/*********** FIXTHIS **************/
 			}
 
 			/* Wait for write to complete, interrupt should wake us. */
 			while ((status_error == 0) && (dma_mode != 0)) {
-				sleep_on(&tape_qic02_transfer);
+				sleep_on(&qic02_tape_transfer);
 			}
 
 			end_dma(&bytes_done);
 			if (bytes_done>bytes_todo) {
-				tpqputs("write: Oops, wrote more bytes than requested");
+				tpqputs(TPQD_ALWAYS, "write: Oops, wrote more bytes than requested");
 				return -EIO;
 			}
 			/* If the dma-transfer was aborted because of an exception,
@@ -2084,11 +2197,11 @@
 			 */
 			if (status_error) {
 				if (status_eom_detected == YES)	{
-					tpqputs("write: EW detected");
+					tpqputs(TPQD_ALWAYS, "write: EW detected");
 					return_write_eof = YES;
 				} else {
 					/* probably EXC_RWA */
-					tpqputs("write: dma: error in writing");
+					tpqputs(TPQD_ALWAYS, "write: dma: error in writing");
 					return -EIO;
 				}
 			}
@@ -2106,14 +2219,15 @@
 			count -= bytes_done;
 		}
 	}
-	tpqputs("write request for <0 bytes");
-	printk(TPQIC_NAME ": status_bytes_wr %x, buf %p, total_bytes_done %lx, count %x\n", status_bytes_wr, buf, total_bytes_done, count);
+	tpqputs(TPQD_ALWAYS, "write request for <0 bytes");
+	if (TPQDBG(DEBUG))
+		printk(TPQIC02_NAME ": status_bytes_wr %x, buf %p, total_bytes_done %lx, count %x\n", status_bytes_wr, buf, total_bytes_done, count);
 	return -EINVAL;
-} /* tape_qic02_write */
+} /* qic02_tape_write */
 
 
 
-/* tape_qic02_open()
+/* qic02_tape_open()
  * We allow the device to be opened, even if it is marked 'dead' because
  * we want to be able to reset the tape device without rebooting.
  * Only one open tape file at a time, except when minor=255.
@@ -2124,17 +2238,16 @@
  * remembered values, rewind the tape and set the required density.
  * Don't rewind if the minor bits specify density 0.
  */
-static int tape_qic02_open(struct inode * inode, struct file * filp)
+static int qic02_tape_open(struct inode * inode, struct file * filp)
 {
 	dev_t dev = inode->i_rdev;
 	unsigned short flags = filp->f_flags;
-	unsigned short dens;
+	unsigned short dens = 0;
 	int s;
 
-	status_open = NO;
 
 	if (TP_DIAGS(dev)) {
-		printk("tape_qic02_open: dev=%x, flags=%x     ", dev, flags);
+		printk("qic02_tape_open: dev=%x, flags=%x     ", dev, flags);
 	}
 
 	if (MINOR(dev)==255)	/* special case for resetting */
@@ -2143,10 +2256,20 @@
 		else
 			return -EPERM;
 
-	if (status_open==YES) {
-		return -EBUSY;	/* only one at a time... */
+	if (status_dead==YES)
+		/* Allow `mt reset' ioctl() even when already open()ed. */
+		return 0;
+
+	/* Only one at a time from here on... */
+	if (filp->f_count>1) {	/* filp->f_count==1 for the first open() */
+		return -EBUSY;
 	}
-	status_open = YES;
+
+	if (status_zombie==YES)
+		/* no irq/dma/port stuff allocated yet, no reset done
+		 * yet, so return until MTSETCONFIG has been done.
+		 */
+		return 0;
 
 	status_bytes_rd = NO;
 	status_bytes_wr = NO;
@@ -2166,22 +2289,41 @@
 			mode_access = READ;
 			break;
 		case O_WRONLY:	/* Fallthru... Strictly speaking this is not correct... */
-		case O_RDWR:	/* reads are allowed as long as nothing is written */
+		case O_RDWR:	/* Reads are allowed as long as nothing is written */
 			mode_access = WRITE;
 			break;
 	}
 
+	/* This is to avoid tape-changed problems (TP_CNI exception).
+	 *
+	 * Since removing the cartridge will not raise an exception,
+	 * we always do a tp_sense() to make sure we have the proper
+	 * CNI status, the 2150L may need an additional sense.... - Eddy
+	 */
+	s = tp_sense(TP_WRP|TP_EOM|TP_BOM|TP_CNI|TP_EOR);
+
+	if (s == TE_OK)
+		/* Try to clear cartridge-changed status for Archive-2150L */
+		if ((tperror.exs & TP_ST0) && (tperror.exs & TP_CNI))
+			s = tp_sense(TP_WRP|TP_EOM|TP_BOM|TP_CNI|TP_EOR);
 
-	/* This is to avoid tape-changed problems (TP_CNI exception). */
-	if (is_exception()) {
-		s = tp_sense(TP_WRP|TP_EOM|TP_BOM|TP_CNI|TP_EOR);
-		if (s != TE_OK) {
-			status_open = NO;
-			tpqputs("open: sense() failed after exception was detected");
-			return -EIO;
-		}
+	if (s != TE_OK) {
+		tpqputs(TPQD_ALWAYS, "open: sense() failed");
+		return -EIO;
+	}
+
+	/* exception bits should be up-to-date now, so check for
+	 * tape presence and exit if absent.
+	 * Even `mt stat' will fail without a tape.
+	 */
+	if ((tperror.exs & TP_ST0) && (tperror.exs & TP_CNI)) {
+		tpqputs(TPQD_ALWAYS, "No tape present.");
+		return -EIO;
 	}
 
+	/* At this point we can assume that a tape is present and
+	 * that it will remain present until release() is called.
+	 */
 
 	/* not allowed to do QCMD_DENS_* unless tape is rewound */
 	if ((TP_DENS(dev)!=0) && (TP_DENS(current_tape_dev) != TP_DENS(dev))) {
@@ -2189,8 +2331,10 @@
 		 * i.e. user wants to use tape in different format.
 		 * [assuming single drive operation]
 		 */
-		tpqputs("Density minor bits have changed. Forcing rewind.");
-		need_rewind = YES;
+		if (TP_HAVE_DENS) {
+			tpqputs(TPQD_REWIND, "Density minor bits have changed. Forcing rewind.");
+			need_rewind = YES;
+		}
 	} else {
 		/* density bits still the same, but TP_DIAGS bit 
 		 * may have changed.
@@ -2198,30 +2342,27 @@
 		current_tape_dev = dev;
 	}
 
-	if (need_rewind == YES) {
+	if (need_rewind == YES) { /***************** CHECK THIS!!!!!!!! **********/
 		s = do_qic_cmd(QCMD_REWIND, TIM_R);
 		if (s != 0) {
-			tpqputs("open: rewind failed");
-			status_open = NO;
+			tpqputs(TPQD_ALWAYS, "open: rewind failed");
 			return -EIO;
 		}
 	}
 
 
 /* Note: After a reset command, the controller will rewind the tape
- *	 just before performing any tape movement operation!
+ *	 just before performing any tape movement operation! ************ SO SET need_rewind flag!!!!!
  */ 
-	if (status_dead) {
-		tpqputs("open: tape dead, attempting reset");
+	if (status_dead==YES) {
+		tpqputs(TPQD_ALWAYS, "open: tape dead, attempting reset");
 		if (tape_reset(1)!=TE_OK) {
-			status_open = NO;
 			return -ENXIO;
 		} else {
 			status_dead = NO;
 			if (tp_sense(~(TP_ST1|TP_ILL)) != TE_OK) {
-				tpqputs("open: tp_sense() failed\n");
+				tpqputs(TPQD_ALWAYS, "open: tp_sense() failed\n");
 				status_dead = YES;	/* try reset next time */
-				status_open = NO;
 				return -EIO;
 			}
 		}
@@ -2239,14 +2380,16 @@
 
 	current_tape_dev = dev;
 	need_rewind = NO;
-	dens = TP_DENS(dev);
+	if (TP_HAVE_DENS)
+		dens = TP_DENS(dev);
+
 	if (dens < sizeof(format_names)/sizeof(char *))
-		printk(TPQIC_NAME ": format: %s%s\n", (dens!=0)? "QIC-" : "", format_names[dens]);
+		printk(TPQIC02_NAME ": format: %s%s\n", (dens!=0)? "QIC-" : "", format_names[dens]);
 	else
-		tpqputs("Wait for retensioning...");
+		tpqputs(TPQD_REWIND, "Wait for retensioning...");
 
 	switch (TP_DENS(dev)) {
-		case 0: /* This one's for Eddy ;-) */
+		case 0: /* Minor 0 is for drives without set-density support */
 			s = 0;
 			break;
 		case 1:
@@ -2271,28 +2414,32 @@
 			s = do_qic_cmd(QCMD_RETEN, TIM_R);
 	}
 	if (s != 0) {
-		status_open = NO;	/* fail if fault occurred */
 		status_dead = YES;	/* force reset */
 		current_tape_dev = 0xff80;
 		return -EIO;
 	}
 
 	return 0;
-} /* tape_qic02_open */
+} /* qic02_tape_open */
 
 
-static int tape_qic02_readdir(struct inode * inode, struct file * filp, struct dirent * dp, int count)
+
+static int qic02_tape_readdir(struct inode * inode, struct file * filp, struct dirent * dp, int count)
 {
 	return -ENOTDIR;	/* not supported */
-} /* tape_qic02_readdir */
+} /* qic02_tape_readdir */
+
 
 
-static void tape_qic02_release(struct inode * inode, struct file * filp)
+static void qic02_tape_release(struct inode * inode, struct file * filp)
 {
 	dev_t dev = inode->i_rdev;
 
 	if (TP_DIAGS(dev))
-		printk("tape_qic02_release: dev=%x\n", dev);
+		printk("qic02_tape_release: dev=%x\n", dev);
+
+	if (status_zombie==YES)		/* don't rewind in zombie mode */
+		return;
 
 	/* Terminate any pending write cycle. Terminating the read-cycle
 	 * is delayed until it is required to do so for a new command.
@@ -2300,28 +2447,112 @@
 	terminate_write(-1);
 
 	if (status_dead==YES)
-		tpqputs("release: device dead!?");
+		tpqputs(TPQD_ALWAYS, "release: device dead!?");
 
-	if (status_open==NO) {
-		tpqputs("release: device not open");
-		return;
-	}
  	/* Rewind only if minor number requires it AND 
-	 * read/writes have been done.
+	 * read/writes have been done. ************* IS THIS CORRECT??????????
 	 */
 	if ((TP_REWCLOSE(dev)) && (status_bytes_rd | status_bytes_wr)) {
-		tpqputs("release: Doing rewind...");
+		tpqputs(TPQD_REWIND, "release: Doing rewind...");
 		(void) do_qic_cmd(QCMD_REWIND, TIM_R);
 	}
 
-	status_open = NO;
 	return;
-} /* tape_qic02_release */
+} /* qic02_tape_release */
+
 
+#ifdef CONFIG_QIC02_DYNCONF
+/* Set masks etc. based on the interface card type. */
+int update_ifc_masks(int ifc)
+{
+	QIC02_TAPE_IFC = ifc;
+
+	if ((QIC02_TAPE_IFC == WANGTEK) || (QIC02_TAPE_IFC == EVEREX)) {
+		QIC02_STAT_PORT = QIC02_TAPE_PORT;
+		QIC02_CTL_PORT = QIC02_TAPE_PORT;
+		QIC02_CMD_PORT = QIC02_TAPE_PORT+1;
+		QIC02_DATA_PORT = QIC02_TAPE_PORT+1;
+		QIC02_STAT_READY = WT_QIC02_STAT_READY;
+		QIC02_STAT_EXCEPTION = WT_QIC02_STAT_EXCEPTION;
+		QIC02_STAT_MASK = WT_QIC02_STAT_MASK;
+
+		QIC02_STAT_RESETMASK = WT_QIC02_STAT_RESETMASK;
+		QIC02_STAT_RESETVAL = WT_QIC02_STAT_RESETVAL;
+
+		QIC02_CTL_RESET = WT_QIC02_CTL_RESET;
+		QIC02_CTL_REQUEST = WT_QIC02_CTL_REQUEST;
+ 
+		if (QIC02_TAPE_DMA == 3)
+			WT_CTL_DMA = WT_CTL_DMA3;
+		else if (QIC02_TAPE_DMA == 1)
+			WT_CTL_DMA = WT_CTL_DMA1;
+		else {
+			tpqputs(TPQD_ALWAYS, "Unsupported or incorrect DMA channel");
+			return -EIO;
+		} 
+
+		if (QIC02_TAPE_IFC == EVEREX) {
+			/* Everex is a special case for Wangtek (actually
+			 * it's the other way 'round, but I saw Wangtek first)
+			 */
+			if (QIC02_TAPE_DMA==3)
+				WT_CTL_DMA = WT_CTL_DMA1;
+			/* Fixup the kernel copy of the IFC type to that
+			 * we don't have to distinguish between Wangtek and
+			 * and Everex at runtime.
+			 */
+			QIC02_TAPE_IFC = WANGTEK;
+		}
+	} else if (QIC02_TAPE_IFC == ARCHIVE) {
+		QIC02_STAT_PORT = QIC02_TAPE_PORT+1;
+		QIC02_CTL_PORT = QIC02_TAPE_PORT+1;
+		QIC02_CMD_PORT = QIC02_TAPE_PORT;
+		QIC02_DATA_PORT = QIC02_TAPE_PORT;
+		QIC02_STAT_READY = AR_QIC02_STAT_READY;
+		QIC02_STAT_EXCEPTION  = AR_QIC02_STAT_EXCEPTION;
+		QIC02_STAT_MASK = AR_QIC02_STAT_MASK;
+
+		QIC02_STAT_RESETMASK = AR_QIC02_STAT_RESETMASK;
+		QIC02_STAT_RESETVAL = AR_QIC02_STAT_RESETVAL;
+
+		QIC02_CTL_RESET = AR_QIC02_CTL_RESET;
+		QIC02_CTL_REQUEST = AR_QIC02_CTL_REQUEST;
+ 
+		if (QIC02_TAPE_DMA > 3) {
+			tpqputs(TPQD_ALWAYS, "Unsupported or incorrect DMA channel");
+			return -EIO;
+		} 
+	} else if (QIC02_TAPE_IFC == MOUNTAIN) {
+		QIC02_STAT_PORT = QIC02_TAPE_PORT+1;
+		QIC02_CTL_PORT = QIC02_TAPE_PORT+1;
+		QIC02_CMD_PORT = QIC02_TAPE_PORT;
+		QIC02_DATA_PORT = QIC02_TAPE_PORT;
+
+		QIC02_STAT_READY = MTN_QIC02_STAT_READY;
+		QIC02_STAT_EXCEPTION  = MTN_QIC02_STAT_EXCEPTION;
+		QIC02_STAT_MASK = MTN_QIC02_STAT_MASK;
+
+		QIC02_STAT_RESETMASK = MTN_QIC02_STAT_RESETMASK;
+		QIC02_STAT_RESETVAL = MTN_QIC02_STAT_RESETVAL;
+
+		QIC02_CTL_RESET = MTN_QIC02_CTL_RESET;
+		QIC02_CTL_REQUEST = MTN_QIC02_CTL_REQUEST;
+ 
+		if (QIC02_TAPE_DMA > 3) {
+			tpqputs(TPQD_ALWAYS, "Unsupported or incorrect DMA channel");
+			return -EIO;
+		} 
+	} else {
+		tpqputs(TPQD_ALWAYS, "Invalid interface type");
+		return -ENXIO;
+	}
+	return 0;
+} /* update_ifc-masks */
+#endif
 
 
 /* ioctl allows user programs to rewind the tape and stuff like that */
-static int tape_qic02_ioctl(struct inode * inode, struct file * filp, 
+static int qic02_tape_ioctl(struct inode * inode, struct file * filp, 
 		     unsigned int iocmd, unsigned long ioarg)
 {
 	int error;
@@ -2330,14 +2561,12 @@
 	int c;
 	struct mtop operation;
 	char *stp, *argp;
-
-#ifdef TP_HAVE_TELL
 	unsigned char blk_addr[6];
 	struct mtpos ioctl_tell;
-#endif
 
+
 	if (TP_DIAGS(current_tape_dev))
-		printk(TPQIC_NAME ": ioctl(%4x, %4x, %4lx)\n", dev_maj, iocmd, ioarg);
+		printk(TPQIC02_NAME ": ioctl(%4x, %4x, %4lx)\n", dev_maj, iocmd, ioarg);
 
 	if (!inode || !ioarg)
 		return -EINVAL;
@@ -2345,19 +2574,100 @@
 	/* check iocmd first */
 
 	if (dev_maj != QIC02_TAPE_MAJOR) {
-		printk(TPQIC_NAME ": Oops! Wrong device?\n");
+		printk(TPQIC02_NAME ": Oops! Wrong device?\n");
 		/* A panic() would be appropriate here */
 		return -ENODEV;
 	}
 
 	c = iocmd & IOCCMD_MASK;
+
+#ifdef DDIOCSDBG
+	/* Check for DDI Debug Control, contributed by FvK, edited by HHB. */
+	if (c == DDIOCSDBG) {
+		if (!suser())
+			return -EPERM;
+		verify_area(VERIFY_READ, (int *) ioarg, sizeof(int));
+		c = get_fs_long((int *) ioarg);
+		if (c==0) {
+			QIC02_TAPE_DEBUG = 0;
+			return 0;
+		}
+		if ((c>=1) && (c<=32)) {
+			QIC02_TAPE_DEBUG |= (1 << (c-1));
+			return 0;
+		}
+		if (c >= 128) {
+			QIC02_TAPE_DEBUG &= ~(1 << (c - 128));
+			return 0;
+		}
+		return -EINVAL;
+	}
+#endif
+
+#ifdef CONFIG_QIC02_DYNCONF
+	if (c == (MTIOCGETCONFIG & IOCCMD_MASK)) {
+		if (((iocmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT) != sizeof(struct mtconfiginfo)) {
+			tpqputs(TPQD_ALWAYS, "sizeof(struct mtconfiginfo) does not match!");
+			return -EFAULT;
+		}
+
+		/* check for valid user address */
+		error = verify_area(VERIFY_WRITE, (void *) ioarg, sizeof(qic02_tape_dynconf));
+		if (error)
+			return error;
+		/* copy current settings to user space */
+		stp = (char *) &qic02_tape_dynconf;
+		argp = (char *) ioarg;
+		for (i=0; i<sizeof(qic02_tape_dynconf); i++) 
+			put_fs_byte(*stp++, argp++);
+		return 0;
+
+	} else if (c == (MTIOCSETCONFIG & IOCCMD_MASK)) {
+		static int qic02_get_resources(void), qic02_release_resources(void);
+
+		/* One should always do a MTIOCGETCONFIG first, then update
+		 * user-settings, then write back with MTIOCSETCONFIG.
+		 * Re-open() the device before actual use to make sure
+		 * everything is initialized.
+		 */
+		if (((iocmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT) != sizeof(struct mtconfiginfo)) {
+			tpqputs(TPQD_ALWAYS, "sizeof(struct mtconfiginfo) does not match!");
+			return -EFAULT;
+		}
+		if (!suser())
+			return -EPERM;
+		if ((doing_read!=NO) || (doing_write!=NO))
+			return -EBUSY;
+		error = verify_area(VERIFY_READ, (char *) ioarg, sizeof(qic02_tape_dynconf));
+		if (error)
+			return error;
+
+		/* copy struct from user space to kernel space */
+		stp = (char *) &qic02_tape_dynconf;
+		argp = (char *) ioarg;
+		for (i=0; i<sizeof(qic02_tape_dynconf); i++)
+			*stp++ = get_fs_byte(argp++);
+		if (status_zombie==NO)
+			qic02_release_resources();	/* and go zombie */
+		if (update_ifc_masks(qic02_tape_dynconf.ifc_type))
+			return -EIO;
+		if (qic02_get_resources())
+			return -ENXIO;
+		return 0;
+
+	}
+	if (status_zombie==YES) {
+		tpqputs(TPQD_ALWAYS, "Configs not set");
+		return -ENXIO;
+	}
+#endif
 	if (c == (MTIOCTOP & IOCCMD_MASK)) {
 
 		/* Compare expected struct size and actual struct size. This
 		 * is useful to catch programs compiled with old #includes.
 		 */
 		if (((iocmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT) != sizeof(struct mtop)) {
-			tpqputs("sizeof(struct mtop) does not match!");
+			tpqputs(TPQD_ALWAYS, "sizeof(struct mtop) does not match!");
 			return -EFAULT;
 		}
 		error = verify_area(VERIFY_READ, (char *) ioarg, sizeof(operation));
@@ -2382,16 +2692,17 @@
 			printk("OP op=%4x, count=%4x\n", operation.mt_op, operation.mt_count);
 
 		if (operation.mt_count < 0)
-			tpqputs("Warning: negative mt_count ignored");
+			tpqputs(TPQD_ALWAYS, "Warning: negative mt_count ignored");
 
 		ioctl_status.mt_resid = operation.mt_count;
 		if (operation.mt_op == MTSEEK) {
+			if (!TP_HAVE_SEEK)
+				return -ENOTTY;
 			seek_addr_buf[0] = (operation.mt_count>>16)&0xff;
 			seek_addr_buf[1] = (operation.mt_count>>8)&0xff;
 			seek_addr_buf[2] = (operation.mt_count)&0xff;
 			if (operation.mt_count>>24)
 				return -EINVAL;
-
 			if ((error = do_ioctl_cmd(operation.mt_op)) != 0)
 				return error;
 			ioctl_status.mt_resid = 0;
@@ -2411,7 +2722,7 @@
 
 		/* compare expected struct size and actual struct size */
 		if (((iocmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT) != sizeof(struct mtget)) {
-			tpqputs("sizeof(struct mtget) does not match!");
+			tpqputs(TPQD_ALWAYS, "sizeof(struct mtget) does not match!");
 			return -EFAULT;
 		}
 
@@ -2432,14 +2743,14 @@
 			put_fs_byte(*stp++, argp++);
 		return 0;
 
-#ifdef TP_HAVE_TELL
-	} else if (c == (MTIOCPOS & IOCCMD_MASK)) {
+
+	} else if (TP_HAVE_TELL && (c == (MTIOCPOS & IOCCMD_MASK))) {
 		if (TP_DIAGS(current_tape_dev))
 			printk("POS ");
 
 		/* compare expected struct size and actual struct size */
 		if (((iocmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT) != sizeof(struct mtpos)) {
-			tpqputs("sizeof(struct mtpos) does not match!");
+			tpqputs(TPQD_ALWAYS, "sizeof(struct mtpos) does not match!");
 			return -EFAULT;
 		}
 
@@ -2448,11 +2759,11 @@
 		if (error)
 			return error;
 
-		tpqputs("MTTELL reading block address");
+		tpqputs(TPQD_IOCTLS, "MTTELL reading block address");
 		if ((doing_read==YES) || (doing_write==YES))
-			finish_rw(QCMDV_TELL_BLK);
+			finish_rw(AR_QCMDV_TELL_BLK);
 
-		c = rdstatus((char *) blk_addr, sizeof(blk_addr), QCMDV_TELL_BLK);
+		c = rdstatus((char *) blk_addr, sizeof(blk_addr), AR_QCMDV_TELL_BLK);
 		if (c!=TE_OK)
 			return -EIO;
 
@@ -2464,24 +2775,24 @@
 		for (i=0; i<sizeof(ioctl_tell); i++) 
 			put_fs_byte(*stp++, argp++);
 		return 0;
-#endif
+
 	} else
 		return -ENOTTY;	/* Other cmds not supported. */
-} /* tape_qic02_ioctl */
+} /* qic02_tape_ioctl */
 
 
 
 /* These are (most) of the interface functions: */
-static struct file_operations tape_qic02_fops = {
-	tape_qic02_lseek,		/* not allowed */
-	tape_qic02_read,		/* read */
-	tape_qic02_write,		/* write */
-	tape_qic02_readdir,		/* not allowed */
+static struct file_operations qic02_tape_fops = {
+	qic02_tape_lseek,		/* not allowed */
+	qic02_tape_read,		/* read */
+	qic02_tape_write,		/* write */
+	qic02_tape_readdir,		/* not allowed */
 	NULL,				/* select ??? */
-	tape_qic02_ioctl,		/* ioctl */
+	qic02_tape_ioctl,		/* ioctl */
 	NULL,				/* mmap not allowed */
-	tape_qic02_open,		/* open */
-	tape_qic02_release,		/* release */
+	qic02_tape_open,		/* open */
+	qic02_tape_release,		/* release */
 	NULL				/* fsync */
 };
 
@@ -2491,8 +2802,8 @@
  * then dma_transfer() would have to disable interrupts explicitly.
  * System load is high enough as it is :-(
  */
-static struct sigaction tape_qic02_sigaction = {
-	tape_qic02_interrupt,
+static struct sigaction qic02_tape_sigaction = {
+	qic02_tape_interrupt,
 	0,
 	SA_INTERRUPT,
 	NULL
@@ -2509,105 +2820,159 @@
 }
 
 
-/* init() is called from chr_dev_init() in kernel/chr_drv/mem.c */
-long tape_qic02_init(long kmem_start)
-	/* Shouldn't this be a caddr_t ? */
+
+static void qic02_release_resources(void)
 {
+	free_irq(QIC02_TAPE_IRQ);
+	free_dma(QIC02_TAPE_DMA);
+	status_zombie = YES;
+} /* qic02_release_resources */
 
-	printk(TPQIC_NAME ": IRQ %d, DMA %d, IO %xh, IFC %s, %s, %s\n",
-		 TAPE_QIC02_IRQ, TAPE_QIC02_DMA, TAPE_QIC02_PORT,
-#if TAPE_QIC02_IFC == WANGTEK
-		 "Wangtek",
-#elif TAPE_QIC02_IFC == ARCHIVE
-		 "Archive",
-#else
-# error
-#endif
-		 rcs_revision, rcs_date);
+
+
 
+static int qic02_get_resources(void)
+{
 	/* First perform some checks. If one of them fails,
 	 * the tape driver will not be registered to the system.
 	 */
+	if (QIC02_TAPE_IRQ>16) {
+		tpqputs(TPQD_ALWAYS, "Bogus interrupt number.");
+		return -1;
+	}
 
-	/* Should do IRQ/DMA allocation in open(). Use free_irq() in release()
-	 * return -EBUSY, if allocation fails in open().
-	 * Make IRQ settable/readable through ioctls. DMA is trickier because
-	 * some bits change for different DMA numbers. Also, this would add 
-	 * runtime overhead for having the dma channel number be a variable
-	 * rather than a constant. Probably need to make the DMA stuff #ifdef'd
-	 * to choose between hardcoded and changeable DMA channel.
-	 *
-	 * Also, Linux needs a more generic way to set DMA/IRQ and other stuff.
-	 * Right now, this is done differently for every device. (setserial,
-	 * ctrlaltdel, setdfprm, setfdthr, tunelp, ((setterm))...)
-	 *
-	 * #include <hint.h>
+	/* for DYNCONF, allocating DMA & IRQ should not be done until 
+         * the config parameters have been set using MTSETCONFIG.
 	 */
 
 	/* get IRQ */
-	if (irqaction(TAPE_QIC02_IRQ, &tape_qic02_sigaction)) {
-		printk(TPQIC_NAME ": can't allocate IRQ%d for QIC-02 tape\n",
-			TAPE_QIC02_IRQ);
-		return kmem_start;
+	if (irqaction(QIC02_TAPE_IRQ, &qic02_tape_sigaction)) {
+		printk(TPQIC02_NAME ": can't allocate IRQ%d for QIC-02 tape\n",
+			QIC02_TAPE_IRQ);
+		status_zombie = YES;
+		return -1;
 	}
 
 	/* After IRQ, allocate DMA channel */
-	if (request_dma(TAPE_QIC02_DMA)) {
-		printk(TPQIC_NAME ": can't allocate DMA%d for QIC-02 tape\n",
-			TAPE_QIC02_DMA);
-		free_irq(TAPE_QIC02_IRQ);
-		return kmem_start;
+	if (request_dma(QIC02_TAPE_DMA)) {
+		printk(TPQIC02_NAME ": can't allocate DMA%d for QIC-02 tape\n",
+			QIC02_TAPE_DMA);
+		free_irq(QIC02_TAPE_IRQ);
+		status_zombie = YES;
+		return -1;
+	}
+
+	printk(TPQIC02_NAME ": Settings: IRQ %d, DMA %d, IO 0x%x, IFC %s\n",
+		QIC02_TAPE_IRQ, QIC02_TAPE_DMA,
+		((QIC02_TAPE_IFC==ARCHIVE) || (QIC02_TAPE_IFC==MOUNTAIN))?
+			QIC02_CMD_PORT : QIC02_STAT_PORT,
+		(QIC02_TAPE_IFC==MOUNTAIN)? "Mountain" :
+			((QIC02_TAPE_IFC==ARCHIVE)? "Archive" : "Wangtek"));
+
+	if (tape_reset(0)!=TE_OK || tp_sense(TP_WRP|TP_POR|TP_CNI)!=TE_OK) {
+		/* No drive detected, so vanish */
+		tpqputs(TPQD_ALWAYS, "No drive detected -- releasing irq and dma.");
+		status_dead = YES;
+		qic02_release_resources();
+		return -1;
 	}
 
+	/* All should be ok now */
+	status_zombie = NO;
+	return 0;
+} /* qic02_get_resources */
+
+
+
+/* init() is called from chr_dev_init() in drivers/char/mem.c */
+long qic02_tape_init(long kmem_start)
+	/* Shouldn't this be a caddr_t ? */
+{
+
 	if (TPSTATSIZE != 6) {
-		printk(TPQIC_NAME ": internal error: tpstatus struct incorrect!\n");
+		printk(TPQIC02_NAME ": internal error: tpstatus struct incorrect!\n");
 		return kmem_start;
 	}
 	if ((TPQBUF_SIZE<512) || (TPQBUF_SIZE>=0x10000)) {
-		printk(TPQIC_NAME ": internal error: DMA buffer size out of range\n");
+		printk(TPQIC02_NAME ": internal error: DMA buffer size out of range\n");
 		return kmem_start;
 	}
 
-	printk(TPQIC_NAME ": DMA buffers: %u blocks", NR_BLK_BUF);
+	QIC02_TAPE_DEBUG = TPQD_DEFAULT_FLAGS;
+
+#ifndef CONFIG_QIC02_DYNCONF
+	printk(TPQIC02_NAME ": IRQ %d, DMA %d, IO 0x%x, IFC %s, %s, %s\n",
+		 QIC02_TAPE_IRQ, QIC02_TAPE_DMA,
+# if QIC02_TAPE_IFC == WANGTEK
+		 QIC02_STAT_PORT, "Wangtek",
+# elif QIC02_TAPE_IFC == ARCHIVE
+		 QIC02_CMD_PORT, "Archive",
+# elif QIC02_TAPE_IFC == MOUNTAIN
+		 QIC02_CMD_PORT, "Mountain",
+# else
+#  error
+# endif
+		 rcs_revision, rcs_date);
+	if (qic02_get_resources())
+		return kmem_start;
+#else
+	printk(TPQIC02_NAME ": Runtime config, %s, %s\n", 
+		 rcs_revision, rcs_date);
+	
+	QIC02_TAPE_IRQ = BOGUS_IRQ;	/* invalid value */
+#endif
+
+	printk(TPQIC02_NAME ": DMA buffers: %u blocks", NR_BLK_BUF);
 
 	/* Setup the page-address for the dma transfer.
 	 * This assumes a one-to-one identity mapping between
 	 * kernel addresses and physical memory.
 	 */
-	buffaddr = align_buffer((unsigned long) &tape_qic02_buf, TAPE_BLKSIZE);
-	printk(", at address 0x%lx (0x%lx)\n", buffaddr, (unsigned long) &tape_qic02_buf);
+	buffaddr = align_buffer((unsigned long) &qic02_tape_buf, TAPE_BLKSIZE);
+	printk(", at address 0x%lx (0x%lx)\n", buffaddr, (unsigned long) &qic02_tape_buf);
 
 #ifndef CONFIG_MAX_16M
 	if (buffaddr+TPQBUF_SIZE>=0x1000000) {
-		printk(TPQIC_NAME ": DMA buffer *must* be in lower 16MB\n");
+		printk(TPQIC02_NAME ": DMA buffer *must* be in lower 16MB\n");
 		return kmem_start;
 	}
 #endif
 
 	/* If we got this far, install driver functions */
-	if (register_chrdev(QIC02_TAPE_MAJOR, TPQIC_NAME, &tape_qic02_fops)) {
-		printk(TPQIC_NAME ": Unable to get chrdev major %d\n",
-		       QIC02_TAPE_MAJOR);
+	if (register_chrdev(QIC02_TAPE_MAJOR, TPQIC02_NAME, &qic02_tape_fops)) {
+		printk(TPQIC02_NAME ": Unable to get chrdev major %d\n", QIC02_TAPE_MAJOR);
+#ifndef CONFIG_QIC02_DYNCONF
+		free_irq(QIC02_TAPE_IRQ);
+		free_dma(QIC02_TAPE_DMA);
+#endif
 		return kmem_start;
 	}
 
 	/* prepare timer */
 	TIMEROFF;
-	timer_table[TAPE_QIC02_TIMER].expires = 0;
-	timer_table[TAPE_QIC02_TIMER].fn = tape_qic02_times_out;
+	timer_table[QIC02_TAPE_TIMER].expires = 0;
+	timer_table[QIC02_TAPE_TIMER].fn = qic02_tape_times_out;
 
+#ifndef CONFIG_QIC02_DYNCONF
 	if (tape_reset(0)!=TE_OK || tp_sense(TP_WRP|TP_POR|TP_CNI)!=TE_OK) {
+		/* No drive detected, so vanish */
+		tpqputs(TPQD_ALWAYS, "No drive detected -- driver going on vacation...");
 		status_dead = YES;
+		free_irq(QIC02_TAPE_IRQ);
+		free_dma(QIC02_TAPE_DMA);
+		unregister_chrdev(QIC02_TAPE_MAJOR, TPQIC02_NAME);
+		return kmem_start;
 	} else {
 		if (is_exception()) {
-			tpqputs("exception detected\n");
+			tpqputs(TPQD_ALWAYS, "exception detected\n");
 			(void) tp_sense(TP_WRP|TP_POR|TP_CNI);
 		}
 	}
+#endif
 
 	/* initialize generic status for ioctl requests */
 
-	ioctl_status.mt_type	= TAPE_QIC02_DRIVE;	/* MT_IS* id nr */
+	ioctl_status.mt_type	= QIC02_TAPE_DRIVE;	/* MT_IS* id nr */
 
 	ioctl_status.mt_resid	= 0;	/* ---residual count */
 	ioctl_status.mt_gstat	= 0;	/* ---generic status */
@@ -2616,7 +2981,7 @@
 	ioctl_status.mt_blkno	= 0;	/* number of current (logical) block */
 
 	return kmem_start;
-} /* tape_qic02_init */
+} /* qic02_tape_init */
 
 
-#endif /* CONFIG_TAPE_QIC02 */
+#endif /* CONFIG_QIC02_TAPE */
diff -u --recursive --new-file v1.1.34/linux/drivers/char/tty_io.c linux/drivers/char/tty_io.c
--- v1.1.34/linux/drivers/char/tty_io.c	Fri Jul 22 16:41:01 1994
+++ linux/drivers/char/tty_io.c	Sun Jul 24 17:39:43 1994
@@ -69,7 +69,7 @@
 extern int set_selection(const int arg, struct tty_struct *tty);
 extern int paste_selection(struct tty_struct *tty);
 extern int sel_loadlut(const int arg);
-extern int mouse_reporting_p(void);
+extern int mouse_reporting(void);
 extern int shift_state;
 #endif /* CONFIG_SELECTION */
 extern int do_screendump(int arg);
@@ -1394,7 +1394,8 @@
 					put_fs_byte(shift_state,arg);
 					return 0;
 				case 7:
-					return mouse_reporting_p();
+					put_fs_byte(mouse_reporting(),arg);
+					return 0;
 #endif /* CONFIG_SELECTION */
 				default: 
 					return -EINVAL;
diff -u --recursive --new-file v1.1.34/linux/drivers/net/auto_irq.c linux/drivers/net/auto_irq.c
--- v1.1.34/linux/drivers/net/auto_irq.c	Tue Apr 19 10:53:43 1994
+++ linux/drivers/net/auto_irq.c	Sun Jul 24 17:44:45 1994
@@ -62,8 +62,6 @@
     int i, mask;
     int timeout = jiffies+waittime;
 
-    irq_number = 0;
-    irq_bitmap = 0;
     irq_handled = 0;
     for (i = 0; i < 16; i++) {
 	if (!irqaction(i, &autoirq_sigaction))
@@ -71,6 +69,8 @@
     }
     /* Update our USED lists. */
     irqs_used |= ~irq_handled;
+    irq_number = 0;
+    irq_bitmap = 0;
 
     /* Hang out at least <waittime> jiffies waiting for bogus IRQ hits. */
     while (timeout > jiffies)
diff -u --recursive --new-file v1.1.34/linux/drivers/scsi/ultrastor.c linux/drivers/scsi/ultrastor.c
--- v1.1.34/linux/drivers/scsi/ultrastor.c	Thu Jul 14 00:48:25 1994
+++ linux/drivers/scsi/ultrastor.c	Sat Jul 23 13:26:24 1994
@@ -305,7 +305,7 @@
 				  volatile unsigned char *mem)
 {
   asm("xchgb %0,%1" :
-      "=r" (reg), "=m" (*(unsigned char *)mem) :
+      "=q" (reg), "=m" (*(unsigned char *)mem) :
       "0" (reg), "1" (*(unsigned char *)mem));
   return reg;
 }
diff -u --recursive --new-file v1.1.34/linux/fs/Makefile linux/fs/Makefile
--- v1.1.34/linux/fs/Makefile	Tue May 24 08:47:06 1994
+++ linux/fs/Makefile	Sun Jul 24 17:39:43 1994
@@ -33,6 +33,9 @@
 ifdef CONFIG_XIA_FS
 FS_SUBDIRS := $(FS_SUBDIRS) xiafs
 endif
+ifdef CONFIG_UMSDOS_FS
+FS_SUBDIRS := $(FS_SUBDIRS) umsdos
+endif
 ifdef CONFIG_SYSV_FS
 FS_SUBDIRS := $(FS_SUBDIRS) sysv
 endif
diff -u --recursive --new-file v1.1.34/linux/fs/filesystems.c linux/fs/filesystems.c
--- v1.1.34/linux/fs/filesystems.c	Mon Apr 25 10:04:33 1994
+++ linux/fs/filesystems.c	Sun Jul 24 17:39:44 1994
@@ -14,6 +14,7 @@
 #include <linux/ext2_fs.h>
 #include <linux/xia_fs.h>
 #include <linux/msdos_fs.h>
+#include <linux/umsdos_fs.h>
 #include <linux/proc_fs.h>
 #include <linux/nfs_fs.h>
 #include <linux/iso_fs.h>
@@ -51,6 +52,10 @@
 #ifdef CONFIG_XIA_FS
 	register_filesystem(&(struct file_system_type)
 		{xiafs_read_super, "xiafs", 1, NULL});
+#endif
+#ifdef CONFIG_UMSDOS_FS
+	register_filesystem(&(struct file_system_type)
+	{UMSDOS_read_super,	"umsdos",	1, NULL});
 #endif
 
 #ifdef CONFIG_MSDOS_FS
diff -u --recursive --new-file v1.1.34/linux/fs/msdos/Makefile linux/fs/msdos/Makefile
--- v1.1.34/linux/fs/msdos/Makefile	Wed Dec  1 14:44:15 1993
+++ linux/fs/msdos/Makefile	Sun Jul 24 17:39:44 1994
@@ -14,7 +14,7 @@
 .s.o:
 	$(AS) -o $*.o $<
 
-OBJS=	namei.o inode.o file.o dir.o misc.o fat.o
+OBJS=	namei.o inode.o file.o dir.o misc.o fat.o mmap.o
 
 msdos.o: $(OBJS)
 	$(LD) -r -o msdos.o $(OBJS)
diff -u --recursive --new-file v1.1.34/linux/fs/msdos/dir.c linux/fs/msdos/dir.c
--- v1.1.34/linux/fs/msdos/dir.c	Wed Dec  1 14:44:15 1993
+++ linux/fs/msdos/dir.c	Sun Jul 24 17:39:44 1994
@@ -12,6 +12,7 @@
 #include <linux/msdos_fs.h>
 #include <linux/errno.h>
 #include <linux/stat.h>
+#include <linux/string.h>
 
 
 static int msdos_dir_read(struct inode * inode,struct file * filp, char * buf,int count)
@@ -19,7 +20,7 @@
 	return -EISDIR;
 }
 
-static int msdos_readdir(struct inode *inode,struct file *filp,
+int msdos_readdir(struct inode *inode,struct file *filp,
     struct dirent *dirent,int count);
 
 
@@ -54,8 +55,11 @@
 	NULL			/* permission */
 };
 
-static int msdos_readdir(struct inode *inode,struct file *filp,
-    struct dirent *dirent,int count)
+int msdos_readdir(
+	struct inode *inode,
+	struct file *filp,
+	struct dirent *dirent,	/* dirent in user space */
+	int count)
 {
 	int ino,i,i2,last;
 	char c,*walk;
@@ -67,33 +71,39 @@
 /* Fake . and .. for the root directory. */
 		if (filp->f_pos == 2) filp->f_pos = 0;
 		else if (filp->f_pos < 2) {
-				walk = filp->f_pos++ ? ".." : ".";
-				for (i = 0; *walk; walk++)
-					put_fs_byte(*walk,dirent->d_name+i++);
-				put_fs_long(MSDOS_ROOT_INO,&dirent->d_ino);
-				put_fs_byte(0,dirent->d_name+i);
-				put_fs_word(i,&dirent->d_reclen);
-				return i;
-			}
+			walk = filp->f_pos++ ? ".." : ".";
+			for (i = 0; *walk; walk++)
+				put_fs_byte(*walk,dirent->d_name+i++);
+			put_fs_long(MSDOS_ROOT_INO,&dirent->d_ino);
+			put_fs_byte(0,dirent->d_name+i);
+			put_fs_word(i,&dirent->d_reclen);
+			return i;
+		}
 	}
 	if (filp->f_pos & (sizeof(struct msdos_dir_entry)-1)) return -ENOENT;
 	bh = NULL;
 	while ((ino = msdos_get_entry(inode,&filp->f_pos,&bh,&de)) > -1) {
 		if (!IS_FREE(de->name) && !(de->attr & ATTR_VOLUME)) {
+			char bufname[13];
+			char *ptname = bufname;
 			for (i = last = 0; i < 8; i++) {
 				if (!(c = de->name[i])) break;
 				if (c >= 'A' && c <= 'Z') c += 32;
-				if (c != ' ') last = i+1;
-				put_fs_byte(c,i+dirent->d_name);
+				if (c != ' '){
+					last = i+1;
+					*ptname++ = c;
+				}
 			}
 			i = last;
-			put_fs_byte('.',i+dirent->d_name);
+			*ptname++ = '.';
 			i++;
 			for (i2 = 0; i2 < 3; i2++) {
 				if (!(c = de->ext[i2])) break;
 				if (c >= 'A' && c <= 'Z') c += 32;
-				if (c != ' ') last = i+1;
-				put_fs_byte(c,i+dirent->d_name);
+				if (c != ' '){
+					last = i+1;
+					*ptname++ = c;
+				}
 				i++;
 			}
 			if ((i = last) != 0) {
@@ -101,8 +111,9 @@
 					ino = inode->i_ino;
 				else if (!strcmp(de->name,MSDOS_DOTDOT))
 						ino = msdos_parent_ino(inode,0);
+				bufname[i] = '\0';
 				put_fs_long(ino,&dirent->d_ino);
-				put_fs_byte(0,i+dirent->d_name);
+				memcpy_tofs(dirent->d_name,bufname,i+1);
 				put_fs_word(i,&dirent->d_reclen);
 				brelse(bh);
 				return i;
diff -u --recursive --new-file v1.1.34/linux/fs/msdos/file.c linux/fs/msdos/file.c
--- v1.1.34/linux/fs/msdos/file.c	Tue Apr 19 10:53:24 1994
+++ linux/fs/msdos/file.c	Sun Jul 24 17:39:44 1994
@@ -15,16 +15,11 @@
 #include <linux/errno.h>
 #include <linux/fcntl.h>
 #include <linux/stat.h>
+#include <linux/string.h>
 
 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
 
-static int msdos_file_read(struct inode *inode,struct file *filp,char *buf,
-    int count);
-static int msdos_file_write(struct inode *inode,struct file *filp,char *buf,
-    int count);
-
-
 static struct file_operations msdos_file_operations = {
 	NULL,			/* lseek - default */
 	msdos_file_read,	/* read */
@@ -32,7 +27,7 @@
 	NULL,			/* readdir - bad */
 	NULL,			/* select - default */
 	NULL,			/* ioctl - default */
-	NULL,			/* mmap */
+	msdos_mmap,		/* mmap */
 	NULL,			/* no special open is needed */
 	NULL,			/* release */
 	file_fsync		/* fsync */
@@ -53,7 +48,8 @@
 	NULL,			/* follow_link */
 	msdos_bmap,		/* bmap */
 	msdos_truncate,		/* truncate */
-	NULL			/* permission */
+	NULL,			/* permission */
+	msdos_smap		/* smap */
 };
 
 /* No bmap for MS-DOS FS' that don't align data at kByte boundaries. */
@@ -73,12 +69,19 @@
 	NULL,			/* follow_link */
 	NULL,			/* bmap */
 	msdos_truncate,		/* truncate */
-	NULL			/* permission */
+	NULL,			/* permission */
+	msdos_smap		/* smap */
 };
 
 
-static int msdos_file_read(struct inode *inode,struct file *filp,char *buf,
-    int count)
+/*
+	Read a file into user space
+*/
+int msdos_file_read(
+	struct inode *inode,
+	struct file *filp,
+	char *buf,
+	int count)
 {
 	char *start;
 	int left,offset,size,sector,cnt;
@@ -91,7 +94,8 @@
 		printk("msdos_file_read: inode = NULL\n");
 		return -EINVAL;
 	}
-	if (!S_ISREG(inode->i_mode)) {
+	/* S_ISLNK allows for UMSDOS. Should never happen for normal MSDOS */
+	if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode)) {
 		printk("msdos_file_read: mode = %07o\n",inode->i_mode);
 		return -EINVAL;
 	}
@@ -131,9 +135,14 @@
 	return buf-start;
 }
 
-
-static int msdos_file_write(struct inode *inode,struct file *filp,char *buf,
-    int count)
+/*
+	Write to a file either from user space
+*/
+int msdos_file_write(
+	struct inode *inode,
+	struct file *filp,
+	char *buf,
+	int count)
 {
 	int sector,offset,size,left,written;
 	int error,carry;
@@ -145,7 +154,8 @@
 		printk("msdos_file_write: inode = NULL\n");
 		return -EINVAL;
 	}
-	if (!S_ISREG(inode->i_mode)) {
+	/* S_ISLNK allows for UMSDOS. Should never happen for normal MSDOS */
+	if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode)) {
 		printk("msdos_file_write: mode = %07o\n",inode->i_mode);
 		return -EINVAL;
 	}
@@ -210,7 +220,6 @@
 	inode->i_dirt = 1;
 	return buf-start;
 }
-
 
 void msdos_truncate(struct inode *inode)
 {
diff -u --recursive --new-file v1.1.34/linux/fs/msdos/mmap.c linux/fs/msdos/mmap.c
--- v1.1.34/linux/fs/msdos/mmap.c	Thu Jan  1 02:00:00 1970
+++ linux/fs/msdos/mmap.c	Sun Jul 24 17:39:44 1994
@@ -0,0 +1,123 @@
+/*
+ *	fs/msdos/mmap.c
+ *
+ *	Written by Jacques Gelinas (jacques@solucorp.qc.ca)
+ *	Inspired by fs/nfs/mmap.c (Jaon Tombs 15 Aug 1993)
+ *
+ *	msdos mmap handling
+ */
+#include <linux/stat.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/shm.h>
+#include <linux/errno.h>
+#include <linux/mman.h>
+#include <linux/string.h>
+#include <linux/malloc.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <linux/msdos_fs.h>
+
+/*
+ * Fill in the supplied page for mmap
+ */
+static unsigned long msdos_file_mmap_nopage(
+	struct vm_area_struct * area,
+	unsigned long address,
+	unsigned long page,
+	int error_code)
+{
+	struct inode * inode = area->vm_inode;
+	unsigned int clear;
+	int pos;
+	long gap;	/* distance from eof to pos */
+
+	address &= PAGE_MASK;
+	pos = address - area->vm_start + area->vm_offset;
+
+	clear = 0;
+	gap = inode->i_size - pos;
+	if (gap <= 0){
+		/* mmaping beyong end of file */
+		clear = PAGE_SIZE;
+	}else{
+		int cur_read;
+		int need_read;
+		struct file filp;
+		if (gap < PAGE_SIZE){
+			clear = PAGE_SIZE - gap;
+		}
+		filp.f_pos = pos;
+		need_read = PAGE_SIZE - clear;
+		{
+			unsigned long cur_fs = get_fs();
+			set_fs (KERNEL_DS);
+			cur_read = msdos_file_read (inode,&filp,(char*)page
+				,need_read);
+			set_fs (cur_fs);
+		}
+		if (cur_read != need_read){
+			printk ("MSDOS: Error while reading an mmap file %d <> %d\n"
+				,cur_read,need_read);
+		}
+	}
+	if (clear > 0){
+		memset ((char*)page+PAGE_SIZE-clear,0,clear);
+	}
+	return page;
+}
+
+struct vm_operations_struct msdos_file_mmap = {
+	NULL,			/* open */
+	NULL,			/* close */
+	msdos_file_mmap_nopage,	/* nopage */
+	NULL,			/* wppage */
+	NULL,			/* share */
+	NULL,			/* unmap */
+};
+
+/*
+ * This is used for a general mmap of an msdos file
+ * Returns 0 if ok, or a negative error code if not.
+ */
+int msdos_mmap(
+	struct inode * inode,
+	struct file * file,
+	unsigned long addr,
+	size_t len,
+	int prot,
+	unsigned long off)
+{
+	struct vm_area_struct * mpnt;
+
+	if (prot & PAGE_RW)	/* only PAGE_COW or read-only supported now */
+		return -EINVAL;
+	if (off & (inode->i_sb->s_blocksize - 1))
+		return -EINVAL;
+	if (!inode->i_sb || !S_ISREG(inode->i_mode))
+		return -EACCES;
+	if (!IS_RDONLY(inode)) {
+		inode->i_atime = CURRENT_TIME;
+		inode->i_dirt = 1;
+	}
+
+	mpnt = (struct vm_area_struct * ) kmalloc(sizeof(struct vm_area_struct), GFP_KERNEL);
+	if (!mpnt)
+		return -ENOMEM;
+
+	unmap_page_range(addr, len);
+	mpnt->vm_task = current;
+	mpnt->vm_start = addr;
+	mpnt->vm_end = addr + len;
+	mpnt->vm_page_prot = prot;
+	mpnt->vm_share = NULL;
+	mpnt->vm_inode = inode;
+	inode->i_count++;
+	mpnt->vm_offset = off;
+	mpnt->vm_ops = &msdos_file_mmap;
+	insert_vm_struct (current,mpnt);
+	merge_segments (current->mm->mmap,NULL,NULL);
+	return 0;
+}
+
diff -u --recursive --new-file v1.1.34/linux/fs/msdos/namei.c linux/fs/msdos/namei.c
--- v1.1.34/linux/fs/msdos/namei.c	Tue Apr 19 10:53:24 1994
+++ linux/fs/msdos/namei.c	Sun Jul 24 17:39:44 1994
@@ -360,7 +360,11 @@
 }
 
 
-int msdos_unlink(struct inode *dir,const char *name,int len)
+static int msdos_unlinkx(
+	struct inode *dir,
+	const char *name,
+	int len,
+	int nospc)	/* Flag special file ? */
 {
 	int res,ino;
 	struct buffer_head *bh;
@@ -375,7 +379,7 @@
 		res = -ENOENT;
 		goto unlink_done;
 	}
-	if (!S_ISREG(inode->i_mode)) {
+	if (!S_ISREG(inode->i_mode) && nospc){
 		res = -EPERM;
 		goto unlink_done;
 	}
@@ -392,6 +396,17 @@
 	return res;
 }
 
+int msdos_unlink(struct inode *dir,const char *name,int len)
+{
+	return msdos_unlinkx (dir,name,len,1);
+}
+/*
+	Special entry for umsdos
+*/
+int msdos_unlink_umsdos(struct inode *dir,const char *name,int len)
+{
+	return msdos_unlinkx (dir,name,len,0);
+}
 
 static int rename_same_dir(struct inode *old_dir,char *old_name,
     struct inode *new_dir,char *new_name,struct buffer_head *old_bh,
diff -u --recursive --new-file v1.1.34/linux/fs/umsdos/Makefile linux/fs/umsdos/Makefile
--- v1.1.34/linux/fs/umsdos/Makefile	Thu Jan  1 02:00:00 1970
+++ linux/fs/umsdos/Makefile	Sun Jul 24 17:39:44 1994
@@ -0,0 +1,40 @@
+#
+# Makefile for the umsdos unix-like filesystem routines.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+.c.s:
+	$(CC) $(CFLAGS) -S $<
+.c.o:
+	$(CC) $(CFLAGS) -c $<
+.s.o:
+	$(AS) -o $*.o $<
+
+OBJS=	dir.o emd.o file.o inode.o ioctl.o mangle.o namei.o\
+	rdir.o symlink.o #check.o
+
+umsdos.o: $(OBJS)
+	$(LD) -r -o umsdos.o $(OBJS)
+
+clean:
+	rm -f core *.o *.a *.s
+
+dep:
+	$(CPP) -M *.c > .depend
+
+p:
+	proto *.c >/usr/include/linux/umsdos_fs.p
+
+doc:
+	nadoc -i -p umsdos.doc - /tmp/umsdos.mpg
+
+#
+# include a dependency file if one exists
+#
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
diff -u --recursive --new-file v1.1.34/linux/fs/umsdos/README linux/fs/umsdos/README
--- v1.1.34/linux/fs/umsdos/README	Thu Jan  1 02:00:00 1970
+++ linux/fs/umsdos/README	Sun Jul 24 17:39:44 1994
@@ -0,0 +1,84 @@
+Very short explanation for the impatient!!!
+
+Umsdos is a file system driver that run on top the MSDOS fs driver.
+It is written by Jacques Gelinas (jacques@solucorp.qc.ca)
+
+Umsdos is not a file system per se, but a twist to make a boring
+one into a useful one.
+
+It gives you:
+
+	long file name
+	Permisssions and owner
+	Links
+	Special files (devices, pipe...)
+	All is need to be a linux root fs.
+
+There is plenty of documentation on it in the source. A formated document
+made from those comments is available from
+sunsite.unc.edu:/pub/Linux/ALPHA/umsdos
+
+Mostly...
+
+You mount a DOS partition like this
+
+mount -t umsdos /dev/hda3 /mnt
+         ^
+---------|
+
+All option are passed to the msdos drivers. Option like uid,gid etc are
+given to msdos.
+
+The default behavior of Umsdos is to do the same thing as the msdos driver
+mostly passing commands to it without much processing. Again, this is
+the default. After doing the mount on a DOS partition, nothing special
+happen. This is why all mount options are passed to the Msdos fs driver.
+
+Umsdos use a special DOS file --linux-.--- to store the information
+which can't be handle by the normal MsDOS file system. This is the trick.
+
+--linux-.--- is optionnal. There is one per directory.
+
+**** If --linux-.--- is missing, then Umsdos process the directory the
+     same way the msdos driver do. Short file name, no goodies, default
+     owner and permissions. So each directory may have or not this
+     --linux-.---
+
+Now, how to get those --linux-.---.
+
+\begin joke_section
+
+	Well send me a directory content
+	and I will send you one customised for you.
+	$5 per directory. Add any applicable taxes.
+\end joke_section
+
+A utility umssync creates those and maintain them. It is available
+from the same directory above (sunsite) in the file umsdos_progs-0.3.tar.gz.
+A compiled version is available in umsdos-0.3a.bin.tar.gz.
+
+So in our example, after mounting mnt, we do
+
+umssync .
+
+This will promote this directory (a recursive option is available) to full
+umsdos capabilities (long name ...). A ls -l before and after won't show
+much difference however. The file which were there are still there. But now
+you can do all this:
+
+	chmod 644 *
+	chown you.your_groupe *
+	ls >THIS_IS.A.VERY.LONG.NAME
+	ln -s toto tata
+	ls -l
+
+Once a directory is promoted, all subdirectory created will inherit that
+promotion.
+
+What happen if you boot DOS and create files in those promoted directories ?
+Umsdos won't notice new files, but will signal removed file (it won't crash).
+Using umssync in /etc/rc will make sure the DOS directory is in sync with
+the --linux-.---.
+
+Hope this helps!
+
diff -u --recursive --new-file v1.1.34/linux/fs/umsdos/check.c linux/fs/umsdos/check.c
--- v1.1.34/linux/fs/umsdos/check.c	Thu Jan  1 02:00:00 1970
+++ linux/fs/umsdos/check.c	Sun Jul 24 17:39:44 1994
@@ -0,0 +1,55 @@
+#include <asm/system.h>
+
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/head.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/ptrace.h>
+#include <linux/mman.h>
+
+extern unsigned long high_memory;
+
+static int check_one_table(unsigned long * page_dir)
+{
+	unsigned long pg_table = *page_dir;
+
+	if (!pg_table)
+		return 0;
+	if (pg_table >= high_memory || !(pg_table & PAGE_PRESENT)) {
+		return 1;
+	}
+	return 0;
+}
+
+/*
+ * This function frees up all page tables of a process when it exits.
+ */
+void check_page_tables(void)
+{
+	unsigned long pg_dir;
+	static int err = 0;
+
+	int stack_level = (long)(&pg_dir)-current->kernel_stack_page;
+	if (stack_level < 1500) printk ("** %d ** ",stack_level);
+	pg_dir = current->tss.cr3;
+	if (mem_map[MAP_NR(pg_dir)] > 1) {
+		return;
+	}
+	if (err == 0){
+		unsigned long *page_dir = (unsigned long *) pg_dir;
+		unsigned long *base = page_dir;
+		int i;
+		for (i = 0 ; i < PTRS_PER_PAGE ; i++,page_dir++){
+			int notok = check_one_table(page_dir);
+			if (notok){
+				err++;
+				printk ("|%d| ",page_dir-base);
+			}
+		}
+		if (err) printk ("Erreur MM %d\n",err);
+	}
+}
+
diff -u --recursive --new-file v1.1.34/linux/fs/umsdos/dir.c linux/fs/umsdos/dir.c
--- v1.1.34/linux/fs/umsdos/dir.c	Thu Jan  1 02:00:00 1970
+++ linux/fs/umsdos/dir.c	Sun Jul 24 17:39:44 1994
@@ -0,0 +1,703 @@
+/*
+ *  linux/fs/umsdos/dir.c
+ *
+ *  Written 1993 by Jacques Gelinas
+ *	Inspired from linux/fs/msdos/... : Werner Almesberger
+ *
+ *  Extended MS-DOS directory handling functions
+ */
+
+#include <asm/segment.h>
+
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/fs.h>
+#include <linux/msdos_fs.h>
+#include <linux/errno.h>
+#include <linux/stat.h>
+#include <linux/limits.h>
+#include <linux/umsdos_fs.h>
+#include <linux/malloc.h>
+
+#define PRINTK(x)
+#define Printk(x) printk x
+
+#define UMSDOS_SPECIAL_DIRFPOS	3
+extern struct inode *pseudo_root;
+/*
+	So  grep *  doesn't complain in the presence of directories.
+*/
+int UMSDOS_dir_read(struct inode *inode,struct file *filp,char *buf,
+    int count)
+{
+	return -EISDIR;
+}
+/*
+	Read count directory entries from directory filp
+	Return a negative value from linux/errno.h.
+	Return > 0 if success (the length of the file name).
+
+	This function is used by the normal readdir VFS entry point and by
+	some function who try to find out info on a file from a pure MSDOS
+	inode. See umsdos_locate_ancestor() below.
+*/
+static int umsdos_readdir_x(
+	struct inode *dir,		/* Point to a description of the super block */
+	struct file *filp,		/* Point to a directory which is read */
+    struct dirent *dirent,	/* Will hold count directory entry */
+	int dirent_in_fs,		/* dirent point in user's space ? */
+	int count,
+	struct umsdos_dirent *u_entry,	/* Optionnal umsdos entry */
+	int follow_hlink,
+	off_t *pt_f_pos)		/* will hold the offset of the entry in EMD */
+{
+	int ret = 0;
+	
+	umsdos_startlookup(dir);	
+	if (filp->f_pos == UMSDOS_SPECIAL_DIRFPOS
+		&& dir == pseudo_root
+		&& dirent_in_fs){
+		/*
+			We don't need to simulate this pseudo directory
+			when umsdos_readdir_x is called for internal operation
+			of umsdos. This is why dirent_in_fs is tested
+		*/
+		/* #Specification: pseudo root / directory /DOS
+			When umsdos operates in pseudo root mode (C:\linux is the
+			linux root), it simulate a directory /DOS which points to
+			the real root of the file system.
+		*/
+		put_fs_long(dir->i_sb->s_mounted->i_ino,&dirent->d_ino);
+		memcpy_tofs (dirent->d_name,"DOS",3);
+		put_fs_byte(0,dirent->d_name+3);
+		put_fs_word (3,&dirent->d_reclen);
+		if (u_entry != NULL) u_entry->flags = 0;
+		ret = 3;
+		filp->f_pos++;
+	}else if (filp->f_pos < 2
+		|| (dir != dir->i_sb->s_mounted && filp->f_pos == 32)){
+		/* #Specification: readdir / . and ..
+			The msdos filesystem manage the . and .. entry properly
+			so the EMD file won't hold any info about it.
+
+			In readdir, we assume that for the root directory
+			the read position will be 0 for ".", 1 for "..". For
+			a non root directory, the read position will be 0 for "."
+			and 32 for "..".
+		*/
+		/*
+			This is a trick used by the msdos file system (fs/msdos/dir.c)
+			to manage . and .. for the root directory of a file system.
+			Since there is no such entry in the root, fs/msdos/dir.c
+			use the following:
+
+			if f_pos == 0, return ".".
+			if f_pos == 1, return "..".
+
+			So let msdos handle it
+
+			Since umsdos entries are much larger, we share the same f_pos.
+			if f_pos is 0 or 1 or 32, we are clearly looking at . and
+			..
+
+			As soon as we get f_pos == 2 or f_pos == 64, then back to
+			0, but this time we are reading the EMD file.
+
+			Well, not so true. The problem, is that UMSDOS_REC_SIZE is
+			also 64, so as soon as we read the first record in the
+			EMD, we are back at offset 64. So we set the offset
+			to UMSDOS_SPECIAL_DIRFPOS(3) as soon as we have read the
+			.. entry from msdos.
+		*/
+		ret = msdos_readdir(dir,filp,dirent,count);
+		if (filp->f_pos == 64) filp->f_pos = UMSDOS_SPECIAL_DIRFPOS;
+		if (u_entry != NULL) u_entry->flags = 0;
+	}else{
+		struct inode *emd_dir = umsdos_emd_dir_lookup(dir,0);
+		if (emd_dir != NULL){
+			if (filp->f_pos <= UMSDOS_SPECIAL_DIRFPOS+1) filp->f_pos = 0;
+			PRINTK (("f_pos %ld i_size %d\n",filp->f_pos,emd_dir->i_size));
+			ret = 0;
+			while (filp->f_pos < emd_dir->i_size){
+				struct umsdos_dirent entry;
+				off_t cur_f_pos = filp->f_pos;
+				if (umsdos_emd_dir_readentry (emd_dir,filp,&entry)!=0){
+					ret = -EIO;
+					break;
+				}else if (entry.name_len != 0){
+					/* #Specification: umsdos / readdir
+						umsdos_readdir() should fill a struct dirent with
+						an inode number. The cheap way to get it is to
+						do a lookup in the MSDOS directory for each
+						entry processed by the readdir() function.
+						This is not very efficient, but very simple. The
+						other way around is to maintain a copy of the inode
+						number in the EMD file. This is a problem because
+						this has to be maintained in sync using tricks.
+						Remember that MSDOS (the OS) does not update the
+						modification time (mtime) of a directory. There is
+						no easy way to tell that a directory was modified
+						during a DOS session and synchronise the EMD file.
+
+						Suggestion welcome.
+
+						So the easy way is used!
+					*/
+					struct umsdos_info info;
+					struct inode *inode;
+					int lret;
+					umsdos_parse (entry.name,entry.name_len,&info);
+					info.f_pos = cur_f_pos;
+					*pt_f_pos = cur_f_pos;
+					umsdos_manglename (&info);
+					lret = umsdos_real_lookup (dir,info.fake.fname
+						,info.fake.len,&inode);
+					PRINTK (("Cherche inode de %s lret %d flags %d\n"
+						,info.fake.fname,lret,entry.flags));
+					if (lret == 0
+						&& (entry.flags & UMSDOS_HLINK)
+						&& follow_hlink){
+						struct inode *rinode;
+						lret = umsdos_hlink2inode (inode,&rinode);
+						inode = rinode;
+					}
+					if (lret == 0){
+						/* #Specification: pseudo root / reading real root
+							The pseudo root (/linux) is logically
+							erased from the real root. This mean that
+							ls /DOS, won't show "linux". This avoids
+							infinite recursion /DOS/linux/DOS/linux while
+							walking the file system.
+						*/
+						if (inode != pseudo_root){
+							PRINTK (("Trouve ino %d ",inode->i_ino));
+							if (dirent_in_fs){
+								put_fs_long(inode->i_ino,&dirent->d_ino);
+								memcpy_tofs (dirent->d_name,entry.name
+									,entry.name_len);
+								put_fs_byte(0,dirent->d_name+entry.name_len);
+								put_fs_word (entry.name_len
+									,&dirent->d_reclen);
+								/* In this case, the caller only needs */
+								/* flags */
+								if (u_entry != NULL){
+									u_entry->flags = entry.flags;
+								}
+							}else{
+								dirent->d_ino = inode->i_ino;
+								memcpy (dirent->d_name,entry.name
+									,entry.name_len);
+								dirent->d_name[entry.name_len] = '\0';
+								dirent->d_reclen = entry.name_len;
+								if (u_entry != NULL) *u_entry = entry;
+							}
+							ret = entry.name_len;
+							iput (inode);
+							break;
+						}
+						iput (inode);
+					}else{
+						/* #Specification: umsdos / readdir / not in MSDOS
+							During a readdir operation, if the file is not
+							in the MSDOS directory anymore, the entry is
+							removed from the EMD file silently.
+						*/
+						ret = umsdos_writeentry (dir,emd_dir,&info,1);
+						if (ret != 0){
+							break;
+						}
+					}
+				}
+			}
+			iput(emd_dir);
+		}
+	}
+	umsdos_endlookup(dir);	
+	PRINTK (("read dir %p pos %d ret %d\n",dir,filp->f_pos,ret));
+	return ret;
+}
+/*
+	Read count directory entries from directory filp
+	Return a negative value from linux/errno.h.
+	Return > 0 if success (the length of the file name).
+*/
+static int UMSDOS_readdir(
+	struct inode *dir,		/* Point to a description of the super block */
+	struct file *filp,		/* Point to a directory which is read */
+    struct dirent *dirent,	/* Will hold count directory entry */
+	int count)
+{
+	int ret = -ENOENT;
+	while (1){
+		struct umsdos_dirent entry;
+		off_t f_pos;
+		ret = umsdos_readdir_x (dir,filp,dirent,1,count,&entry,1,&f_pos);
+		if (ret <= 0 || !(entry.flags & UMSDOS_HIDDEN)) break;
+	}
+	return ret;
+}
+/*
+	Complete the inode content with info from the EMD file
+*/
+void umsdos_lookup_patch (
+	struct inode *dir,
+	struct inode *inode,
+	struct umsdos_dirent *entry,
+	off_t  emd_pos)
+{
+	/*
+		This function modify the state of a dir inode. It decides
+		if the dir is a umsdos dir or a dos dir. This is done
+		deeper in umsdos_patch_inode() called at the end of this function.
+
+		umsdos_patch_inode() may block because it is doing disk access.
+		At the same time, another process may get here to initialise
+		the same dir inode. There is 3 cases.
+
+		1-The inode is already initialised. We do nothing.
+		2-The inode is not initialised. We lock access and do it.
+		3-Like 2 but another process has lock the inode, so we try
+		  to lock it and right after check if initialisation is still
+		  needed.
+
+
+		Thanks to the mem option of the kernel command line, it was
+		possible to consistently reproduce this problem by limiting
+		my mem to 4 meg and running X.
+	*/
+	/*
+		Do this only if the inode is freshly read, because we will lose
+		the current (updated) content.
+	*/
+	/*
+		A lookup of a mount point directory yield the inode into
+		the other fs, so we don't care about initialising it. iget()
+		does this automaticly.
+	*/
+	if (inode->i_sb == dir->i_sb && !umsdos_isinit(inode)){
+		if (S_ISDIR(inode->i_mode)) umsdos_lockcreate(inode);
+		if (!umsdos_isinit(inode)){
+			/* #Specification: umsdos / lookup / inode info
+				After successfully reading an inode from the MSDOS
+				filesystem, we use the EMD file to complete it.
+				We update the following field.
+
+				uid, gid, atime, ctime, mtime, mode.
+
+				We rely on MSDOS for mtime. If the file
+				was modified during an MSDOS session, at least
+				mtime will be meaningful. We do this only for regular
+				file.
+				
+				We don't rely on MSDOS for mtime for directory because
+				the MSDOS directory date is creation time (strange
+				MSDOS behavior) which fit nowhere in the three UNIX
+				time stamp.
+			*/
+			if (S_ISREG(entry->mode)) entry->mtime = inode->i_mtime;
+			inode->i_mode  = entry->mode;
+			inode->i_rdev  = entry->rdev;
+			inode->i_atime = entry->atime;
+			inode->i_ctime = entry->ctime;
+			inode->i_mtime = entry->mtime;
+			inode->i_uid   = entry->uid;
+			inode->i_gid   = entry->gid;
+			/* #Specification: umsdos / i_nlink
+				The nlink field of an inode is maintain by the MSDOS file system
+				for directory and by UMSDOS for other file. The logic is that
+				MSDOS is already figuring out what to do for directories and
+				does nothing for other files. For MSDOS, there are no hard link
+				so all file carry nlink==1. UMSDOS use some info in the
+				EMD file to plug the correct value.
+			*/
+			if (!S_ISDIR(entry->mode)){
+				if (entry->nlink > 0){
+					inode->i_nlink = entry->nlink;
+				}else{
+					printk ("UMSDOS: lookup_patch entry->nlink < 1 ???\n");
+				}
+			}
+			umsdos_patch_inode(inode,dir,emd_pos);
+		}
+		if (S_ISDIR(inode->i_mode)) umsdos_unlockcreate(inode);
+if (inode->u.umsdos_i.i_emd_owner==0) printk ("emd_owner still 0 ???\n");
+	}
+}
+/*
+	Locate entry of an inode in a directory.
+	Return 0 or a negative error code.
+
+	Normally, this function must succeed. It means a strange corruption
+	in the file system if not.
+*/
+int umsdos_inode2entry (
+	struct inode *dir,
+	struct inode *inode,
+	struct umsdos_dirent *entry)	/* Will hold the entry */
+{
+	int ret = -ENOENT;
+	if (inode == pseudo_root){
+		/*
+			Quick way to find the name.
+			Also umsdos_readdir_x won't show /linux anyway
+		*/
+		memcpy (entry->name,UMSDOS_PSDROOT_NAME,UMSDOS_PSDROOT_LEN+1);
+		entry->name_len = UMSDOS_PSDROOT_LEN;
+		ret = 0;
+	}else{
+		struct inode *emddir = umsdos_emd_dir_lookup(dir,0);
+		iput (emddir);
+		if (emddir == NULL){
+			/* This is a DOS directory */
+			struct file filp;
+			filp.f_pos = 0;
+			while (1){
+				struct dirent dirent;
+				if (umsdos_readdir_kmem (dir,&filp,&dirent,1) <= 0){
+					printk ("UMSDOS: can't locate inode %ld in DOS directory???\n"
+						,inode->i_ino);
+				}else if (dirent.d_ino == inode->i_ino){
+					ret = 0;
+					memcpy (entry->name,dirent.d_name,dirent.d_reclen);
+					entry->name[dirent.d_reclen] = '\0';
+					entry->name_len = dirent.d_reclen;
+					inode->u.umsdos_i.i_dir_owner = dir->i_ino;
+					inode->u.umsdos_i.i_emd_owner = 0;
+					umsdos_setup_dir_inode(inode);
+					break;
+				}
+			}
+		}else{
+			/* skip . and .. see umsdos_readdir_x() */
+			struct file filp;
+			filp.f_pos = UMSDOS_SPECIAL_DIRFPOS;
+			while (1){
+				struct dirent dirent;
+				off_t f_pos;
+				if (umsdos_readdir_x(dir,&filp,&dirent
+					,0,1,entry,0,&f_pos) <= 0){
+					printk ("UMSDOS: can't locate inode %ld in EMD file???\n"
+						,inode->i_ino);
+					break;
+				}else if (dirent.d_ino == inode->i_ino){
+					ret = 0;
+					umsdos_lookup_patch (dir,inode,entry,f_pos);
+					break;
+				}
+			}
+		}
+	}
+	return ret;
+}
+/*
+	Locate the parent of a directory and the info on that directory
+	Return 0 or a negative error code.
+*/
+static int umsdos_locate_ancestor (
+	struct inode *dir,
+	struct inode **result,
+	struct umsdos_dirent *entry)
+{
+	int ret;
+	umsdos_patch_inode (dir,NULL,0);
+	ret = umsdos_real_lookup (dir,"..",2,result);
+	PRINTK (("result %d %x ",ret,*result));
+	if (ret == 0){
+		struct inode *adir = *result;
+		ret = umsdos_inode2entry (adir,dir,entry);
+	}
+	PRINTK (("\n"));
+	return ret;
+}
+/*
+	Build the path name of an inode (relative to the file system.
+	This function is need to set (pseudo) hard link.
+
+	It uses the same strategy as the standard getcwd().
+*/
+int umsdos_locate_path (
+	struct inode *inode,
+	char *path)
+{
+	int ret = 0;
+	struct inode *dir = inode;
+	char *bpath = (char*)kmalloc(PATH_MAX,GFP_KERNEL);
+	if (bpath == NULL){
+		ret = -ENOMEM;
+	}else{
+		struct umsdos_dirent entry;
+		char *ptbpath = bpath+PATH_MAX-1;
+		*ptbpath = '\0';
+		PRINTK (("locate_path mode %x ",inode->i_mode));
+		if (!S_ISDIR(inode->i_mode)){
+			ret = umsdos_get_dirowner (inode,&dir);
+			PRINTK (("locate_path ret %d ",ret));
+			if (ret == 0){
+				ret = umsdos_inode2entry (dir,inode,&entry);
+				if (ret == 0){
+					ptbpath -= entry.name_len;
+					memcpy (ptbpath,entry.name,entry.name_len);
+					PRINTK (("ptbpath :%s: ",ptbpath));
+				}
+			}
+		}else{
+			dir->i_count++;
+		}
+		if (ret == 0){
+			while (dir != dir->i_sb->s_mounted){
+				struct inode *adir;
+				ret = umsdos_locate_ancestor (dir,&adir,&entry);
+				iput (dir);
+				dir = NULL;
+				PRINTK (("ancestor %d ",ret));
+				if (ret == 0){
+					*--ptbpath = '/';
+					ptbpath -= entry.name_len;
+					memcpy (ptbpath,entry.name,entry.name_len);
+					dir = adir;
+					PRINTK (("ptbpath :%s: ",ptbpath));
+				}else{
+					break;
+				}
+			}
+		}
+		strcpy (path,ptbpath);
+		kfree (bpath);
+	}
+	PRINTK (("\n"));
+	iput (dir);
+	return ret;
+}
+
+/*
+	Return != 0 if an entry is the pseudo DOS entry in the pseudo root.
+*/
+int umsdos_is_pseudodos (
+	struct inode *dir,
+	const char *name,
+	int len)
+{
+	/* #Specification: pseudo root / DOS hard coded
+		The pseudo sub-directory DOS in the pseudo root is hard coded.
+		The name is DOS. This is done this way to help standardised
+		the umsdos layout. The idea is that from now on /DOS is
+		a reserved path and nobody will think of using such a path
+		for a package.
+	*/
+	return dir == pseudo_root
+		&& len == 3
+		&& name[0] == 'D' && name[1] == 'O' && name[2] == 'S';
+}
+/*
+	Check if a file exist in the current directory.
+	Return 0 if ok, negative error code if not (ex: -ENOENT).
+*/
+static int umsdos_lookup_x (
+	struct inode *dir,
+	const char *name,
+	int len,
+	struct inode **result,	/* Will hold inode of the file, if successful */
+	int nopseudo)			/* Don't care about pseudo root mode */
+{
+	int ret = -ENOENT;
+	*result = NULL;
+	umsdos_startlookup(dir);	
+	if (len == 1 && name[0] == '.'){
+		*result = dir;
+		dir->i_count++;
+		ret = 0;
+	}else if (len == 2 && name[0] == '.' && name[1] == '.'){
+		if (pseudo_root != NULL && dir == pseudo_root->i_sb->s_mounted){
+			/* #Specification: pseudo root / .. in real root
+				Whenever a lookup is those in the real root for
+				the directory .., and pseudo root is active, the
+				pseudo root is returned.
+			*/
+			ret = 0;
+			*result = pseudo_root;
+			pseudo_root->i_count++;
+		}else{
+			/* #Specification: locating .. / strategy
+				We use the msdos filesystem to locate the parent directory.
+				But it is more complicated than that.
+				
+				We have to step back even further to
+				get the parent of the parent, so we can get the EMD
+				of the parent of the parent. Using the EMD file, we can
+				locate all the info on the parent, such a permissions
+				and owner.
+			*/
+			ret = umsdos_real_lookup (dir,"..",2,result);
+			PRINTK (("ancestor ret %d dir %p *result %p ",ret,dir,*result));
+			if (ret == 0
+				&& *result != dir->i_sb->s_mounted
+				&& *result != pseudo_root){
+				struct inode *aadir;
+				struct umsdos_dirent entry;
+				ret = umsdos_locate_ancestor (*result,&aadir,&entry);
+				iput (aadir);
+			}
+		}
+	}else if (umsdos_is_pseudodos(dir,name,len)){
+		/* #Specification: pseudo root / lookup(DOS)
+			A lookup of DOS in the pseudo root will always succeed
+			and return the inode of the real root.
+		*/
+		*result = dir->i_sb->s_mounted;
+		(*result)->i_count++;
+		ret = 0;
+	}else{
+		struct umsdos_info info;
+		ret = umsdos_parse (name,len,&info);
+		if (ret == 0) ret = umsdos_findentry (dir,&info,0);
+		PRINTK (("lookup %s pos %d ret %d len %d ",info.fake.fname,info.f_pos,ret
+			,info.fake.len));
+		if (ret == 0){
+			/* #Specification: umsdos / lookup
+				A lookup for a file is done in two step. First, we locate
+				the file in the EMD file. If not present, we return
+				an error code (-ENOENT). If it is there, we repeat the
+				operation on the msdos file system. If this fails, it means
+				that the file system is not in sync with the emd file.
+				We silently remove this entry from the emd file,
+				and return ENOENT.
+			*/
+			struct inode *inode;
+			ret = umsdos_real_lookup (dir,info.fake.fname,info.fake.len,result);
+			inode = *result;
+			if (inode == NULL){
+				printk ("UMSDOS: Erase entry %s, out of sync with MsDOS\n"
+					,info.fake.fname);
+				umsdos_delentry (dir,&info,S_ISDIR(info.entry.mode));
+			}else{
+				umsdos_lookup_patch (dir,inode,&info.entry,info.f_pos);
+				PRINTK (("lookup ino %d flags %d\n",inode->i_ino
+					,info.entry.flags));
+				if (info.entry.flags & UMSDOS_HLINK){
+					ret = umsdos_hlink2inode (inode,result);
+				}
+				if (*result == pseudo_root && !nopseudo){
+					/* #Specification: pseudo root / dir lookup
+						For the same reason as readdir, a lookup in /DOS for
+						the pseudo root directory (linux) will fail.
+					*/
+					/*
+						This has to be allowed for resolving hard link
+						which are recorded independantly of the pseudo-root
+						mode.
+					*/
+					iput (pseudo_root);
+					*result = NULL;
+					ret = -ENOENT;
+				}
+			}
+		}
+	}
+	umsdos_endlookup(dir);	
+	iput (dir);
+	return ret;
+}
+/*
+	Check if a file exist in the current directory.
+	Return 0 if ok, negative error code if not (ex: -ENOENT).
+*/
+int UMSDOS_lookup (
+	struct inode *dir,
+	const char *name,
+	int len,
+	struct inode **result)	/* Will hold inode of the file, if successful */
+{
+	return umsdos_lookup_x(dir,name,len,result,0);
+}
+/*
+	Locate the inode pointed by a (pseudo) hard link
+	Return 0 if ok, a negative error code if not.
+*/
+int umsdos_hlink2inode (struct inode *hlink, struct inode **result)
+{
+	int ret = -EIO;
+	char *path = (char*)kmalloc(PATH_MAX,GFP_KERNEL);
+	*result = NULL;
+	if (path == NULL){
+		ret = -ENOMEM;
+		iput (hlink);
+	}else{
+		struct file filp;
+		filp.f_pos = 0;
+		PRINTK (("hlink2inode "));
+		if (umsdos_file_read_kmem (hlink,&filp,path,hlink->i_size)
+			==hlink->i_size){
+			struct inode *dir;
+			char *pt = path;
+			dir = hlink->i_sb->s_mounted;
+			path[hlink->i_size] = '\0';
+			iput (hlink);
+			dir->i_count++;
+			while (1){
+				char *start = pt;
+				int len;
+				while (*pt != '\0' && *pt != '/') pt++;
+				len = (int)(pt - start);
+				if (*pt == '/') *pt++ = '\0';
+				if (dir->u.umsdos_i.i_emd_dir == 0){
+					/* This is a DOS directory */
+					ret = msdos_lookup(dir,start,len,result);
+				}else{
+					ret = umsdos_lookup_x(dir,start,len,result,1);
+				}
+				PRINTK (("h2n lookup :%s: -> %d ",start,ret));
+				if (ret == 0 && *pt != '\0'){
+					dir = *result;
+				}else{
+					break;
+				}
+			}
+		}else{
+			iput (hlink);
+		}
+		PRINTK (("hlink2inode ret = %d %p -> %p\n",ret,hlink,*result));
+		kfree (path);
+	}
+	return ret;
+}
+
+static struct file_operations umsdos_dir_operations = {
+	NULL,				/* lseek - default */
+	UMSDOS_dir_read,	/* read */
+	NULL,				/* write - bad */
+	UMSDOS_readdir,		/* readdir */
+	NULL,				/* select - default */
+	UMSDOS_ioctl_dir,	/* ioctl - default */
+	NULL,				/* mmap */
+	NULL,				/* no special open code */
+	NULL,				/* no special release code */
+	NULL				/* fsync */
+};
+
+struct inode_operations umsdos_dir_inode_operations = {
+	&umsdos_dir_operations,	/* default directory file-ops */
+	UMSDOS_create,		/* create */
+	UMSDOS_lookup,		/* lookup */
+	UMSDOS_link,		/* link */
+	UMSDOS_unlink,		/* unlink */
+	UMSDOS_symlink,		/* symlink */
+	UMSDOS_mkdir,		/* mkdir */
+	UMSDOS_rmdir,		/* rmdir */
+	UMSDOS_mknod,		/* mknod */
+	UMSDOS_rename,		/* rename */
+	NULL,			/* readlink */
+	NULL,			/* follow_link */
+	NULL,			/* bmap */
+	NULL,			/* truncate */
+	NULL			/* permission */
+};
+
+
+
+
+
+
+
+
+
+
diff -u --recursive --new-file v1.1.34/linux/fs/umsdos/emd.c linux/fs/umsdos/emd.c
--- v1.1.34/linux/fs/umsdos/emd.c	Thu Jan  1 02:00:00 1970
+++ linux/fs/umsdos/emd.c	Sun Jul 24 17:39:45 1994
@@ -0,0 +1,499 @@
+/*
+ *  linux/fs/umsdos/emd.c
+ *
+ *  Written 1993 by Jacques Gelinas
+ *
+ *  Extended MS-DOS directory handling functions
+ */
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/kernel.h>
+#include <asm/segment.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/msdos_fs.h>
+#include <linux/umsdos_fs.h>
+
+#define PRINTK(x)
+#define Printk(x) printk x
+
+int umsdos_readdir_kmem(
+	struct inode *inode,
+	struct file *filp,
+	struct dirent *dirent,
+	int count)
+{
+	int ret;
+	set_fs (KERNEL_DS);
+	ret = msdos_readdir(inode,filp,dirent,count);
+	set_fs (USER_DS);
+	return ret;
+}
+/*
+	Read a file into kernel space memory
+*/
+int umsdos_file_read_kmem(
+	struct inode *inode,
+	struct file *filp,
+	char *buf,
+	int count)
+{
+	int ret;	
+	set_fs (KERNEL_DS);
+	ret = msdos_file_read(inode,filp,buf,count);
+	set_fs (USER_DS);
+	return ret;
+}
+/*
+	Write to a file from kernel space
+*/
+int umsdos_file_write_kmem(
+	struct inode *inode,
+	struct file *filp,
+	char *buf,
+	int count)
+{
+	int ret;
+	set_fs (KERNEL_DS);
+	ret = msdos_file_write(inode,filp,buf,count);
+	set_fs (USER_DS);
+	return ret;
+}
+
+
+/*
+	Write a block of bytes into one EMD file.
+	The block of data is NOT in user space.
+
+	Return 0 if ok, a negative error code if not.
+*/
+int umsdos_emd_dir_write (
+	struct inode *emd_dir,
+	struct file *filp,
+	char *buf,	/* buffer in kernel memory, not in user space */
+	int count)
+{
+	int written;
+	filp->f_flags = 0;
+	written = umsdos_file_write_kmem (emd_dir,filp,buf,count);
+	return written != count ? -EIO : 0;
+}
+/*
+	Read a block of bytes from one EMD file.
+	The block of data is NOT in user space.
+	Retourne 0 if ok, -EIO if any error.
+*/
+int umsdos_emd_dir_read (
+	struct inode *emd_dir,
+	struct file *filp,
+	char *buf,	/* buffer in kernel memory, not in user space */
+	int count)
+{
+	int ret = 0;
+	int sizeread;
+	filp->f_flags = 0;
+	sizeread = umsdos_file_read_kmem (emd_dir,filp,buf,count);
+	if (sizeread != count){
+		printk ("UMSDOS: problem with EMD file. Can't read\n");
+		ret = -EIO;
+	}
+	return ret;
+
+}
+/*
+	Locate the EMD file in a directory and optionnally, creates it.
+
+	Return NULL if error. If ok, dir->u.umsdos_i.emd_inode 
+*/
+struct inode *umsdos_emd_dir_lookup(struct inode *dir, int creat)
+{
+	struct inode *ret = NULL;
+	if (dir->u.umsdos_i.i_emd_dir != 0){
+		ret = iget (dir->i_sb,dir->u.umsdos_i.i_emd_dir);
+		PRINTK (("deja trouve %d %x [%d] "
+			,dir->u.umsdos_i.i_emd_dir,ret,ret->i_count));
+	}else{
+		umsdos_real_lookup (dir,UMSDOS_EMD_FILE,UMSDOS_EMD_NAMELEN,&ret);
+		PRINTK (("emd_dir_lookup "));
+		if (ret != NULL){
+			PRINTK (("Find --linux "));
+			dir->u.umsdos_i.i_emd_dir = ret->i_ino;
+		}else if (creat){
+			int code;
+			PRINTK (("avant create "));
+			dir->i_count++;
+			code = msdos_create (dir,UMSDOS_EMD_FILE,UMSDOS_EMD_NAMELEN
+				,S_IFREG|0777,&ret);
+			PRINTK (("Creat EMD code %d ret %x ",code,ret));
+			if (ret != NULL){
+				dir->u.umsdos_i.i_emd_dir = ret->i_ino;
+			}else{
+				printk ("UMSDOS: Can't create EMD file\n");
+			}
+		}
+	}
+	if (ret != NULL){
+		/* Disable UMSDOS_notify_change() for EMD file */
+		ret->u.umsdos_i.i_emd_owner = 0xffffffff;
+	}
+	return ret;
+}
+
+/*
+	Read an entry from the EMD file.
+	Support variable length record.
+	Return -EIO if error, 0 if ok.
+*/
+int umsdos_emd_dir_readentry (
+	struct inode *emd_dir,
+	struct file *filp,
+	struct umsdos_dirent *entry)
+{
+	int ret = umsdos_emd_dir_read(emd_dir,filp,(char*)entry,UMSDOS_REC_SIZE);
+	if (ret == 0){
+		/* Variable size record. Maybe, we have to read some more */
+		int recsize = umsdos_evalrecsize (entry->name_len);
+		if (recsize > UMSDOS_REC_SIZE){
+			ret = umsdos_emd_dir_read(emd_dir,filp
+				,((char*)entry)+UMSDOS_REC_SIZE,recsize - UMSDOS_REC_SIZE);
+			
+		}
+	}
+	return ret;
+}
+/*
+	Write an entry in the EMD file.
+	Return 0 if ok, -EIO if some error.
+*/
+int umsdos_writeentry (
+	struct inode *dir,
+	struct inode *emd_dir,
+	struct umsdos_info *info,
+	int free_entry)		/* This entry is deleted, so Write all 0's */
+{
+	int ret = 0;
+	struct file filp;
+	struct umsdos_dirent *entry = &info->entry;
+	struct umsdos_dirent entry0;
+	if (free_entry){
+		/* #Specification: EMD file / empty entries
+			Unused entry in the EMD file are identify
+			by the name_len field equal to 0. However to
+			help future extension (or bug corretion :-( ),
+			empty entries are filled with 0.
+		*/
+		memset (&entry0,0,sizeof(entry0));
+		entry = &entry0;
+	}else if (entry->name_len > 0){
+		memset (entry->name+entry->name_len,'\0'
+			,sizeof(entry->name)-entry->name_len);
+		/* #Specification: EMD file / spare bytes
+			10 bytes are unused in each record of the EMD. They
+			are set to 0 all the time. So it will be possible
+			to do new stuff and rely on the state of those
+			bytes in old EMD file around.
+		*/
+		memset (entry->spare,0,sizeof(entry->spare));
+	}
+	filp.f_pos = info->f_pos;
+	ret = umsdos_emd_dir_write(emd_dir,&filp,(char*)entry,info->recsize);
+	if (ret != 0){
+		printk ("UMSDOS: problem with EMD file. Can't write\n");
+	}else{
+		dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+		dir->i_dirt = 1;
+	}
+	return ret;
+}
+
+#define CHUNK_SIZE (16*UMSDOS_REC_SIZE)
+struct find_buffer{
+	char buffer[CHUNK_SIZE];
+	int pos;	/* read offset in buffer */
+	int size;	/* Current size of buffer */
+	struct file filp;
+};
+
+/*
+	Fill the read buffer and take care of the byte remaining inside.
+	Unread bytes are simply move to the beginning.
+
+	Return -ENOENT if EOF, 0 if ok, a negativ error code if any problem.
+*/
+static int umsdos_fillbuf (
+	struct inode *inode,
+	struct find_buffer *buf)
+{
+	int ret = -ENOENT;
+	int mustmove = buf->size - buf->pos;
+	int mustread;
+	int remain;
+	if (mustmove > 0){
+		memcpy (buf->buffer,buf->buffer+buf->pos,mustmove);
+	}
+	buf->pos = 0;
+	mustread = CHUNK_SIZE - mustmove;
+	remain = inode->i_size - buf->filp.f_pos;
+	if (remain < mustread) mustread = remain;
+	if (mustread > 0){
+		ret = umsdos_emd_dir_read (inode,&buf->filp,buf->buffer+mustmove
+			,mustread);
+		if (ret == 0) buf->size = mustmove + mustread;		
+	}else if (mustmove){
+		buf->size = mustmove;
+		ret = 0;
+	}
+	return ret;
+}
+
+/*
+	General search, locate a name in the EMD file or an empty slot to
+	store it. if info->entry.name_len == 0, search the first empty
+	slot (of the proper size).
+
+	Caller must do iput on *pt_emd_dir.
+
+	Return 0 if found, -ENOENT if not found, another error code if
+	other problem.
+
+	So this routine is used to either find an existing entry or to
+	create a new one, while making sure it is a new one. After you
+	get -ENOENT, you make sure the entry is stuffed correctly and
+	call umsdos_writeentry().
+
+	To delete an entry, you find it, zero out the entry (memset)
+	and call umsdos_writeentry().
+
+	All this to say that umsdos_writeentry must be call after this
+	function since it rely on the f_pos field of info.
+*/
+static int umsdos_find (
+	struct inode *dir,
+	struct umsdos_info *info,		/* Hold name and name_len */
+									/* Will hold the entry found */
+	struct inode **pt_emd_dir)		/* Will hold the emd_dir inode */
+									/* or NULL if not found */
+{
+	/* #Specification: EMD file structure
+		The EMD file uses a fairly simple layout. It is made of records
+		(UMSDOS_REC_SIZE == 64). When a name can't be written is a single
+		record, multiple contiguous record are allocated.
+	*/
+	int ret = -ENOENT;
+	struct inode *emd_dir = umsdos_emd_dir_lookup(dir,1);
+	if (emd_dir != NULL){
+		struct umsdos_dirent *entry = &info->entry;
+		int recsize = info->recsize;
+		struct {
+			off_t posok;	/* Position available to store the entry */
+			int found;		/* A valid empty position has been found */
+			off_t one;		/* One empty position -> maybe <- large enough */
+			int onesize;	/* size of empty region starting at one */
+		}empty;
+		/* Read several entries at a time to speed up the search */
+		struct find_buffer buf;
+		buf.pos = 0;
+		buf.size = 0;
+		buf.filp.f_pos = 0;
+		empty.found = 0;
+		empty.posok = emd_dir->i_size;
+		empty.onesize = 0;
+		while (1){
+			struct umsdos_dirent *rentry = (struct umsdos_dirent*)
+				(buf.buffer + buf.pos);
+			int file_pos = buf.filp.f_pos - buf.size + buf.pos;
+			if (buf.pos == buf.size){
+				ret = umsdos_fillbuf (emd_dir,&buf);
+				if (ret < 0){
+					/* Not found, so note where it can be added */
+					info->f_pos = empty.posok;
+					break;
+				}
+			}else if (rentry->name_len == 0){
+				/* We are looking for an empty section at least */
+				/* recsize large */
+				if (entry->name_len == 0){
+					info->f_pos = file_pos;
+					ret = 0;
+					break;
+				}else if (!empty.found){
+					if (empty.onesize == 0){
+						/* This is the first empty record of a section */
+						empty.one = file_pos;
+					}
+					/* grow the empty section */
+					empty.onesize += UMSDOS_REC_SIZE;
+					if (empty.onesize == recsize){
+						/* here is a large enough section */
+						empty.posok = empty.one;
+						empty.found = 1;
+					}
+				}
+				buf.pos += UMSDOS_REC_SIZE;
+			}else{
+				int entry_size = umsdos_evalrecsize(rentry->name_len);
+				if (buf.pos+entry_size > buf.size){
+					ret = umsdos_fillbuf (emd_dir,&buf);
+					if (ret < 0){
+						/* Not found, so note where it can be added */
+						info->f_pos = empty.posok;
+						break;
+					}
+				}else{
+					empty.onesize = 0;	/* Reset the free slot search */
+					if (entry->name_len == rentry->name_len
+						&& memcmp(entry->name,rentry->name,rentry->name_len)
+							==0){
+						info->f_pos = file_pos;
+						*entry = *rentry;
+						ret = 0;
+						break;
+					}else{
+						buf.pos += entry_size;
+					}
+				}
+			}	
+		}
+		umsdos_manglename(info);
+	}
+	*pt_emd_dir = emd_dir;
+	return ret;
+}
+/*
+	Add a new entry in the emd file
+	Return 0 if ok or a negative error code.
+	Return -EEXIST if the entry already exist.
+
+	Complete the information missing in info.
+*/
+int umsdos_newentry (
+	struct inode *dir,
+	struct umsdos_info *info)
+{
+	struct inode *emd_dir;
+	int ret = umsdos_find (dir,info,&emd_dir);
+	if (ret == 0){
+		ret = -EEXIST;
+	}else if (ret == -ENOENT){
+		ret = umsdos_writeentry(dir,emd_dir,info,0);
+		PRINTK (("umsdos_newentry EDM ret = %d\n",ret));
+	}
+	iput (emd_dir);
+	return ret;
+}
+/*
+	Create a new hidden link.
+	Return 0 if ok, an error code if not.
+*/
+int umsdos_newhidden (
+	struct inode *dir,
+	struct umsdos_info *info)
+{
+	struct inode *emd_dir;
+	int ret;
+	umsdos_parse ("..LINK",6,info);
+	info->entry.name_len = 0;
+	ret = umsdos_find (dir,info,&emd_dir);
+	iput (emd_dir);
+	if (ret == -ENOENT || ret == 0){
+		/* #Specification: hard link / hidden name
+			When a hard link is created, the original file is renamed
+			to a hidden name. The name is "..LINKNNN" where NNN is a
+			number define from the entry offset in the EMD file.
+		*/
+		info->entry.name_len = sprintf (info->entry.name,"..LINK%ld"
+			,info->f_pos);
+		ret = 0;
+	}
+	return ret;
+}
+/*
+	Remove an entry from the emd file
+	Return 0 if ok, a negative error code otherwise.
+
+	Complete the information missing in info.
+*/
+int umsdos_delentry (
+	struct inode *dir,
+	struct umsdos_info *info,
+	int isdir)
+{
+	struct inode *emd_dir;
+	int ret = umsdos_find (dir,info,&emd_dir);
+	if (ret == 0){
+		if (info->entry.name_len != 0){
+			if ((isdir != 0) != (S_ISDIR(info->entry.mode) != 0)){
+				if (S_ISDIR(info->entry.mode)){
+					ret = -EISDIR;
+				}else{
+					ret = -ENOTDIR;
+				}
+			}else{
+				ret = umsdos_writeentry(dir,emd_dir,info,1);
+			}
+		}
+	}
+	iput(emd_dir);
+	return ret;
+}
+
+
+/*
+	Verify is a EMD directory is empty.
+	Return 0 if not empty
+		   1 if empty
+		   2 if empty, no EMD file.
+*/
+int umsdos_isempty (struct inode *dir)
+{
+	int ret = 2;
+	struct inode *emd_dir = umsdos_emd_dir_lookup(dir,0);
+	/* If the EMD file does not exist, it is certainly empty :-) */
+	if (emd_dir != NULL){
+		struct file filp;
+		/* Find an empty slot */
+		filp.f_pos = 0;
+		filp.f_flags = O_RDONLY;
+		ret = 1;
+		while (filp.f_pos < emd_dir->i_size){
+			struct umsdos_dirent entry;
+			if (umsdos_emd_dir_readentry(emd_dir,&filp,&entry)!=0){
+				ret = 0;
+				break;
+			}else if (entry.name_len != 0){
+				ret = 0;
+				break;
+			}	
+		}
+		iput (emd_dir);
+	}
+	return ret;
+}
+
+/*
+	Locate an entry in a EMD directory.
+	Return 0 if ok, errcod if not, generally -ENOENT.
+*/
+int umsdos_findentry (
+	struct inode *dir,
+	struct umsdos_info *info,
+	int expect)		/* 0: anything */
+					/* 1: file */
+					/* 2: directory */
+{
+	struct inode *emd_dir;
+	int ret = umsdos_find (dir,info,&emd_dir);
+	if (ret == 0){
+		if (expect != 0){
+			if (S_ISDIR(info->entry.mode)){
+				if (expect != 2) ret = -EISDIR;
+			}else if (expect == 2){
+				ret = -ENOTDIR;
+			}
+		}
+	}
+	iput (emd_dir);
+	return ret;
+}
+
diff -u --recursive --new-file v1.1.34/linux/fs/umsdos/file.c linux/fs/umsdos/file.c
--- v1.1.34/linux/fs/umsdos/file.c	Thu Jan  1 02:00:00 1970
+++ linux/fs/umsdos/file.c	Sun Jul 24 17:39:45 1994
@@ -0,0 +1,103 @@
+/*
+ *  linux/fs/umsdos/file.c
+ *
+ *  Written 1993 by Jacques Gelinas
+ *	inpired from linux/fs/msdos/file.c Werner Almesberger
+ *
+ *  Extended MS-DOS regular file handling primitives
+ */
+
+#include <asm/segment.h>
+#include <asm/system.h>
+
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/msdos_fs.h>
+#include <linux/errno.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <linux/msdos_fs.h>
+#include <linux/umsdos_fs.h>
+
+
+#define PRINTK(x)
+#define Printk(x)	printk x
+/*
+	Read a file into user space memory
+*/
+static int UMSDOS_file_read(
+	struct inode *inode,
+	struct file *filp,
+	char *buf,
+    int count)
+{
+	/* We have to set the access time because msdos don't care */
+	int ret = msdos_file_read(inode,filp,buf,count);
+	inode->i_atime = CURRENT_TIME;
+	inode->i_dirt = 1;
+	return ret;
+}
+/*
+	Write a file from user space memory
+*/
+static int UMSDOS_file_write(
+	struct inode *inode,
+	struct file *filp,
+	char *buf,
+    int count)
+{
+	return msdos_file_write(inode,filp,buf,count);
+}
+/*
+	Truncate a file to 0 length.
+*/
+static void UMSDOS_truncate(struct inode *inode)
+{
+	PRINTK (("UMSDOS_truncate\n"));
+	msdos_truncate (inode);
+	inode->i_ctime = inode->i_mtime = CURRENT_TIME;
+	inode->i_dirt = 1;
+}
+/*
+	See inode.c
+	
+	Some entry point are filled dynamicly with function pointers
+	from the msdos file_operations and file_inode_operations.
+	
+	The idea is to have the code as independant as possible from
+	the msdos file system.
+*/
+
+struct file_operations umsdos_file_operations = {
+	NULL,				/* lseek - default */
+	UMSDOS_file_read,	/* read */
+	UMSDOS_file_write,	/* write */
+	NULL,				/* readdir - bad */
+	NULL,				/* select - default */
+	NULL,				/* ioctl - default */
+	msdos_mmap,			/* mmap */
+	NULL,				/* no special open is needed */
+	NULL,				/* release */
+	NULL				/* fsync */
+};
+
+struct inode_operations umsdos_file_inode_operations = {
+	&umsdos_file_operations,	/* default file operations */
+	NULL,			/* create */
+	NULL,			/* lookup */
+	NULL,			/* link */
+	NULL,			/* unlink */
+	NULL,			/* symlink */
+	NULL,			/* mkdir */
+	NULL,			/* rmdir */
+	NULL,			/* mknod */
+	NULL,			/* rename */
+	NULL,			/* readlink */
+	NULL,			/* follow_link */
+	NULL,			/* bmap */
+	UMSDOS_truncate,/* truncate */
+	NULL,			/* permission */
+	msdos_smap		/* smap */
+};
+
+
diff -u --recursive --new-file v1.1.34/linux/fs/umsdos/inode.c linux/fs/umsdos/inode.c
--- v1.1.34/linux/fs/umsdos/inode.c	Thu Jan  1 02:00:00 1970
+++ linux/fs/umsdos/inode.c	Sun Jul 24 17:39:45 1994
@@ -0,0 +1,466 @@
+/*
+ *  linux/fs/umsdos/inode.c
+ *
+ *	Written 1993 by Jacques Gelinas 
+ *	Inspired from linux/fs/msdos/... by Werner Almesberger
+ *
+ */
+
+#include <stdlib.h>
+#include <linux/fs.h>
+#include <linux/msdos_fs.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <asm/segment.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/stat.h>
+#include <linux/umsdos_fs.h>
+
+struct inode *pseudo_root=NULL;		/* Useful to simulate the pseudo DOS */
+									/* directory. See UMSDOS_readdir_x() */
+
+/* #Specification: convention / PRINTK Printk and printk
+	Here is the convention for the use of printk inside fs/umsdos
+
+	printk carry important message (error or status).
+	Printk is for debugging (it is a macro defined at the beginning of
+		   most source.
+	PRINTK is a nulled Printk macro.
+
+	This convention makes the source easier to read, and Printk easier
+	to shut off.
+*/
+#define PRINTK(x)
+#define Printk(x) printk x
+
+
+void UMSDOS_put_inode(struct inode *inode)
+{
+	PRINTK (("put inode %x owner %x pos %d dir %x\n",inode
+		,inode->u.umsdos_i.i_emd_owner,inode->u.umsdos_i.pos
+		,inode->u.umsdos_i.i_emd_dir));
+	msdos_put_inode(inode);
+}
+
+
+void UMSDOS_put_super(struct super_block *sb)
+{
+	msdos_put_super(sb);
+}
+
+
+void UMSDOS_statfs(struct super_block *sb,struct statfs *buf)
+{
+	msdos_statfs(sb,buf);
+}
+
+
+/*
+	Call msdos_lookup, but set back the original msdos function table.
+	Retourne 0 if ok, or a negative error code if not.
+*/
+int umsdos_real_lookup (
+	struct inode *dir,
+	const char *name,
+	int len,
+	struct inode **result)	/* Will hold inode of the file, if successful */
+{
+	int ret;
+	dir->i_count++;
+	ret = msdos_lookup (dir,name,len,result);
+	return ret;
+}
+/*
+	Complete the setup of an directory inode.
+	First, it completes the function pointers, then
+	it locates the EMD file. If the EMD is there, then plug the
+	umsdos function table. If not, use the msdos one.
+*/
+void umsdos_setup_dir_inode (struct inode *inode)
+{
+	inode->u.umsdos_i.i_emd_dir = 0;
+	{
+		struct inode *emd_dir = umsdos_emd_dir_lookup (inode,0);
+		extern struct inode_operations umsdos_rdir_inode_operations;
+		inode->i_op = emd_dir != NULL
+			? &umsdos_dir_inode_operations
+			: &umsdos_rdir_inode_operations;
+		iput (emd_dir);
+	}
+}
+/*
+	Add some info into an inode so it can find its owner quickly
+*/
+void umsdos_set_dirinfo(
+	struct inode *inode,
+	struct inode *dir,
+	off_t f_pos)
+{
+	struct inode *emd_owner = umsdos_emd_dir_lookup(dir,1);
+	inode->u.umsdos_i.i_dir_owner = dir->i_ino;
+	inode->u.umsdos_i.i_emd_owner = emd_owner->i_ino;
+	iput (emd_owner);
+	inode->u.umsdos_i.pos = f_pos;
+}
+/*
+	Tells if an Umsdos inode has been "patched" once.
+	Returne != 0 if so.
+*/
+int umsdos_isinit (struct inode *inode)
+{
+#if	1
+	return inode->u.umsdos_i.i_emd_owner != 0;
+#elif 0
+	return inode->i_atime != 0;
+#else
+	return inode->i_count > 1;
+#endif
+}
+/*
+	Connect the proper tables in the inode and add some info.
+*/
+void umsdos_patch_inode (
+	struct inode *inode,
+	struct inode *dir,		/* May be NULL */
+	off_t f_pos)
+{
+	/*
+		This function is called very early to setup the inode, somewhat
+		too early (called by UMSDOS_read_inode). At this point, we can't
+		do to much, such as lookup up EMD files and so on. This causes
+		confusion in the kernel. This is why some initialisation
+		will be done when dir != NULL only.
+
+		UMSDOS do run piggy back on top of msdos fs. It looks like something
+		is missing in the VFS to accomodate stacked fs. Still unclear what
+		(quite honestly).
+
+		Well, maybe one! A new entry "may_unmount" which would allow
+		the stacked fs to allocate some inode permanently and release
+		them at the end. Doing that now introduce a problem. unmount
+		always fail because some inodes are in use.
+	*/
+	if (!umsdos_isinit(inode)){
+		inode->u.umsdos_i.i_emd_dir = 0;
+		if (S_ISREG(inode->i_mode)){
+			static char is_init = 0;
+			if (!is_init){
+				/*
+					I don't want to change the msdos file system code
+					so I get the adress of some subroutine dynamicly
+					once.
+				*/
+				umsdos_file_inode_operations.bmap = inode->i_op->bmap;
+				inode->i_op = &umsdos_file_inode_operations;
+				is_init = 1;
+			}
+			inode->i_op = &umsdos_file_inode_operations;
+		}else if (S_ISDIR(inode->i_mode)){
+			if (dir != NULL){
+				umsdos_setup_dir_inode(inode);
+			}
+		}else if (S_ISLNK(inode->i_mode)){
+			inode->i_op = &umsdos_symlink_inode_operations;
+		}else if (S_ISCHR(inode->i_mode)){
+			inode->i_op = &chrdev_inode_operations;
+		}else if (S_ISBLK(inode->i_mode)){
+			inode->i_op = &blkdev_inode_operations;
+		}else if (S_ISFIFO(inode->i_mode)){
+			init_fifo(inode);
+		}
+		if (dir != NULL){
+			/* #Specification: inode / umsdos info
+				The first time an inode is seen (inode->i_count == 1),
+				the inode number of the EMD file which control this inode
+				is tagged to this inode. It allows operation such
+				as notify_change to be handled.
+			*/
+			/*
+				This is done last because it also control the
+				status of umsdos_isinit()
+			*/
+			umsdos_set_dirinfo (inode,dir,f_pos);
+		}
+	}else if (dir != NULL){
+		/*
+			Test to see if the info is maintained.
+			This should be removed when the file system will be proven.
+		*/
+		struct inode *emd_owner = umsdos_emd_dir_lookup(dir,1);
+		iput (emd_owner);
+		if (emd_owner->i_ino != inode->u.umsdos_i.i_emd_owner){
+			printk ("UMSDOS: *** EMD_OWNER ??? *** ino = %ld %ld <> %ld "
+				,inode->i_ino,emd_owner->i_ino,inode->u.umsdos_i.i_emd_owner);
+		}
+	}
+}
+/*
+	Get the inode of the directory which owns this inode.
+	Return 0 if ok, -EIO if error.
+*/
+int umsdos_get_dirowner(
+	struct inode *inode,
+	struct inode **result)	/* Hold NULL if any error */
+							/* else, the inode of the directory */
+{
+	int ret = -EIO;
+	unsigned long ino = inode->u.umsdos_i.i_dir_owner;
+	*result = NULL;
+	if (ino == 0){
+		printk ("UMSDOS: umsdos_get_dirowner ino == 0\n");
+	}else{
+		struct inode *dir = *result = iget(inode->i_sb,ino);
+		if (dir != NULL){
+			umsdos_patch_inode (dir,NULL,0);
+			ret = 0;
+		}
+	}
+	return ret;
+}
+/*
+	Load an inode from disk.
+*/
+void UMSDOS_read_inode(struct inode *inode)
+{
+	PRINTK (("read inode %x ino = %d ",inode,inode->i_ino));
+	msdos_read_inode(inode);
+	PRINTK (("ino = %d %d\n",inode->i_ino,inode->i_count));
+	if (S_ISDIR(inode->i_mode)
+		&& (inode->u.umsdos_i.u.dir_info.creating != 0
+			|| inode->u.umsdos_i.u.dir_info.looking != 0
+			|| inode->u.umsdos_i.u.dir_info.p != NULL)){
+		Printk (("read inode %d %d %p\n"
+			,inode->u.umsdos_i.u.dir_info.creating
+			,inode->u.umsdos_i.u.dir_info.looking
+			,inode->u.umsdos_i.u.dir_info.p));
+	}
+	/* #Specification: Inode / post initialisation
+		To completly initialise an inode, we need access to the owner
+		directory, so we can locate more info in the EMD file. This is
+		not available the first time the inode is access, we use
+		a value in the inode to tell if it has been finally initialised.
+
+		At first, we have tried testing i_count but it was causing
+		problem. It is possible that two or more process use the
+		newly accessed inode. While the first one block during
+		the initialisation (probably while reading the EMD file), the
+		others believe all is well because i_count > 1. They go banana
+		with a broken inode. See umsdos_lookup_patch and umsdos_patch_inode.
+	*/
+	umsdos_patch_inode(inode,NULL,0);
+}
+
+/*
+	Update the disk with the inode content
+*/
+void UMSDOS_write_inode(struct inode *inode)
+{
+	PRINTK (("UMSDOS_write_inode emd %d\n",inode->u.umsdos_i.i_emd_owner));
+	msdos_write_inode(inode);
+	UMSDOS_notify_change (NOTIFY_TIME,inode);
+}
+int UMSDOS_notify_change (int flags, struct inode *inode)
+{
+	int ret = 0;
+	if (inode->i_nlink > 0){
+		/* #Specification: notify_change / i_nlink > 0
+			notify change is only done for inode with nlink > 0. An inode
+			with nlink == 0 is no longer associated with any entry in
+			the EMD file, so there is nothing to update.
+		*/
+		unsigned long i_emd_owner = inode->u.umsdos_i.i_emd_owner;
+		if (inode == inode->i_sb->s_mounted){
+			/* #Specification: root inode / attributes
+				I don't know yet how this should work. Normally
+				the attributes (permissions bits, owner, times) of
+				a directory are stored in the EMD file of its parent.
+
+				One thing we could do is store the attributes of the root
+				inode in its own EMD file. A simple entry named "." could
+				be used for this special case. It would be read once
+				when the file system is mounted and update in
+				UMSDOS_notify_change() (right here).
+
+				I am not sure of the behavior of the root inode for
+				a real UNIX file system. For now, this is a nop.
+			*/
+		}else if (i_emd_owner != 0xffffffff && i_emd_owner != 0){
+			/* This inode is not a EMD file nor an inode used internally
+				by MSDOS, so we can update its status.
+				See emd.c
+			*/
+			struct inode *emd_owner = iget (inode->i_sb,i_emd_owner);
+			PRINTK (("notify change %p ",inode));
+			if (emd_owner == NULL){
+				printk ("UMSDOS: emd_owner = NULL ???");
+				ret = -EPERM;
+			}else{
+				struct file filp;
+				struct umsdos_dirent entry;
+				filp.f_pos = inode->u.umsdos_i.pos;
+				PRINTK (("pos = %d ",filp.f_pos));
+				/* Read only the start of the entry since we don't touch */
+				/* the name */
+				ret = umsdos_emd_dir_read (emd_owner,&filp,(char*)&entry
+					,UMSDOS_REC_SIZE);
+				if (ret == 0){
+					if (flags & NOTIFY_UIDGID){
+						entry.uid = inode->i_uid;
+						entry.gid = inode->i_gid;
+						/* Remove those flags msdos don't like */
+						flags &= ~NOTIFY_UIDGID;
+					}
+					if (flags & NOTIFY_MODE){
+						entry.mode = inode->i_mode;
+						flags &= ~NOTIFY_MODE;
+					}
+					if (flags & NOTIFY_TIME){
+						entry.atime = inode->i_atime;
+						entry.mtime = inode->i_mtime;
+						entry.ctime = inode->i_ctime;
+					}
+					entry.nlink = inode->i_nlink;
+					filp.f_pos = inode->u.umsdos_i.pos;
+					ret = umsdos_emd_dir_write (emd_owner,&filp,(char*)&entry
+						,UMSDOS_REC_SIZE);
+					PRINTK (("notify pos %d ret %d nlink %d "
+						,inode->u.umsdos_i.pos
+						,ret,entry.nlink));
+					/* #Specification: notify_change / msdos fs
+						notify_change operation are done only on the
+						EMD file. The msdos fs is not even called.
+					*/
+					#if 0
+					if (ret == 0
+						&& (S_ISDIR(inode->i_mode)
+							|| S_ISREG(inode->i_mode))){
+						ret = msdos_notify_change(flags, inode);
+						printk ("msdos_notify %x %d",inode,ret);
+					}
+					#endif
+				}
+				iput (emd_owner);
+			}
+			PRINTK (("\n"));
+		}
+	}
+	return ret;
+}
+
+/* #Specification: function name / convention
+	A simple convention for function name has been used in
+	the UMSDOS file system. First all function use the prefix
+	umsdos_ to avoid name clash with other part of the kernel.
+
+	And standard VFS entry point use the prefix UMSDOS (upper case)
+	so it's easier to tell them apart.
+*/
+
+static struct super_operations umsdos_sops = { 
+	UMSDOS_read_inode,
+	UMSDOS_notify_change,
+	UMSDOS_write_inode,
+	UMSDOS_put_inode,
+	UMSDOS_put_super,
+	NULL, /* added in 0.96c */
+	UMSDOS_statfs,
+	NULL
+};
+
+/*
+	Read the super block of an Extended MS-DOS FS.
+*/
+struct super_block *UMSDOS_read_super(
+	struct super_block *s,
+	void *data,
+	int silent)
+{
+	/* #Specification: mount / options
+		Umsdos run on top of msdos. Currently, it supports no
+		mount option, but happily pass all option received to
+		the msdos driver. I am not sure if all msdos mount option
+		make sens with Umsdos. Here are at least those who
+		are useful.
+			uid=
+			gid=
+
+		These options affect the operation of umsdos in directories
+		which do not have an EMD file. They behave like normal
+		msdos directory, with all limitation of msdos.
+	*/
+	struct super_block *sb = msdos_read_super(s,data,silent);
+	printk ("UMSDOS Alpha 0.4 (compatibility level %d.%d)\n"
+		,UMSDOS_VERSION,UMSDOS_RELEASE);
+	if (sb != NULL){
+		sb->s_op = &umsdos_sops;
+		PRINTK (("umsdos_read_super %p\n",sb->s_mounted));
+		umsdos_setup_dir_inode (sb->s_mounted);
+		PRINTK (("End umsdos_read_super\n"));
+		if (s == super_blocks){
+			/* #Specification: pseudo root / mount
+				When a umsdos fs is mounted, a special handling is done
+				if it is the root partition. We check for the presence
+				of the file /linux/etc/init or /linux/etc/rc.
+				If one is there, we do a chroot("/linux").
+
+				We check both because (see init/main.c) the kernel
+				try to exec init at different place and if it fails
+				it tries /bin/sh /etc/rc. To be consistent with
+				init/main.c, many more test would have to be done
+				to locate init. Any complain ?
+
+				The chroot is done manually in init/main.c but the
+				info (the inode) is located at mount time and store
+				in a global variable (pseudo_root) which is used at
+				different place in the umsdos driver. There is no
+				need to store this variable elsewhere because it
+				will always be one, not one per mount.
+
+				This feature allows the installation
+				of a linux system within a DOS system in a subdirectory.
+	
+				A user may install its linux stuff in c:\linux
+				avoiding any clash with existing DOS file and subdirectory.
+				When linux boots, it hides this fact, showing a normal
+				root directory with /etc /bin /tmp ...
+
+				The word "linux" is hardcoded in /usr/include/linux/umsdos_fs.h
+				in the macro UMSDOS_PSDROOT_NAME.
+			*/
+
+			struct inode *pseudo;
+			Printk (("Mounting root\n"));
+			if (umsdos_real_lookup (sb->s_mounted,UMSDOS_PSDROOT_NAME
+					,UMSDOS_PSDROOT_LEN,&pseudo)==0
+				&& S_ISDIR(pseudo->i_mode)){
+				struct inode *etc = NULL;
+				struct inode *rc = NULL;
+				Printk (("/%s is there\n",UMSDOS_PSDROOT_NAME));
+				if (umsdos_real_lookup (pseudo,"etc",3,&etc)==0
+					&& S_ISDIR(etc->i_mode)){
+					struct inode *init;
+					Printk (("/%s/etc is there\n",UMSDOS_PSDROOT_NAME));
+					if ((umsdos_real_lookup (etc,"init",4,&init)==0
+							&& S_ISREG(init->i_mode))
+						|| (umsdos_real_lookup (etc,"rc",2,&rc)==0
+							&& S_ISREG(rc->i_mode))){
+						umsdos_setup_dir_inode (pseudo);
+						Printk (("Activating pseudo root /%s\n",UMSDOS_PSDROOT_NAME));
+						pseudo_root = pseudo;
+						pseudo->i_count++;
+						pseudo = NULL;
+					}
+					iput (init);
+					iput (rc);
+				}
+				iput (etc);
+			}
+			iput (pseudo);
+		}
+	}
+	return sb;
+}
+
+
diff -u --recursive --new-file v1.1.34/linux/fs/umsdos/ioctl.c linux/fs/umsdos/ioctl.c
--- v1.1.34/linux/fs/umsdos/ioctl.c	Thu Jan  1 02:00:00 1970
+++ linux/fs/umsdos/ioctl.c	Sun Jul 24 17:39:45 1994
@@ -0,0 +1,259 @@
+/*
+ *  linux/fs/umsdos/ioctl.c
+ *
+ *  Written 1993 by Jacques Gelinas
+ *
+ *  Extended MS-DOS ioctl directory handling functions
+ */
+#include <asm/segment.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/msdos_fs.h>
+#include <linux/umsdos_fs.h>
+
+#define PRINTK(x)
+#define Printk(x) printk x
+
+/*
+	Perform special function on a directory
+*/
+int UMSDOS_ioctl_dir (
+	struct inode *dir,
+	struct file *filp,
+	unsigned int cmd,
+	unsigned long data)
+{
+	int ret = -EPERM;
+	/* #Specification: ioctl / acces
+		Only root (effective id) is allowed to do IOCTL on directory
+		in UMSDOS. EPERM is returned for other user.
+	*/
+	if (current->euid == 0
+		|| cmd == UMSDOS_GETVERSION){
+		struct umsdos_ioctl *idata = (struct umsdos_ioctl *)data;
+		ret = -EINVAL;
+		/* #Specification: ioctl / prototypes
+			The official prototype for the umsdos ioctl on directory
+			is:
+
+			int ioctl (
+				int fd,		// File handle of the directory
+				int cmd,	// command
+				struct umsdos_ioctl *data)
+
+			The struct and the commands are defined in linux/umsdos_fs.h.
+
+			umsdos_progs/umsdosio.c provide an interface in C++ to all
+			these ioctl. umsdos_progs/udosctl is a small utility showing
+			all this.
+
+			These ioctl generally allow one to work on the EMD or the
+			DOS directory independantly. These are essential to implement
+			the synchroniser.
+		*/
+		PRINTK (("ioctl %d ",cmd));
+		if (cmd == UMSDOS_GETVERSION){
+			/* #Specification: ioctl / UMSDOS_GETVERSION
+				The field version and release of the structure
+				umsdos_ioctl are filled with the version and release
+				number of the fs code in the kernel. This will allow
+				some form of checking. Users won't be able to run
+				incompatible utility such as the synchroniser (umssync).
+				umsdos_progs/umsdosio.c enforce this checking.
+
+				Return always 0.
+			*/
+			put_fs_byte (UMSDOS_VERSION,&idata->version);
+			put_fs_byte (UMSDOS_RELEASE,&idata->release);
+			ret = 0;
+		}else if (cmd == UMSDOS_READDIR_DOS){
+			/* #Specification: ioctl / UMSDOS_READDIR_DOS
+				One entry is read from the DOS directory at the current
+				file position. The entry is put as is in the dos_dirent
+				field of struct umsdos_ioctl.
+
+				Return > 0 if success.
+			*/
+			ret = msdos_readdir(dir,filp,&idata->dos_dirent,1);
+		}else if (cmd == UMSDOS_READDIR_EMD){
+			/* #Specification: ioctl / UMSDOS_READDIR_EMD
+				One entry is read from the EMD at the current
+				file position. The entry is put as is in the umsdos_dirent
+				field of struct umsdos_ioctl. The corresponding mangled
+				DOS entry name is put in the dos_dirent field.
+
+				All entries are read including hidden links. Blank
+				entries are skipped.
+
+				Return > 0 if success.
+			*/
+			struct inode *emd_dir = umsdos_emd_dir_lookup (dir,0);
+			if (emd_dir != NULL){
+				while (1){
+					if (filp->f_pos >= emd_dir->i_size){
+						ret = 0;
+						break;
+					}else{
+						struct umsdos_dirent entry;
+						off_t f_pos = filp->f_pos;
+						ret = umsdos_emd_dir_readentry (emd_dir,filp,&entry);
+						if (ret < 0){
+							break;
+						}else if (entry.name_len > 0){
+							struct umsdos_info info;
+							ret = entry.name_len;
+							umsdos_parse (entry.name,entry.name_len,&info);
+							info.f_pos = f_pos;
+							umsdos_manglename(&info);
+							memcpy_tofs(&idata->umsdos_dirent,&entry
+								,sizeof(entry));
+							memcpy_tofs(&idata->dos_dirent.d_name
+								,info.fake.fname,info.fake.len+1);
+							break;
+						}
+					}
+				}
+				iput (emd_dir);
+			}else{
+				/* The absence of the EMD is simply seen as an EOF */
+				ret = 0;
+			}
+		}else if (cmd == UMSDOS_INIT_EMD){
+			/* #Specification: ioctl / UMSDOS_INIT_EMD
+				The UMSDOS_INIT_EMD command make sure the EMD
+				exist for a directory. If it does not, it is
+				created. Also, it makes sure the directory functions
+				table (struct inode_operations) is set to the UMSDOS
+				semantic. This mean that umssync may be applied to
+				an "opened" msdos directory, and it will change behavior
+				on the fly.
+
+				Return 0 if success.
+			*/
+			extern struct inode_operations umsdos_rdir_inode_operations;
+			struct inode *emd_dir = umsdos_emd_dir_lookup (dir,1);
+			ret = emd_dir != NULL;
+			iput (emd_dir);
+					
+			dir->i_op = ret
+				? &umsdos_dir_inode_operations
+				: &umsdos_rdir_inode_operations;
+		}else{
+			struct umsdos_ioctl data;
+			memcpy_fromfs (&data,idata,sizeof(data));
+			if (cmd == UMSDOS_CREAT_EMD){
+				/* #Specification: ioctl / UMSDOS_CREAT_EMD
+					The umsdos_dirent field of the struct umsdos_ioctl is used
+					as is to create a new entry in the EMD of the directory.
+					The DOS directory is not modified.
+					No validation is done (yet).
+
+					Return 0 if success.
+				*/
+				struct umsdos_info info;
+				/* This makes sure info.entry and info in general is correctly */
+				/* initialised */
+				memcpy (&info.entry,&data.umsdos_dirent
+					,sizeof(data.umsdos_dirent));
+				umsdos_parse (data.umsdos_dirent.name
+					,data.umsdos_dirent.name_len,&info);
+				ret = umsdos_newentry (dir,&info);
+			}else if (cmd == UMSDOS_UNLINK_EMD){
+				/* #Specification: ioctl / UMSDOS_UNLINK_EMD
+					The umsdos_dirent field of the struct umsdos_ioctl is used
+					as is to remove an entry from the EMD of the directory.
+					No validation is done (yet). The mode field is used
+					to validate S_ISDIR or S_ISREG.
+
+					Return 0 if success.
+				*/
+				struct umsdos_info info;
+				/* This makes sure info.entry and info in general is correctly */
+				/* initialised */
+				memcpy (&info.entry,&data.umsdos_dirent
+					,sizeof(data.umsdos_dirent));
+				umsdos_parse (data.umsdos_dirent.name
+					,data.umsdos_dirent.name_len,&info);
+				ret = umsdos_delentry (dir,&info
+					,S_ISDIR(data.umsdos_dirent.mode));
+			}else if (cmd == UMSDOS_UNLINK_DOS){
+				/* #Specification: ioctl / UMSDOS_UNLINK_DOS
+					The dos_dirent field of the struct umsdos_ioctl is used to
+					execute a msdos_unlink operation. The d_name and d_reclen
+					fields are used.
+
+					Return 0 if success.
+				*/
+				dir->i_count++;
+				ret = msdos_unlink (dir,data.dos_dirent.d_name
+					,data.dos_dirent.d_reclen);
+			}else if (cmd == UMSDOS_RMDIR_DOS){
+				/* #Specification: ioctl / UMSDOS_RMDIR_DOS
+					The dos_dirent field of the struct umsdos_ioctl is used to
+					execute a msdos_unlink operation. The d_name and d_reclen
+					fields are used.
+
+					Return 0 if success.
+				*/
+				dir->i_count++;
+				ret = msdos_rmdir (dir,data.dos_dirent.d_name
+					,data.dos_dirent.d_reclen);
+			}else if (cmd == UMSDOS_STAT_DOS){
+				/* #Specification: ioctl / UMSDOS_STAT_DOS
+					The dos_dirent field of the struct umsdos_ioctl is
+					used to execute a stat operation in the DOS directory.
+					The d_name and d_reclen fields are used.
+
+					The following field of umsdos_ioctl.stat are filled.
+
+					st_ino,st_mode,st_size,st_atime,st_mtime,st_ctime,
+					Return 0 if success.
+				*/
+				struct inode *inode;
+				ret = umsdos_real_lookup (dir,data.dos_dirent.d_name
+					,data.dos_dirent.d_reclen,&inode);
+				if (ret == 0){
+					data.stat.st_ino = inode->i_ino;
+					data.stat.st_mode = inode->i_mode;
+					data.stat.st_size = inode->i_size;
+					data.stat.st_atime = inode->i_atime;
+					data.stat.st_ctime = inode->i_ctime;
+					data.stat.st_mtime = inode->i_mtime;
+					memcpy_tofs (&idata->stat,&data.stat,sizeof(data.stat));
+					iput (inode);
+				}
+			}else if (cmd == UMSDOS_DOS_SETUP){
+				/* #Specification: ioctl / UMSDOS_DOS_SETUP
+					The UMSDOS_DOS_SETUP ioctl allow changing the
+					default permission of the MsDOS file system driver
+					on the fly. The MsDOS driver apply global permission
+					to every file and directory. Normally these permissions
+					are controlled by a mount option. This is not
+					available for root partition, so a special utility
+					(umssetup) is provided to do this, normally in
+					/etc/rc.local.
+
+					Be aware that this apply ONLY to MsDOS directory
+					(those without EMD --linux-.---). Umsdos directory
+					have independant (standard) permission for each
+					and every file.
+
+					The field umsdos_dirent provide the information needed.
+					umsdos_dirent.uid and gid sets the owner and group.
+					umsdos_dirent.mode set the permissions flags.
+				*/
+				dir->i_sb->u.msdos_sb.fs_uid = data.umsdos_dirent.uid;
+				dir->i_sb->u.msdos_sb.fs_gid = data.umsdos_dirent.gid;
+				dir->i_sb->u.msdos_sb.fs_umask = data.umsdos_dirent.mode;
+				ret = 0;
+			}
+		}
+	}
+	PRINTK (("ioctl return %d\n",ret));
+	return ret;
+}
+
+
+
diff -u --recursive --new-file v1.1.34/linux/fs/umsdos/mangle.c linux/fs/umsdos/mangle.c
--- v1.1.34/linux/fs/umsdos/mangle.c	Thu Jan  1 02:00:00 1970
+++ linux/fs/umsdos/mangle.c	Sun Jul 24 17:39:45 1994
@@ -0,0 +1,478 @@
+/*
+ *  linux/fs/umsdos/mangle.c
+ *
+ *	Written 1993 by Jacques Gelinas 
+ *
+ * Control the mangling of file name to fit msdos name space.
+ * Many optimisation by GLU == dglaude@is1.vub.ac.be (GLAUDE DAVID)
+*/
+#include <linux/errno.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/umsdos_fs.h>
+
+/*
+	Complete the mangling of the MSDOS fake name
+	based on the position of the entry in the EMD file.
+
+	Simply complete the job of umsdos_parse; fill the extension.
+
+	Beware that info->f_pos must be set.
+*/
+void umsdos_manglename (struct umsdos_info *info)
+{
+	if (info->msdos_reject){
+		/* #Specification: file name / non MSDOS conforming / mangling
+			Each non MSDOS conforming file has a special extension
+			build from the entry position in the EMD file.
+
+			This number is then transform in a base 32 number, where
+			each digit is expressed like hexadecimal number, using
+			digit and letter, except it uses 22 letters from 'a' to 'v'.
+			The number 32 comes from 2**5. It is faster to split a binary
+			number using a base which is a power of two. And I was 32
+			when I started this project. Pick your answer :-) .
+
+			If the result is '0', it is replace with '_', simply
+			to make it odd.
+
+			This is true for the first two character of the extension.
+			The last one is taken from a list of odd character, which
+			are:
+
+				{ } ( ) ! ` ^ & @
+
+			With this scheme, we can produce 9216 ( 9* 32 * 32)
+			different extensions which should not clash with any useful
+			extension already popular or meaningful. Since most directory
+			have much less than 32 * 32 files in it, the first character
+			of the extension of any mangle name will be {.
+
+			Here are the reason to do this (this kind of mangling).
+
+			-The mangling is deterministic. Just by the extension, we
+			 are able to locate the entry in the EMD file.
+
+			-By keeping to beginning of the file name almost unchange,
+			 we are helping the MSDOS user.
+
+			-The mangling produces names not too ugly, so an msdos user
+			 may live with it (remember it, type it, etc...).
+
+			-The mangling produces names ugly enough so no one will
+			 ever think of using such a name in real life. This is not
+			 fool proof. I don't think there is a total solution to this.
+		*/
+		union {
+			int entry_num;
+			struct {
+				unsigned num1:5,num2:5,num3:5;
+			}num;
+		} u;
+		char *pt = info->fake.fname + info->fake.len;
+		/* lookup for encoding the last character of the extension */
+		/* It contain valid character after the ugly one to make sure */
+		/* even if someone overflow the 32 * 32 * 9 limit, it still do */
+		/* something */
+		#define SPECIAL_MANGLING '{','}','(',')','!','`','^','&','@'
+		static char lookup3[]={
+			SPECIAL_MANGLING,
+			/* This is the start of lookup12 */
+			'_','1','2','3','4','5','6','7','8','9',
+			'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o',
+			'p','q','r','s','t','u','v' 
+		};
+		#define lookup12 (lookup3+9)
+		u.entry_num = info->f_pos / UMSDOS_REC_SIZE;
+		if (u.entry_num > (9* 32 * 32)){
+			printk ("UMSDOS: More than 9216 file in a directory.\n"
+				"This may break the mangling strategy.\n"
+				"Not a killer problem. See doc.\n");
+		}
+		*pt++ = '.';
+		*pt++ = lookup3 [u.num.num3];
+		*pt++ = lookup12[u.num.num2];
+		*pt++ = lookup12[u.num.num1];
+		*pt = '\0';		/* help doing printk */	
+		info->fake.len += 4;
+		info->msdos_reject = 0;		/* Avoid mangling twice */
+	}
+}
+
+/*
+	Evaluate the record size needed to store of name of len character.
+	The value returned is a multiple of UMSDOS_REC_SIZE.
+*/
+int umsdos_evalrecsize (int len)
+{
+	struct umsdos_dirent dirent;
+	int nbrec = 1+((len-1+(dirent.name-(char*)&dirent))
+                   / UMSDOS_REC_SIZE);
+	return nbrec * UMSDOS_REC_SIZE;
+	/*
+	GLU	This should be inlined or something to speed it up to the max.
+	GLU	nbrec is absolutely not needed to return the value.
+	*/
+}
+#ifdef TEST
+int umsdos_evalrecsize_old (int len)
+{
+	struct umsdos_dirent dirent;
+	int size = len + (dirent.name-(char*)&dirent);
+	int nbrec = size / UMSDOS_REC_SIZE;
+	int extra = size % UMSDOS_REC_SIZE;
+	if (extra > 0) nbrec++;
+	return nbrec * UMSDOS_REC_SIZE;
+}
+#endif
+/*
+	Fill the struct info with the full and msdos name of a file
+	Return 0 if all is ok, a negative error code otherwise.
+*/
+int umsdos_parse (
+	const char *fname,
+	int len,
+	struct umsdos_info *info)
+{
+	int ret = -ENAMETOOLONG;
+	/* #Specification: file name / too long
+		If a file name exceed UMSDOS maxima, the file name is silently
+		truncated. This makes it conformant with the other file system
+		of Linux (minix and ext2 at least).
+	*/
+	if (len > UMSDOS_MAXNAME) len = UMSDOS_MAXNAME;
+	{
+		const char *firstpt=NULL;	/* First place we saw a . in fname */
+		/* #Specification: file name / non MSDOS conforming / base length 0
+			file name beginning with a period '.' are invalid for MsDOS.
+			It needs absolutly a base name. So the file name is mangled
+		*/
+		int ivldchar = fname[0] == '.';/* At least one invalid character */
+		int msdos_len = len;
+		int base_len;
+		/*
+			cardinal_per_size tells if there exist at least one
+			DOS pseudo devices on length n. See the test below.
+		*/
+		static const char cardinal_per_size[9]={
+			0, 0, 0, 1, 1, 0, 1, 0, 1
+		};
+		/*
+			lkp translate all character to acceptable character (for DOS).
+			When lkp[n] == n, it means also it is an acceptable one.
+			So it serve both as a flag and as a translator.
+		*/
+		static char lkp[256];
+		static char is_init=0;
+		if (!is_init){
+			/*
+				Initialisation of the array is easier and less error prone
+				like this.
+			*/
+			int i;
+			static char *spc = "\"*+,/:;<=>?[\\]|~";
+			is_init = 1;
+			for (i=0; i<=32; i++) lkp[i] = '#';
+			for (i=33; i<'A'; i++) lkp[i] = (char)i;
+			for (i='A'; i<='Z'; i++) lkp[i] = (char)(i+('a'-'A'));
+			for (i='Z'+1; i<127; i++) lkp[i] = (char)i;
+			for (i=128; i<256; i++) lkp[i] = '#';
+
+			lkp['.'] = '_';
+			while (*spc != '\0') lkp[(unsigned char)(*spc++)] = '#';
+		}
+		/*	GLU
+			file name wich are longer than 8+'.'+3 are invalid for MsDOS.
+			So the file name is to be mangled no more test needed.
+			This Speed Up for long and very long name.
+			The position of the last point is no more necessary anyway.
+		*/
+		if (len<=(8+1+3)){
+			const char *pt = fname;
+			const char *endpt = fname + len;
+			while (pt < endpt){
+				if (*pt == '.'){
+					if (firstpt != NULL){
+						/* 2 . in a file name. Reject */
+						ivldchar = 1;
+						break;
+					}else{
+						int extlen = (int)(endpt - pt);
+						firstpt = pt;
+						if (firstpt - fname > 8){
+							/* base name longer than 8: reject */
+							ivldchar = 1;
+							break;
+						}else if (extlen > 4){
+							/* Extension longer than 4 (including .): reject */
+							ivldchar = 1;
+							break;
+						}else if (extlen == 1){
+							/* #Specification: file name / non MSDOS conforming / last char == .
+								If the last character of a file name is
+								a period, mangling is applied. MsDOS do
+								not support those file name.
+							*/
+							ivldchar = 1;
+							break;
+						}else if (extlen == 4){
+							/* #Specification: file name / non MSDOS conforming / mangling clash
+								To avoid clash with	the umsdos mangling, any file
+								with a special character as the first character
+								of the extension will be mangled. This solve the
+								following problem:
+				
+								touch FILE
+								# FILE is invalid for DOS, so mangling is applied
+								# file.{_1 is created in the DOS directory
+								touch file.{_1
+								# To UMSDOS file point to a single DOS entry.
+								# So file.{_1 has to be mangled.
+							*/	
+							static char special[]={
+								SPECIAL_MANGLING,'\0'
+							};
+							if (strchr(special,firstpt[1])!= NULL){
+								ivldchar = 1;
+								break;
+							}
+						}
+					}
+				}else if (lkp[(unsigned char)(*pt)] != *pt){
+					ivldchar = 1;
+					break;
+				}
+				pt++;
+			}
+		}else{
+			ivldchar = 1;
+		}
+		if (ivldchar
+			|| (firstpt == NULL && len > 8)
+			|| (len == UMSDOS_EMD_NAMELEN
+				&& memcmp(fname,UMSDOS_EMD_FILE,UMSDOS_EMD_NAMELEN)==0)){
+			/* #Specification: file name / --linux-.---
+				The name of the EMD file --linux-.--- is map to a mangled
+				name. So UMSDOS does not restrict its use.
+			*/
+			/* #Specification: file name / non MSDOS conforming / mangling
+				Non MSDOS conforming file name must use some alias to fit
+				in the MSDOS name space.
+
+				The strategy is simple. The name is simply truncated to
+				8 char. points are replace with underscore and a
+				number is given as an extension. This number correspond
+				to the entry number in the EMD file. The EMD file
+				only need to carry the real name.
+
+				Upper case is also convert to lower case.
+				Control character are converted to #.
+				Space are converted to #.
+				The following character are also converted to #.
+					" * + , / : ; < = > ? [ \ ] | ~
+
+				Sometime, the problem is not in MsDOS itself but in
+				command.com.
+			*/
+			int i;
+			char *pt = info->fake.fname;
+			base_len = msdos_len = (msdos_len>8) ? 8 : msdos_len;
+			/*
+				There is no '.' any more so we know for a fact that
+				the base lenght is the lenght.
+			*/
+			memcpy (info->fake.fname,fname,msdos_len);
+			for (i=0; i<msdos_len; i++, pt++) *pt = lkp[(unsigned char)(*pt)];
+			*pt = '\0';	/* GLU	C'est sur on a un 0 a la fin */
+			info->msdos_reject = 1;
+			/*
+				The numeric extension is added only when we know
+				the position in the EMD file, in umsdos_newentry(),
+				umsdos_delentry(), and umsdos_findentry().
+				See umsdos_manglename().
+			*/
+		}else{
+			/* Conforming MSDOS file name */
+			strcpy (info->fake.fname,fname);	/* GLU	C'est sur on a un 0 a la fin */
+			info->msdos_reject = 0;
+			base_len = firstpt != NULL ? (int)(firstpt - fname) : len;
+		}
+		if (cardinal_per_size[base_len]){
+			/* #Specification: file name / MSDOS devices / mangling
+				To avoid unreachable file from MsDOS, any MsDOS conforming
+				file with a basename equal to one of the MsDOS pseudo
+				devices will be mangled.
+
+				If a file such as "prn" was created, it would be unreachable
+				under MsDOS because prn is assumed to be the printer, even
+				if the file does have an extension.
+
+				Since the extension is unimportant to MsDOS, we must patch
+				the basename also. We simply insert a minus '-'. To avoid
+				conflict with valid file with a minus in front (such as
+				"-prn"), we add an mangled extension like any other
+				mangled file name.
+
+				Here is the list of DOS pseudo devices:
+
+					"prn","con","aux","nul",
+					"lpt1","lpt2","lpt3","lpt4",
+					"com1","com2","com3","com4",
+					"clock$"
+
+				and some standard ones for common DOS programs
+
+					"emmxxxx0","xmsxxxx0","setverxx"
+
+				(Thanks to Chris Hall <CAH17@PHOENIX.CAMBRIDGE.AC.UK>
+				 for pointing these to me).
+
+				Is there one missing ?
+			*/
+			/* This table must be ordered by length */
+			static const char *tbdev[]={
+				"prn","con","aux","nul",
+				"lpt1","lpt2","lpt3","lpt4",
+				"com1","com2","com3","com4",
+				"clock$",
+				"emmxxxx0","xmsxxxx0","setverxx"
+			};
+			/* Tell where to find in tbdev[], the first name of */
+			/* a certain length */
+			static const char start_ind_dev[9]={
+				0, 0, 0, 4, 12, 12, 13, 13, 16 
+			};
+			char basen[9];
+			int i;
+			for (i=start_ind_dev[base_len-1]; i<start_ind_dev[base_len]; i++){
+				if (memcmp(info->fake.fname,tbdev[i],base_len)==0){
+					memcpy (basen,info->fake.fname,base_len);
+					basen[base_len] = '\0';		/* GLU	C'est sur on a un 0 a la fin */
+					/*
+					GLU	On ne fait cela que si necessaire, on essaye d'etre le
+					GLU	simple dans le cas general (le plus frequent).
+					*/
+					info->fake.fname[0] = '-';
+					strcpy (info->fake.fname+1,basen);	/* GLU	C'est sur on a un 0 a la fin */
+					msdos_len = (base_len==8) ? 8 : base_len + 1;
+					info->msdos_reject = 1;
+					break;
+				}
+			}
+		}
+		info->fake.fname[msdos_len] = '\0';	/* Help doing printk */
+		/* GLU	Ce zero devrais deja y etre ! (invariant ?) */
+		info->fake.len = msdos_len;
+		/* Pourquoi ne pas utiliser info->fake.len partout ??? plus long ?*/
+		memcpy (info->entry.name,fname,len);
+		info->entry.name_len = len;
+		ret = 0;
+	}
+	/*
+		Evaluate how many record are needed to store this entry.
+	*/
+	info->recsize = umsdos_evalrecsize (len);
+	return ret;
+}
+
+#ifdef TEST
+
+struct MANG_TEST{
+	char *fname;		/* Name to validate */
+	int msdos_reject;	/* Expected msdos_reject flag */
+	char *msname;		/* Expected msdos name */
+};
+
+struct MANG_TEST tb[]={
+	"hello",		0,	"hello",
+	"hello.1",		0,	"hello.1",
+	"hello.1_",		0,	"hello.1_",
+	"prm",			0,	"prm",
+
+#ifdef PROPOSITION
+	"HELLO",		1,	"hello",
+	"Hello.1",		1,	"hello.1",
+	"Hello.c",		1,	"hello.c",
+#elseif
+/*
+	Je trouve les trois exemples ci-dessous tres "malheureux".
+	Je propose de mettre en minuscule dans un passe preliminaire,
+	et de tester apres si il y a d'autres caracters "mechants".
+	Bon, je ne l'ai pas fait, parceque ce n'est pas si facilement
+	modifiable que ca. Mais c'est pour le principe.
+	Evidemment cela augmente les chances de "Colision",
+	par exemple: entre "HELLO" et "Hello", mais ces problemes
+	peuvent etre traiter ailleur avec les autres colisions.
+*/
+	"HELLO",		1,	"hello",
+	"Hello.1",		1,	"hello_1",
+	"Hello.c",		1,	"hello_c",
+#endif
+
+	"hello.{_1",		1,	"hello_{_",
+	"hello\t",		1,	"hello#",
+	"hello.1.1",		1,	"hello_1_",
+	"hel,lo",		1,	"hel#lo",
+	"Salut.Tu.vas.bien?",	1,	"salut_tu",
+	".profile",		1,	"_profile",
+	".xv",			1,	"_xv",
+	"toto.",		1,	"toto_",
+	"clock$.x",		1,	"-clock$",
+	"emmxxxx0",		1,	"-emmxxxx",
+	"emmxxxx0.abcd",	1,	"-emmxxxx",
+	"aux",			1,	"-aux",
+	"prn",			1,	"-prn",
+	"prn.abc",		1,	"-prn",
+	"PRN",			1,	"-prn",
+/* 
+GLU	ATTENTION : Le resultat de ceux-ci sont differents avec ma version
+GLU	du mangle par rapport au mangle originale.
+GLU	CAUSE: La maniere de calculer la variable baselen. 
+GLU		Pour toi c'est toujours 3
+GLU		Pour moi c'est respectivement 7, 8 et 8
+*/
+	"PRN.abc",		1,	"prn_abc",
+	"Prn.abcd",		1,	"prn_abcd",
+	"prn.abcd",		1,	"prn_abcd",
+	"Prn.abcdefghij",	1,	"prn_abcd"
+};
+
+int main (int argc, char *argv[])
+{
+	int i,rold,rnew;
+	printf ("Testing the umsdos_parse.\n");
+	for (i=0; i<sizeof(tb)/sizeof(tb[0]); i++){
+		struct MANG_TEST *pttb = tb+i;
+		struct umsdos_info info;
+		int ok = umsdos_parse (pttb->fname,strlen(pttb->fname),&info);
+		if (strcmp(info.fake.fname,pttb->msname)!=0){
+			printf ("**** %s -> ",pttb->fname);
+			printf ("%s <> %s\n",info.fake.fname,pttb->msname);
+		}else if (info.msdos_reject != pttb->msdos_reject){
+			printf ("**** %s -> %s ",pttb->fname,pttb->msname);
+			printf ("%d <> %d\n",info.msdos_reject,pttb->msdos_reject);
+		}else{
+			printf ("     %s -> %s %d\n",pttb->fname,pttb->msname
+				,pttb->msdos_reject);
+		}
+	}
+	printf ("Testing the new umsdos_evalrecsize.");
+	for (i=0; i<UMSDOS_MAXNAME ; i++){
+		rnew=umsdos_evalrecsize (i);
+		rold=umsdos_evalrecsize_old (i);
+		if (!(i%UMSDOS_REC_SIZE)){
+			printf ("\n%d:\t",i);
+		}
+		if (rnew!=rold){
+			printf ("**** %d newres: %d != %d \n", i, rnew, rold);
+		}else{
+			printf(".");
+		}
+	}
+	printf ("\nEnd of Testing.\n");
+
+	return 0;
+}
+
+#endif
diff -u --recursive --new-file v1.1.34/linux/fs/umsdos/namei.c linux/fs/umsdos/namei.c
--- v1.1.34/linux/fs/umsdos/namei.c	Thu Jan  1 02:00:00 1970
+++ linux/fs/umsdos/namei.c	Sun Jul 24 17:39:45 1994
@@ -0,0 +1,1037 @@
+/*
+ *  linux/fs/umsdos/namei.c
+ *
+ *	Written 1993 by Jacques Gelinas 
+ *	Inspired from linux/fs/msdos/... by Werner Almesberger
+ *
+ * Maintain and access the --linux alternate directory file.
+*/
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/msdos_fs.h>
+#include <linux/umsdos_fs.h>
+#include <linux/malloc.h>
+
+#define PRINTK(x)
+#define Printk(x)	printk x
+
+#if 1
+/*
+	Wait for creation exclusivity.
+	Return 0 if the dir was already available.
+	Return 1 if a wait was necessary.
+		When 1 is return, it means a wait was done. It does not
+		mean the directory is available.
+*/
+static int umsdos_waitcreate(struct inode *dir)
+{
+	int ret = 0;
+	if (dir->u.umsdos_i.u.dir_info.creating
+		&& dir->u.umsdos_i.u.dir_info.pid != current->pid){
+		sleep_on(&dir->u.umsdos_i.u.dir_info.p);
+		ret = 1;
+	}
+	return ret;
+}
+/*
+	Wait for any lookup process to finish
+*/
+static void umsdos_waitlookup (struct inode *dir)
+{
+	while (dir->u.umsdos_i.u.dir_info.looking){
+		sleep_on(&dir->u.umsdos_i.u.dir_info.p);
+	}
+}
+/*
+	Lock all other process out of this directory.
+*/
+void umsdos_lockcreate (struct inode *dir)
+{
+	/* #Specification: file creation / not atomic
+		File creation is a two step process. First we create (allocate)
+		an entry in the EMD file and then (using the entry offset) we
+		build a unique name for MSDOS. We create this name in the msdos
+		space.
+
+		We have to use semaphore (sleep_on/wake_up) to prevent lookup
+		into a directory when we create a file or directory and to
+		prevent creation while a lookup is going on. Since many lookup
+		may happen at the same time, the semaphore is a counter.
+
+		Only one creation is allowed at the same time. This protection
+		may not be necessary. The problem arise mainly when a lookup
+		or a readdir is done while a file is partially created. The
+		lookup process see that as a "normal" problem and silently
+		erase the file from the EMD file. Normal because a file
+		may be erased during a MSDOS session, but not removed from
+		the EMD file.
+
+		The locking is done on a directory per directory basis. Each
+		directory inode has its wait_queue.
+
+		For some operation like hard link, things even get worse. Many
+		creation must occur at once (atomic). To simplify the design
+		a process is allowed to recursivly lock the directory for
+		creation. The pid of the locking process is kept along with
+		a counter so a second level of locking is granted or not.
+	*/
+	/*
+		Wait for any creation process to finish except
+		if we (the process) own the lock
+	*/
+	while (umsdos_waitcreate(dir)!=0);
+	dir->u.umsdos_i.u.dir_info.creating++;
+	dir->u.umsdos_i.u.dir_info.pid = current->pid;
+	umsdos_waitlookup (dir);
+}
+/*
+	Lock all other process out of those two directories.
+*/
+static void umsdos_lockcreate2 (struct inode *dir1, struct inode *dir2)
+{
+	/*
+		We must check that both directory are available before
+		locking anyone of them. This is to avoid some deadlock.
+		Thanks to dglaude@is1.vub.ac.be (GLAUDE DAVID) for pointing
+		this to me.
+	*/
+	while (1){
+		if (umsdos_waitcreate(dir1)==0
+			&& umsdos_waitcreate(dir2)==0){
+			/* We own both now */
+			dir1->u.umsdos_i.u.dir_info.creating++;
+			dir1->u.umsdos_i.u.dir_info.pid = current->pid;
+			dir2->u.umsdos_i.u.dir_info.creating++;
+			dir2->u.umsdos_i.u.dir_info.pid = current->pid;
+			break;
+		}
+	}
+	umsdos_waitlookup(dir1);
+	umsdos_waitlookup(dir2);
+}
+/*
+	Wait until creation is finish in this directory.
+*/
+void umsdos_startlookup (struct inode *dir)
+{
+	while (umsdos_waitcreate (dir) != 0);
+	dir->u.umsdos_i.u.dir_info.looking++;
+}
+void check_page_tables(void);
+
+/*
+	Unlock the directory.
+*/
+void umsdos_unlockcreate (struct inode *dir)
+{
+	dir->u.umsdos_i.u.dir_info.creating--;
+	if (dir->u.umsdos_i.u.dir_info.creating < 0){
+		printk ("UMSDOS: dir->u.umsdos_i.u.dir_info.creating < 0: %d"
+			,dir->u.umsdos_i.u.dir_info.creating);
+	}
+	wake_up (&dir->u.umsdos_i.u.dir_info.p);
+}
+/*
+	Tell directory lookup is over.
+*/
+void umsdos_endlookup (struct inode *dir)
+{
+	dir->u.umsdos_i.u.dir_info.looking--;
+	if (dir->u.umsdos_i.u.dir_info.looking < 0){
+		printk ("UMSDOS: dir->u.umsdos_i.u.dir_info.looking < 0: %d"
+			,dir->u.umsdos_i.u.dir_info.looking);
+	}
+	wake_up (&dir->u.umsdos_i.u.dir_info.p);
+}
+#else
+static void umsdos_lockcreate (struct inode *dir){}
+static void umsdos_lockcreate2 (struct inode *dir1, struct inode *dir2){}
+void umsdos_startlookup (struct inode *dir){}
+static void umsdos_unlockcreate (struct inode *dir){}
+void umsdos_endlookup (struct inode *dir){}
+#endif
+static int umsdos_nevercreat(
+	struct inode *dir,
+	const char *name,		/* Name of the file to add */
+	int len,
+	int errcod)				/* Length of the name */
+{
+	int ret = 0;
+	if (umsdos_is_pseudodos(dir,name,len)){
+		/* #Specification: pseudo root / any file creation /DOS
+			The pseudo sub-directory /DOS can't be created!
+			EEXIST is returned.
+
+			The pseudo sub-directory /DOS can't be removed!
+			EPERM is returned.
+		*/
+		ret = -EPERM;
+		ret = errcod;
+	}else if (name[0] == '.'
+		&& (len == 1 || (len == 2 && name[1] == '.'))){
+		/* #Specification: create / . and ..
+			If one try to creates . or .., it always fail and return
+			EEXIST.
+
+			If one try to delete . or .., it always fail and return
+			EPERM.
+
+			This should be test at the VFS layer level to avoid
+			duplicating this in all file systems. Any comments ?
+		*/
+		ret = errcod;
+	}
+	return ret;
+}
+	
+/*
+	Add a new file (ordinary or special) into the alternate directory.
+	The file is added to the real MSDOS directory. If successfull, it
+	is then added to the EDM file.
+
+	Return the status of the operation. 0 mean success.
+*/
+static int umsdos_create_any (
+	struct inode *dir,
+	const char *name,		/* Name of the file to add */
+	int len,				/* Length of the name */
+	int mode,				/* Permission bit + file type ??? */
+	int rdev,				/* major, minor or 0 for ordinary file */
+							/* and symlinks */
+	char flags,
+	struct inode **result)	/* Will hold the inode of the newly created */
+							/* file */
+{
+	int ret = umsdos_nevercreat(dir,name,len,-EEXIST);
+	if (ret == 0){
+		struct umsdos_info info;
+		ret = umsdos_parse (name,len,&info);
+		*result = NULL;
+		if (ret == 0){
+			info.entry.mode = mode;
+			info.entry.rdev = rdev;
+			info.entry.flags = flags;
+			info.entry.uid = current->euid;
+			info.entry.gid = (dir->i_mode & S_ISGID)
+				? dir->i_gid : current->egid;
+			info.entry.ctime = info.entry.atime = info.entry.mtime
+				= CURRENT_TIME;
+			info.entry.nlink = 1;
+			umsdos_lockcreate(dir);
+			ret = umsdos_newentry (dir,&info);
+			if (ret == 0){
+				dir->i_count++;
+				ret = msdos_create (dir,info.fake.fname,info.fake.len
+					,S_IFREG|0777,result);
+				if (ret == 0){
+					struct inode *inode = *result;
+					umsdos_lookup_patch (dir,inode,&info.entry,info.f_pos);
+					PRINTK (("inode %p[%d] ",inode,inode->i_count));
+					PRINTK (("Creation OK: [%d] %s %d pos %d\n",dir->i_ino
+						,info.fake.fname,current->pid,info.f_pos));
+				}else{
+					/* #Specification: create / file exist in DOS
+						Here is a situation. Trying to create a file with
+						UMSDOS. The file is unknown to UMSDOS but already
+						exist in the DOS directory.
+
+						Here is what we are NOT doing:
+
+						We could silently assume that everything is fine
+						and allows the creation to succeed.
+
+						It is possible not all files in the partition
+						are mean to be visible from linux. By trying to create
+						those file in some directory, one user may get access
+						to those file without proper permissions. Looks like
+						a security hole to me. Off course sharing a file system
+						with DOS is some kind of security hole :-)
+
+						So ?
+
+						We return EEXIST in this case.
+						The same is true for directory creation.
+					*/
+					if (ret == -EEXIST){
+						printk ("UMSDOS: out of sync, Creation error [%ld], "
+							"deleting %s %d %d pos %ld\n",dir->i_ino
+							,info.fake.fname,-ret,current->pid,info.f_pos);
+					}
+					umsdos_delentry (dir,&info,0);
+				}
+				PRINTK (("umsdos_create %s ret = %d pos %d\n"
+					,info.fake.fname,ret,info.f_pos));
+			}
+			umsdos_unlockcreate(dir);
+		}
+	}
+	iput (dir);
+	return ret;
+}
+/*
+	Initialise the new_entry from the old for a rename operation.
+	(Only useful for umsdos_rename_f() below).
+*/
+static void umsdos_ren_init(
+	struct umsdos_info *new_info,
+	struct umsdos_info *old_info,
+	int flags)		/* 0 == copy flags from old_name */
+					/* != 0, this is the value of flags */
+{
+	new_info->entry.mode = old_info->entry.mode;
+	new_info->entry.rdev = old_info->entry.rdev;
+	new_info->entry.uid = old_info->entry.uid;
+	new_info->entry.gid = old_info->entry.gid;
+	new_info->entry.ctime = old_info->entry.ctime;
+	new_info->entry.atime = old_info->entry.atime;
+	new_info->entry.mtime = old_info->entry.mtime;
+	new_info->entry.flags = flags ? flags : old_info->entry.flags;
+	new_info->entry.nlink = old_info->entry.nlink;
+}
+
+#define chkstk() \
+	if (STACK_MAGIC != *(unsigned long *)current->kernel_stack_page){\
+		printk(KERN_ALERT "UMSDOS: %s magic %x != %lx ligne %d\n" \
+		, current->comm,STACK_MAGIC \
+		,*(unsigned long *)current->kernel_stack_page \
+		,__LINE__); \
+	}
+	
+/*
+	Rename a file (move) in the file system.
+*/
+static int umsdos_rename_f(
+	struct inode * old_dir,
+	const char * old_name,
+	int old_len,
+	struct inode * new_dir,
+	const char * new_name,
+	int new_len,
+	int flags)		/* 0 == copy flags from old_name */
+					/* != 0, this is the value of flags */
+{
+	int ret = EPERM;
+	struct umsdos_info old_info;
+	int old_ret = umsdos_parse (old_name,old_len,&old_info);
+	struct umsdos_info new_info;
+	int new_ret = umsdos_parse (new_name,new_len,&new_info);
+chkstk();
+	PRINTK (("umsdos_rename %d %d ",old_ret,new_ret));
+	if (old_ret == 0 && new_ret == 0){
+		umsdos_lockcreate2(old_dir,new_dir);
+chkstk();
+		PRINTK (("old findentry "));
+		ret = umsdos_findentry(old_dir,&old_info,0);
+chkstk();
+		PRINTK (("ret %d ",ret));
+		if (ret == 0){
+			PRINTK (("new newentry "));
+			umsdos_ren_init(&new_info,&old_info,flags);
+			ret = umsdos_newentry (new_dir,&new_info);
+chkstk();
+			PRINTK (("ret %d %d ",ret,new_info.fake.len));
+			if (ret == 0){
+				PRINTK (("msdos_rename "));
+				old_dir->i_count++;
+				new_dir->i_count++;	/* Both inode are needed later */
+				ret = msdos_rename (old_dir
+					,old_info.fake.fname,old_info.fake.len
+					,new_dir
+					,new_info.fake.fname,new_info.fake.len);
+chkstk();
+				PRINTK (("after m_rename ret %d ",ret));
+				if (ret != 0){
+					umsdos_delentry (new_dir,&new_info
+						,S_ISDIR(new_info.entry.mode));
+chkstk();
+				}else{
+					ret = umsdos_delentry (old_dir,&old_info
+						,S_ISDIR(old_info.entry.mode));
+chkstk();
+					if (ret == 0){
+						/*
+							This UMSDOS_lookup does not look very useful.
+							It makes sure that the inode of the file will
+							be correctly setup (umsdos_patch_inode()) in
+							case it is already in use.
+
+							Not very efficient ...
+						*/
+						struct inode *inode;
+						new_dir->i_count++;
+						PRINTK (("rename lookup len %d %d -- ",new_len,new_info.entry.flags));
+						ret = UMSDOS_lookup (new_dir,new_name,new_len
+							,&inode);
+chkstk();
+						if (ret != 0){
+							printk ("UMSDOS: partial rename for file %s\n"
+								,new_info.entry.name);
+						}else{
+							/*
+								Update f_pos so notify_change will succeed
+								if the file was already in use.
+							*/
+							umsdos_set_dirinfo (inode,new_dir,new_info.f_pos);
+chkstk();
+							iput (inode);
+						}
+					}
+				}
+			}
+		}
+		umsdos_unlockcreate(old_dir);
+		umsdos_unlockcreate(new_dir);
+	}
+	iput (old_dir);
+	iput (new_dir);
+	PRINTK (("\n"));
+	return ret;
+}
+/*
+	Setup un Symbolic link or a (pseudo) hard link
+	Return a negative error code or 0 if ok.
+*/
+static int umsdos_symlink_x(
+	struct inode * dir,
+	const char * name,
+	int len,
+	const char * symname,	/* name will point to this path */
+	int mode,
+	char flags)
+{
+	/* #Specification: symbolic links / strategy
+		A symbolic link is simply a file which hold a path. It is
+		implemented as a normal MSDOS file (not very space efficient :-()
+
+		I see 2 different way to do it. One is to place the link data
+		in unused entry of the EMD file. The other is to have a separate
+		file dedicated to hold all symbolic links data.
+
+		Lets go for simplicity...
+	*/
+	struct inode *inode;
+	int ret;
+	dir->i_count++;		/* We keep the inode in case we need it */
+						/* later */
+	ret = umsdos_create_any (dir,name,len,mode,0,flags,&inode);
+	PRINTK (("umsdos_symlink ret %d ",ret));
+	if (ret == 0){
+		int len = strlen(symname);
+		struct file filp;
+		filp.f_pos = 0;
+		/* Make the inode acceptable to MSDOS */
+		ret = umsdos_file_write_kmem (inode,&filp,(char*)symname,len);
+		iput (inode);
+		if (ret >= 0){
+			if (ret != len){
+				ret = -EIO;
+				printk ("UMSDOS: "
+					"Can't write symbolic link data\n");
+			}else{
+				ret = 0;
+			}
+		}
+		if (ret != 0){
+			UMSDOS_unlink (dir,name,len);
+			dir = NULL;
+		}
+	}
+	iput (dir);
+	PRINTK (("\n"));
+	return ret;
+}
+/*
+	Setup un Symbolic link.
+	Return a negative error code or 0 if ok.
+*/
+int UMSDOS_symlink(
+	struct inode * dir,
+	const char * name,
+	int len,
+	const char * symname)	/* name will point to this path */
+{
+	return umsdos_symlink_x (dir,name,len,symname,S_IFLNK|0777,0);
+}
+/*
+	Add a link to an inode in a directory
+*/
+int UMSDOS_link (
+	struct inode * oldinode,
+	struct inode * dir,
+	const char * name,
+	int len)
+{
+	/* #Specification: hard link / strategy
+		Well ... hard link are difficult to implement on top of an
+		MsDOS fat file system. Unlike UNIX file systems, there are no
+		inode. A directory entry hold the functionnality of the inode
+		and the entry.
+
+		We will used the same strategy as a normal Unix file system
+		(with inode) except we will do it symbolicly (using paths).
+
+		Because anything can happen during a DOS session (defragment,
+		directory sorting, etc...), we can't rely on MsDOS pseudo
+		inode number to record the link. For this reason, the link
+		will be done using hidden symbolic links. The following
+		scenario illustrate how it work.
+		
+		Given a file /foo/file
+
+			ln /foo/file /tmp/file2
+
+			become internally
+
+			mv /foo/file /foo/-LINK1
+			ln -s /foo/-LINK1 /foo/file
+			ln -s /foo/-LINK1 /tmp/file2
+
+		Using this strategy, we can operate on /foo/file or /foo/file2.
+		We can remove one and keep the other, like a normal Unix hard link.
+		We can rename /foo/file ou /tmp/file2 independantly.
+			
+		The entry -LINK1 will be hidden. It will hold a link count.
+		When all link are erased, the hidden file is erased too.
+	*/
+	/* #Specification: weakness / hard link
+		The strategy for hard link introduces a side effect that
+		may or may not be acceptable. Here is the sequence
+
+		mkdir subdir1
+		touch subdir1/file
+		mkdir subdir2
+		ln    subdir1/file subdir2/file
+		rm    subdir1/file
+		rmdir subdir1
+		rmdir: subdir1: Directory not empty
+
+		This happen because there is an invisible file (--link) in
+		subdir1 which is referenced by subdir2/file.
+
+		Any idea ?
+	*/
+	/* #Specification: weakness / hard link / rename directory
+		Another weakness of hard link come from the fact that
+		it is based on hidden symbolic links. Here is an example.
+
+		mkdir /subdir1
+		touch /subdir1/file
+		mkdir /subdir2
+		ln    /subdir1/file subdir2/file
+		mv    /subdir1 subdir3
+		ls -l /subdir2/file
+
+		Since /subdir2/file is a hidden symbolic link
+		to /subdir1/..hlinkNNN, accessing it will fail since
+		/subdir1 does not exist anymore (has been renamed).
+	*/
+	int ret = 0;
+	if (S_ISDIR(oldinode->i_mode)){
+		/* #Specification: hard link / directory
+			A hard link can't be made on a directory. EPERM is returned
+			in this case.
+		*/
+		ret = -EPERM;
+	}else if ((ret = umsdos_nevercreat(dir,name,len,-EPERM))==0){
+		struct inode *olddir;
+		ret = umsdos_get_dirowner(oldinode,&olddir);
+		PRINTK (("umsdos_link dir_owner = %d -> %p [%d] "
+			,oldinode->u.umsdos_i.i_dir_owner,olddir,olddir->i_count));
+		if (ret == 0){
+			struct umsdos_dirent entry;
+			umsdos_lockcreate2(dir,olddir);
+			ret = umsdos_inode2entry (olddir,oldinode,&entry);
+			if (ret == 0){
+				PRINTK (("umsdos_link :%s: ino %d flags %d "
+					,entry.name
+					,oldinode->i_ino,entry.flags));
+				if (!(entry.flags & UMSDOS_HIDDEN)){
+					/* #Specification: hard link / first hard link
+						The first time a hard link is done on a file, this
+						file must be renamed and hidden. Then an internal
+						simbolic link must be done on the hidden file.
+
+						The second link is done after on this hidden file.
+
+						It is expected that the Linux MSDOS file system
+						keeps the same pseudo inode when a rename operation
+						is done on a file in the same directory.
+					*/
+					struct umsdos_info info;
+					ret = umsdos_newhidden (olddir,&info);
+					if (ret == 0){
+						olddir->i_count+=2;
+						PRINTK (("olddir[%d] ",olddir->i_count));
+						ret = umsdos_rename_f (olddir,entry.name
+							,entry.name_len
+							,olddir,info.entry.name,info.entry.name_len
+							,UMSDOS_HIDDEN);
+						if (ret == 0){
+							char *path = (char*)kmalloc(PATH_MAX,GFP_KERNEL);
+							if (path == NULL){
+								ret = -ENOMEM;
+							}else{
+								PRINTK (("olddir[%d] ",olddir->i_count));
+								ret = umsdos_locate_path (oldinode,path);
+								PRINTK (("olddir[%d] ",olddir->i_count));
+								if (ret == 0){
+									olddir->i_count++;
+									ret = umsdos_symlink_x (olddir
+										,entry.name
+										,entry.name_len,path
+										,S_IFREG|0777,UMSDOS_HLINK);
+									if (ret == 0){
+										dir->i_count++;
+										ret = umsdos_symlink_x (dir,name,len
+											,path
+											,S_IFREG|0777,UMSDOS_HLINK);
+									}
+								}
+								kfree (path);
+							}
+						}
+					}
+				}else{
+					char *path = (char*)kmalloc(PATH_MAX,GFP_KERNEL);
+					if (path == NULL){
+						ret = -ENOMEM;
+					}else{
+						ret = umsdos_locate_path (oldinode,path);
+						if (ret == 0){
+							dir->i_count++;
+							ret = umsdos_symlink_x (dir,name,len,path
+											,S_IFREG|0777,UMSDOS_HLINK);
+						}
+						kfree (path);
+					}
+				}
+			}
+			umsdos_unlockcreate(olddir);
+			umsdos_unlockcreate(dir);
+		}
+		iput (olddir);
+	}
+	if (ret == 0){
+		oldinode->i_nlink++;
+		ret = UMSDOS_notify_change (0,oldinode);
+	}
+	iput (oldinode);
+	iput (dir);
+	PRINTK (("umsdos_link %d\n",ret));
+	return ret;
+}
+/*
+	Add a new file into the alternate directory.
+	The file is added to the real MSDOS directory. If successfull, it
+	is then added to the EDM file.
+
+	Return the status of the operation. 0 mean success.
+*/
+int UMSDOS_create (
+	struct inode *dir,
+	const char *name,		/* Name of the file to add */
+	int len,				/* Length of the name */
+	int mode,				/* Permission bit + file type ??? */
+	struct inode **result)	/* Will hold the inode of the newly created */
+							/* file */
+{
+	return umsdos_create_any (dir,name,len,mode,0,0,result);
+}
+/*
+	Add a sub-directory in a directory
+*/
+int UMSDOS_mkdir(
+	struct inode * dir,
+	const char * name,
+	int len,
+	int mode)
+{
+	int ret = umsdos_nevercreat(dir,name,len,-EEXIST);
+	if (ret == 0){
+		struct umsdos_info info;
+		ret = umsdos_parse (name,len,&info);
+		PRINTK (("umsdos_mkdir %d\n",ret));
+		if (ret == 0){
+			info.entry.mode = mode | S_IFDIR;
+			info.entry.rdev = 0;
+			info.entry.uid = current->euid;
+			info.entry.gid = (dir->i_mode & S_ISGID)
+				? dir->i_gid : current->egid;
+			info.entry.ctime = info.entry.atime = info.entry.mtime
+				= CURRENT_TIME;
+			info.entry.flags = 0;
+			umsdos_lockcreate(dir);
+			info.entry.nlink = 1;
+			ret = umsdos_newentry (dir,&info);
+			PRINTK (("newentry %d ",ret));
+			if (ret == 0){
+				dir->i_count++;
+				ret = msdos_mkdir (dir,info.fake.fname,info.fake.len,mode);
+				if (ret != 0){
+					umsdos_delentry (dir,&info,1);
+					/* #Specification: mkdir / Directory already exist in DOS
+						We do the same thing as for file creation.
+						For all user it is an error.
+					*/
+				}else{
+					/* #Specification: mkdir / umsdos directory / create EMD
+						When we created a new sub-directory in a UMSDOS
+						directory (one with full UMSDOS semantic), we
+						create immediatly an EMD file in the new
+						sub-directory so it inherit UMSDOS semantic.
+					*/
+					struct inode *subdir;
+					ret = umsdos_real_lookup (dir,info.fake.fname
+						,info.fake.len,&subdir);
+					if (ret == 0){
+						struct inode *result;
+						ret = msdos_create (subdir,UMSDOS_EMD_FILE
+							,UMSDOS_EMD_NAMELEN,S_IFREG|0777,&result);
+						subdir = NULL;
+						iput (result);
+					}
+					if (ret < 0){
+						printk ("UMSDOS: Can't create empty --linux-.---\n");
+					}
+					iput (subdir);
+				}
+			}
+			umsdos_unlockcreate(dir);
+		}
+	}
+	PRINTK (("umsdos_mkdir %d\n",ret));
+	iput (dir);
+	return ret;
+}
+/*
+	Add a new device special file into a directory.
+*/
+int UMSDOS_mknod(
+	struct inode * dir,
+	const char * name,
+	int len,
+	int mode,
+	int rdev)
+{
+	/* #Specification: Special files / strategy
+		Device special file, pipes, etc ... are created like normal
+		file in the msdos file system. Of course they remain empty.
+
+		One strategy was to create thoses files only in the EMD file
+		since they were not important for MSDOS. The problem with
+		that, is that there were not getting inode number allocated.
+		The MSDOS filesystems is playing a nice game to fake inode
+		number, so why not use it.
+
+		The absence of inode number compatible with those allocated
+		for ordinary files was causing major trouble with hard link
+		in particular and other parts of the kernel I guess.
+	*/
+	struct inode *inode;
+	int ret = umsdos_create_any (dir,name,len,mode,rdev,0,&inode);
+	iput (inode);
+	return ret;
+}
+
+/*
+	Remove a sub-directory.
+*/
+int UMSDOS_rmdir(
+	struct inode * dir,
+	const char * name,
+	int len)
+{
+	/* #Specification: style / iput strategy
+		In the UMSDOS project, I am trying to apply a single
+		programming style regarding inode management. Many
+		entry point are receiving an inode to act on, and must
+		do an iput() as soon as they are finished with
+		the inode.
+
+		For simple case, there is no problem. When you introduce
+		error checking, you end up with many iput placed around the
+		code.
+
+		The coding style I use all around is one where I am trying
+		to provide independant flow logic (I don't know how to
+		name this). With this style, code is easier to understand
+		but you rapidly get iput() all around. Here is an exemple
+		of what I am trying to avoid.
+
+		if (a){
+			...
+			if(b){
+				...
+			}
+			...
+			if (c){
+				// Complexe state. Was b true ? 
+				...
+			}
+			...
+		}
+		// Weird state
+		if (d){
+			// ...
+		}
+		// Was iput finally done ?
+		return status;
+
+		Here is the style I am using. Still sometime I do the
+		first when things are very simple (or very complicated :-( )
+
+		if (a){
+			if (b){
+				...
+			}else if (c){
+				// A single state gets here
+			}
+		}else if (d){
+			...
+		}
+		return status;
+
+		Again, while this help clarifying the code, I often get a lot
+		of iput(), unlike the first style, where I can place few 
+		"strategic" iput(). "strategic" also mean, more difficult
+		to place.
+
+		So here is the style I will be using from now on in this project.
+		There is always an iput() at the end of a function (which has
+		to do an iput()). One iput by inode. There is also one iput()
+		at the places where a successful operation is achieved. This
+		iput() is often done by a sub-function (often from the msdos
+		file system). So I get one too many iput() ? At the place
+		where an iput() is done, the inode is simply nulled, disabling
+		the last one.
+
+		if (a){
+			if (b){
+				...
+			}else if (c){
+				msdos_rmdir(dir,...);
+				dir = NULL;
+			}
+		}else if (d){
+			...
+		}
+		iput (dir);
+		return status;
+
+		Note that the umsdos_lockcreate() and umsdos_unlockcreate() function
+		paire goes against this practice of "forgetting" the inode as soon
+		as possible.
+	*/		
+	int ret = umsdos_nevercreat(dir,name,len,-EPERM);
+	if (ret == 0){
+		struct inode *sdir;
+		dir->i_count++;
+		ret = UMSDOS_lookup (dir,name,len,&sdir);
+		PRINTK (("rmdir lookup %d ",ret));
+		if (ret == 0){
+			int empty;
+			umsdos_lockcreate(dir);
+			if ((empty = umsdos_isempty (sdir)) != 0){
+				PRINTK (("isempty %d i_count %d ",empty,sdir->i_count));
+				if (empty == 1){
+					/* We have to removed the EMD file */
+					ret = msdos_unlink(sdir,UMSDOS_EMD_FILE
+						,UMSDOS_EMD_NAMELEN);
+					sdir = NULL;
+				}
+				/* sdir must be free before msdos_rmdir() */
+				iput (sdir);
+				sdir = NULL;
+				PRINTK (("isempty ret %d nlink %d ",ret,dir->i_nlink));
+				if (ret == 0){
+					struct umsdos_info info;
+					dir->i_count++;
+					umsdos_parse (name,len,&info);
+					/* The findentry is there only to complete */
+					/* the mangling */
+					umsdos_findentry (dir,&info,2);
+					ret = msdos_rmdir (dir,info.fake.fname
+						,info.fake.len);
+					if (ret == 0){
+						ret = umsdos_delentry (dir,&info,1);
+					}
+				}
+			}else{	
+				/*
+					The subdirectory is not empty, so leave it there
+				*/
+				ret = -ENOTEMPTY;
+			}
+			iput(sdir);
+			umsdos_unlockcreate(dir);
+		}	
+	}
+	iput (dir);
+	PRINTK (("umsdos_rmdir %d\n",ret));
+	return ret;
+}
+/*
+	Remove a file from the directory.
+*/
+int UMSDOS_unlink (
+	struct inode * dir,
+	const char * name,
+	int len)
+{
+	struct umsdos_info info;
+	int ret = umsdos_nevercreat(dir,name,len,-EPERM);
+	if (ret == 0){
+		ret = umsdos_parse (name,len,&info);
+		if (ret == 0){
+			umsdos_lockcreate(dir);
+			ret = umsdos_findentry(dir,&info,1);
+			if (ret == 0){
+				PRINTK (("UMSDOS_unlink %s ",info.fake.fname));
+				if (info.entry.flags & UMSDOS_HLINK){
+					/* #Specification: hard link / deleting a link
+						When we deletes a file, and this file is a link
+						we must substract 1 to the nlink field of the
+						hidden link.
+
+						If the count goes to 0, we delete this hidden
+						link too.
+					*/
+					/*
+						First, get the inode of the hidden link
+						using the standard lookup function.
+					*/
+					struct inode *inode;
+					dir->i_count++;
+					ret = UMSDOS_lookup (dir,name,len,&inode);
+					if (ret == 0){
+						PRINTK (("unlink nlink = %d ",inode->i_nlink));
+						inode->i_nlink--;
+						if (inode->i_nlink == 0){
+							struct inode *hdir = iget(inode->i_sb
+								,inode->u.umsdos_i.i_dir_owner);
+							struct umsdos_dirent entry;
+							ret = umsdos_inode2entry (hdir,inode,&entry);
+							if (ret == 0){
+								ret = UMSDOS_unlink (hdir,entry.name
+									,entry.name_len);
+							}else{
+								iput (hdir);
+							}
+						}else{
+							ret = UMSDOS_notify_change (0,inode);
+						}
+						iput (inode);
+					}
+				}
+				if (ret == 0){
+					ret = umsdos_delentry (dir,&info,0);
+					if (ret == 0){
+						PRINTK (("Avant msdos_unlink %s ",info.fake.fname));
+						dir->i_count++;
+						ret = msdos_unlink_umsdos (dir,info.fake.fname
+							,info.fake.len);
+						PRINTK (("msdos_unlink %s %o ret %d ",info.fake.fname
+							,info.entry.mode,ret));
+					}
+				}
+			}
+			umsdos_unlockcreate(dir);
+		}
+	}	
+	iput (dir);
+	PRINTK (("umsdos_unlink %d\n",ret));
+	return ret;
+}
+
+/*
+	Rename a file (move) in the file system.
+*/
+int UMSDOS_rename(
+	struct inode * old_dir,
+	const char * old_name,
+	int old_len,
+	struct inode * new_dir,
+	const char * new_name,
+	int new_len)
+{
+	/* #Specification: weakness / rename
+		There is a case where UMSDOS rename has a different behavior
+		than normal UNIX file system. Renaming an open file across
+		directory boundary does not work. Renaming an open file within
+		a directory does work however.
+
+		The problem (not sure) is in the linux VFS msdos driver.
+		I believe this is not a bug but a design feature, because
+		an inode number represent some sort of directory address
+		in the MSDOS directory structure. So moving the file into
+		another directory does not preserve the inode number.
+	*/
+	int ret = umsdos_nevercreat(new_dir,new_name,new_len,-EEXIST);
+	if (ret == 0){
+		/* umsdos_rename_f eat the inode and we may need those later */
+		old_dir->i_count++;
+		new_dir->i_count++;
+		ret = umsdos_rename_f (old_dir,old_name,old_len,new_dir,new_name
+			,new_len,0);
+		if (ret == -EEXIST){
+			/* #Specification: rename / new name exist
+				If the destination name already exist, it will
+				silently be removed. EXT2 does it this way
+				and this is the spec of SUNOS. So does UMSDOS.
+
+				If the destination is an empty directory it will
+				also be removed.
+			*/
+			/* #Specification: rename / new name exist / possible flaw
+				The code to handle the deletion of the target (file
+				and directory) use to be in umsdos_rename_f, surrounded
+				by proper directory locking. This was insuring that only
+				one process could achieve a rename (modification) operation
+				in the source and destination directory. This was also
+				insuring the operation was "atomic".
+
+				This has been changed because this was creating a kernel
+				stack overflow (stack is only 4k in the kernel). To avoid
+				the code doing the deletion of the target (if exist) has
+				been moved to a upper layer. umsdos_rename_f is tried
+				once and if it fails with EEXIST, the target is removed
+				and umsdos_rename_f is done again.
+
+				This makes the code cleaner and (not sure) solve a
+				deadlock problem one tester was experiencing.
+
+				The point is to mention that possibly, the semantic of
+				"rename" may be wrong. Anyone dare to check that :-)
+				Be aware that IF it is wrong, to produce the problem you
+				will need two process trying to rename a file to the
+				same target at the same time. Again, I am not sure it
+				is a problem at all.
+			*/
+			/* This is not super efficient but should work */
+			new_dir->i_count++;
+			ret = UMSDOS_unlink (new_dir,new_name,new_len);
+chkstk();
+			PRINTK (("rename unlink ret %d %d -- ",ret,new_len));
+			if (ret == -EISDIR){
+				new_dir->i_count++;
+				ret = UMSDOS_rmdir (new_dir,new_name,new_len);
+chkstk();
+				PRINTK (("rename rmdir ret %d -- ",ret));
+			}
+			if (ret == 0){
+				ret = umsdos_rename_f (old_dir,old_name,old_len
+					,new_dir,new_name,new_len,0);
+				new_dir = old_dir = NULL;
+			}
+		}
+	}
+	iput (new_dir);
+	iput (old_dir);
+	return ret;
+}
+
diff -u --recursive --new-file v1.1.34/linux/fs/umsdos/notes linux/fs/umsdos/notes
--- v1.1.34/linux/fs/umsdos/notes	Thu Jan  1 02:00:00 1970
+++ linux/fs/umsdos/notes	Sun Jul 24 17:39:45 1994
@@ -0,0 +1,17 @@
+This file contain idea and things I don't want to forget
+
+Possible bug in fs/read_write.c
+Function sys_readdir()
+
+	There is a call the verify_area that does not take in account
+	the count parameter. I guess it should read
+
+	error = verify_area(VERIFY_WRITE, dirent, count*sizeof (*dirent));
+
+	instead of
+
+	error = verify_area(VERIFY_WRITE, dirent, sizeof (*dirent));
+
+	Of course, now , count is always 1
+
+
diff -u --recursive --new-file v1.1.34/linux/fs/umsdos/rdir.c linux/fs/umsdos/rdir.c
--- v1.1.34/linux/fs/umsdos/rdir.c	Thu Jan  1 02:00:00 1970
+++ linux/fs/umsdos/rdir.c	Sun Jul 24 17:39:46 1994
@@ -0,0 +1,239 @@
+/*
+ *  linux/fs/umsdos/rdir.c
+ *
+ *  Written 1994 by Jacques Gelinas
+ *
+ *  Extended MS-DOS directory pure MS-DOS handling functions
+ *  (For directory without EMD file).
+ */
+
+#include <asm/segment.h>
+
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/msdos_fs.h>
+#include <linux/errno.h>
+#include <linux/stat.h>
+#include <linux/limits.h>
+#include <linux/umsdos_fs.h>
+#include <linux/malloc.h>
+
+#define PRINTK(x)
+#define Printk(x) printk x
+
+
+extern struct inode *pseudo_root;
+
+static int UMSDOS_rreaddir (
+	struct inode *dir,
+	struct file *filp,
+    struct dirent *dirent,
+	int count)
+{
+	int ret = 0;
+	while (1){
+		ret = msdos_readdir(dir,filp,dirent,count);
+		if (ret == 5
+			&& pseudo_root != NULL
+			&& dir->i_sb->s_mounted == pseudo_root->i_sb->s_mounted){
+			/*
+				In pseudo root mode, we must eliminate logically
+				the directory linux from the real root.
+			*/
+			char name[5];
+			memcpy_fromfs (name,dirent->d_name,5);
+			if (memcmp(name,UMSDOS_PSDROOT_NAME,UMSDOS_PSDROOT_LEN)!=0) break;
+		}else{
+			if (pseudo_root != NULL
+				&& ret == 2
+				&& dir == dir->i_sb->s_mounted
+				&& dir == pseudo_root->i_sb->s_mounted){
+				char name[2];
+				memcpy_fromfs (name,dirent->d_name,2);
+				if (name[0] == '.' && name[1] == '.'){
+					put_fs_long (pseudo_root->i_ino,&dirent->d_ino);
+				}
+			}
+			break;
+		}
+	}
+	return ret;
+}
+
+int UMSDOS_rlookup(
+	struct inode *dir,
+	const char *name,
+	int len,
+	struct inode **result)	/* Will hold inode of the file, if successful */
+{
+	int ret;
+	if (pseudo_root != NULL
+		&& len == 2
+		&& name[0] == '.'
+		&& name[1] == '.'
+		&& dir == dir->i_sb->s_mounted
+		&& dir == pseudo_root->i_sb->s_mounted){
+		*result = pseudo_root;
+		pseudo_root->i_count++;
+		ret = 0;
+		/* #Specification: pseudo root / DOS/..
+			In the real root directory (c:\), the directory ..
+			is the pseudo root (c:\linux).
+		*/
+	}else{
+		ret = umsdos_real_lookup (dir,name,len,result);
+		if (ret == 0){
+			struct inode *inode = *result;
+			if (inode == pseudo_root){
+				/* #Specification: pseudo root / DOS/linux
+					Even in the real root directory (c:\), the directory
+					/linux won't show
+				*/
+				ret = -ENOENT;
+				iput (pseudo_root);
+				*result = NULL;
+			}else if (S_ISDIR(inode->i_mode)){
+				/* We must place the proper function table */
+				/* depending if this is a MsDOS directory or an UMSDOS directory */
+				umsdos_setup_dir_inode(inode);
+			}
+		}
+	}
+	iput (dir);
+	return ret;
+}
+
+static int UMSDOS_rrmdir (
+	struct inode *dir,
+	const char *name,
+	int len)
+{
+	/* #Specification: dual mode / rmdir in a DOS directory
+		In a DOS (not EMD in it) directory, we use a reverse strategy
+		compared with an Umsdos directory. We assume that a subdirectory
+		of a DOS directory is also a DOS directory. This is not always
+		true (umssync may be used anywhere), but make sense.
+
+		So we call msdos_rmdir() directly. If it failed with a -ENOTEMPTY
+		then we check if it is a Umsdos directory. We check if it is
+		really empty (only . .. and --linux-.--- in it). If it is true
+		we remove the EMD and do a msdos_rmdir() again.
+
+		In a Umsdos directory, we assume all subdirectory are also
+		Umsdos directory, so we check the EMD file first.
+	*/
+	int ret;
+	if (umsdos_is_pseudodos(dir,name,len)){
+		/* #Specification: pseudo root / rmdir /DOS
+			The pseudo sub-directory /DOS can't be removed!
+			This is done even if the pseudo root is not a Umsdos
+			directory anymore (very unlikely), but an accident (under
+			MsDOS) is always possible.
+
+			EPERM is returned.
+		*/
+		ret = -EPERM;
+	}else{
+		umsdos_lockcreate (dir);
+		dir->i_count++;
+		ret = msdos_rmdir (dir,name,len);
+		if (ret == -ENOTEMPTY){
+			struct inode *sdir;
+			dir->i_count++;
+			ret = UMSDOS_rlookup (dir,name,len,&sdir);
+			PRINTK (("rrmdir lookup %d ",ret));
+			if (ret == 0){
+				int empty;
+				if ((empty = umsdos_isempty (sdir)) != 0){
+					PRINTK (("isempty %d i_count %d ",empty,sdir->i_count));
+					if (empty == 2){
+						/*
+							Not a Umsdos directory, so the previous msdos_rmdir
+							was not lying :-)
+						*/
+						ret = -ENOTEMPTY;
+					}else if (empty == 1){
+						/* We have to removed the EMD file */
+						ret = msdos_unlink(sdir,UMSDOS_EMD_FILE
+							,UMSDOS_EMD_NAMELEN);
+						sdir = NULL;
+						if (ret == 0){
+							dir->i_count++;
+							ret = msdos_rmdir (dir,name,len);
+						}
+					}
+				}else{
+					ret = -ENOTEMPTY;
+				}
+				iput (sdir);
+			}
+		}
+		umsdos_unlockcreate (dir);
+	}
+	iput (dir);
+	return ret;
+}
+
+/* #Specification: dual mode / introduction
+	One goal of UMSDOS is to allow a practical and simple coexistence
+	between MsDOS and Linux in a single partition. Using the EMD file
+	in each directory, UMSDOS add Unix semantics and capabilities to
+	normal DOS file system. To help and simplify coexistence, here is
+	the logic related to the EMD file.
+
+	If it is missing, then the directory is managed by the MsDOS driver.
+	The names are limited to DOS limits (8.3). No links, no device special
+	and pipe and so on.
+
+	If it is there, it is the directory. If it is there but empty, then
+	the directory looks empty. The utility umssync allows synchronisation
+	of the real DOS directory and the EMD.
+
+	Whenever umssync is applied to a directory without EMD, one is
+	created on the fly. The directory is promoted to full unix semantic.
+	Of course, the ls command will show exactly the same content as before
+	the umssync session.
+
+	It is believed that the user/admin will promote directories to unix
+	semantic as needed.
+
+	The strategy to implement this is to use two function table (struct
+	inode_operations). One for true UMSDOS directory and one for directory
+	with missing EMD.
+
+	Functions related to the DOS semantic (but aware of UMSDOS) generally
+	have a "r" prefix (r for real) such as UMSDOS_rlookup, to differentiate
+	from the one with full UMSDOS semantic.
+*/
+static struct file_operations umsdos_rdir_operations = {
+	NULL,				/* lseek - default */
+	UMSDOS_dir_read,	/* read */
+	NULL,				/* write - bad */
+	UMSDOS_rreaddir,	/* readdir */
+	NULL,				/* select - default */
+	UMSDOS_ioctl_dir,	/* ioctl - default */
+	NULL,				/* mmap */
+	NULL,				/* no special open code */
+	NULL,				/* no special release code */
+	NULL				/* fsync */
+};
+
+struct inode_operations umsdos_rdir_inode_operations = {
+	&umsdos_rdir_operations,	/* default directory file-ops */
+	msdos_create,		/* create */
+	UMSDOS_rlookup,		/* lookup */
+	NULL,				/* link */
+	msdos_unlink,		/* unlink */
+	NULL,				/* symlink */
+	msdos_mkdir,		/* mkdir */
+	UMSDOS_rrmdir,		/* rmdir */
+	NULL,				/* mknod */
+	msdos_rename,		/* rename */
+	NULL,				/* readlink */
+	NULL,				/* follow_link */
+	NULL,				/* bmap */
+	NULL,				/* truncate */
+	NULL				/* permission */
+};
+
+
diff -u --recursive --new-file v1.1.34/linux/fs/umsdos/symlink.c linux/fs/umsdos/symlink.c
--- v1.1.34/linux/fs/umsdos/symlink.c	Thu Jan  1 02:00:00 1970
+++ linux/fs/umsdos/symlink.c	Sun Jul 24 17:39:46 1994
@@ -0,0 +1,144 @@
+/*
+ *  linux/fs/umsdos/file.c
+ *
+ *  Written 1992 by Jacques Gelinas
+ *	inpired from linux/fs/msdos/file.c Werner Almesberger
+ *
+ *  Extended MS-DOS regular file handling primitives
+ */
+
+#include <asm/segment.h>
+#include <asm/system.h>
+
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/msdos_fs.h>
+#include <linux/errno.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <linux/umsdos_fs.h>
+#include <linux/malloc.h>
+
+#define PRINTK(x)
+#define Printk(x)	printk x
+/*
+	Read the data associate with the symlink.
+	Return lenght read in buffer or  a negative error code.
+*/
+static int umsdos_readlink_x (
+	struct inode *inode,
+	char *buffer,
+	int (*msdos_read)(struct inode *, struct file *, char *, int),
+	int bufsiz)
+{
+	int ret = inode->i_size;
+	struct file filp;
+	filp.f_pos = 0;
+	if (ret > bufsiz) ret = bufsiz;
+	if ((*msdos_read) (inode, &filp, buffer,ret) != ret){
+		ret = -EIO;
+	}
+	return ret;
+}
+/*
+	Follow a symbolic link chain by calling open_namei recursivly
+	until an inode is found.
+
+	Return 0 if ok, or a negative error code if not.
+*/
+static int UMSDOS_follow_link(
+	struct inode * dir,
+	struct inode * inode,
+	int flag,
+	int mode,
+	struct inode ** res_inode)
+{
+	int ret = -ELOOP;
+	*res_inode = NULL;
+	if (current->link_count < 5) {
+		char *path = (char*)kmalloc(PATH_MAX,GFP_KERNEL);
+		if (path == NULL){
+			ret = -ENOMEM;
+		}else{
+			if (!dir) {
+				dir = current->fs[1].root;
+				dir->i_count++;
+			}
+			if (!inode){
+				PRINTK (("symlink: inode = NULL\n"));
+				ret = -ENOENT;
+			}else if (!S_ISLNK(inode->i_mode)){
+				PRINTK (("symlink: Not ISLNK\n"));
+				*res_inode = inode;
+				inode = NULL;
+				ret = 0;
+			}else{
+				ret = umsdos_readlink_x (inode,path
+					,umsdos_file_read_kmem,PATH_MAX-1);
+				if (ret > 0){
+					path[ret] = '\0';
+					PRINTK (("follow :%s: %d ",path,ret));
+					iput(inode);
+					inode = NULL;
+					current->link_count++;
+					ret = open_namei(path,flag,mode,res_inode,dir);
+					current->link_count--;
+					dir = NULL;
+				}else{
+					ret = -EIO;
+				}
+			}
+			kfree (path);
+		}
+	}	
+	iput(inode);
+	iput(dir);
+	PRINTK (("follow_link ret %d\n",ret));
+	return ret;
+}
+
+static int UMSDOS_readlink(struct inode * inode, char * buffer, int buflen)
+{
+	int ret = -EINVAL;
+	if (S_ISLNK(inode->i_mode)) {
+		ret = umsdos_readlink_x (inode,buffer,msdos_file_read,buflen);
+	}
+	PRINTK (("readlink %d %x bufsiz %d\n",ret,inode->i_mode,buflen));
+	iput(inode);
+	return ret;
+	
+}
+
+static struct file_operations umsdos_symlink_operations = {
+	NULL,				/* lseek - default */
+	NULL,				/* read */
+	NULL,				/* write */
+	NULL,				/* readdir - bad */
+	NULL,				/* select - default */
+	NULL,				/* ioctl - default */
+	NULL,				/* mmap */
+	NULL,				/* no special open is needed */
+	NULL,				/* release */
+	NULL				/* fsync */
+};
+
+struct inode_operations umsdos_symlink_inode_operations = {
+	&umsdos_symlink_operations,	/* default file operations */
+	NULL,			/* create */
+	NULL,			/* lookup */
+	NULL,			/* link */
+	NULL,			/* unlink */
+	NULL,			/* symlink */
+	NULL,			/* mkdir */
+	NULL,			/* rmdir */
+	NULL,			/* mknod */
+	NULL,			/* rename */
+	UMSDOS_readlink,	/* readlink */
+	UMSDOS_follow_link,	/* follow_link */
+	NULL,			/* bmap */
+	NULL,			/* truncate */
+	NULL			/* permission */
+};
+
+
+
diff -u --recursive --new-file v1.1.34/linux/include/linux/ext_fs.h linux/include/linux/ext_fs.h
--- v1.1.34/linux/include/linux/ext_fs.h	Wed Dec  1 14:44:15 1993
+++ linux/include/linux/ext_fs.h	Sun Jul 24 17:39:46 1994
@@ -59,6 +59,7 @@
 	char name[EXT_NAME_LEN];
 };
 
+#ifdef __KERNEL__
 extern int ext_open(struct inode * inode, struct file * filp);
 extern void ext_release(struct inode * inode, struct file * filp);
 extern int ext_lookup(struct inode * dir,const char * name, int len,
@@ -105,4 +106,5 @@
 extern struct inode_operations ext_dir_inode_operations;
 extern struct inode_operations ext_symlink_inode_operations;
 
+#endif /*__KERNEL__ */
 #endif
diff -u --recursive --new-file v1.1.34/linux/include/linux/fs.h linux/include/linux/fs.h
--- v1.1.34/linux/include/linux/fs.h	Sat Jul 23 17:38:37 1994
+++ linux/include/linux/fs.h	Sun Jul 24 17:39:46 1994
@@ -158,11 +158,14 @@
 #include <linux/ext2_fs_i.h>
 #include <linux/hpfs_fs_i.h>
 #include <linux/msdos_fs_i.h>
+#include <linux/umsdos_fs_i.h>
 #include <linux/iso_fs_i.h>
 #include <linux/nfs_fs_i.h>
 #include <linux/xia_fs_i.h>
 #include <linux/sysv_fs_i.h>
 
+#ifdef __KERNEL__
+
 struct inode {
 	dev_t		i_dev;
 	unsigned long	i_ino;
@@ -203,6 +206,7 @@
 		struct ext2_inode_info ext2_i;
 		struct hpfs_inode_info hpfs_i;
 		struct msdos_inode_info msdos_i;
+		struct umsdos_inode_info umsdos_i;
 		struct iso_inode_info isofs_i;
 		struct nfs_inode_info nfs_i;
 		struct xiafs_inode_info xiafs_i;
@@ -313,6 +317,7 @@
 	int (*bmap) (struct inode *,int);
 	void (*truncate) (struct inode *);
 	int (*permission) (struct inode *, int);
+	int (*smap) (struct inode *,int);
 };
 
 struct super_operations {
@@ -332,8 +337,6 @@
 	int requires_dev;
 	struct file_system_type * next;
 };
-
-#ifdef __KERNEL__
 
 extern int register_filesystem(struct file_system_type *);
 extern int unregister_filesystem(struct file_system_type *);
diff -u --recursive --new-file v1.1.34/linux/include/linux/iso_fs.h linux/include/linux/iso_fs.h
--- v1.1.34/linux/include/linux/iso_fs.h	Wed Dec  1 14:44:15 1993
+++ linux/include/linux/iso_fs.h	Sun Jul 24 17:39:46 1994
@@ -117,22 +117,6 @@
 	char name			[0];
 };
 
-extern int isonum_711(char *);
-extern int isonum_712(char *);
-extern int isonum_721(char *);
-extern int isonum_722(char *);
-extern int isonum_723(char *);
-extern int isonum_731(char *);
-extern int isonum_732(char *);
-extern int isonum_733(char *);
-extern int iso_date(char *, int);
-
-extern int parse_rock_ridge_inode(struct iso_directory_record *, struct inode *);
-extern int get_rock_ridge_filename(struct iso_directory_record *, char ** name, int * len, struct inode *);
-
-extern char * get_rock_ridge_symlink(struct inode *);
-extern int find_rock_ridge_relocation(struct iso_directory_record *, struct inode *);
-
 #define ISOFS_BLOCK_BITS 11
 #define ISOFS_BLOCK_SIZE 2048
 
@@ -160,7 +144,23 @@
 #define ISOFS_FILE_BINARY 2
 #define ISOFS_FILE_TEXT_M 3
 
+#ifdef __KERNEL__
+extern int isonum_711(char *);
+extern int isonum_712(char *);
+extern int isonum_721(char *);
+extern int isonum_722(char *);
+extern int isonum_723(char *);
+extern int isonum_731(char *);
+extern int isonum_732(char *);
+extern int isonum_733(char *);
+extern int iso_date(char *, int);
+
+extern int parse_rock_ridge_inode(struct iso_directory_record *, struct inode *);
+extern int get_rock_ridge_filename(struct iso_directory_record *, char ** name, int * len, struct inode *);
 
+extern char * get_rock_ridge_symlink(struct inode *);
+extern int find_rock_ridge_relocation(struct iso_directory_record *, struct inode *);
+
 /* The stuff that follows may be totally unneeded. I have not checked to see 
  which prototypes we are still using.  */
 
@@ -211,6 +211,8 @@
 extern struct buffer_head * leak_check_bread(int dev, int block, int size);
 extern void leak_check_brelse(struct buffer_head * bh);
 #endif /* LEAK_CHECK */
+
+#endif /* __KERNEL__ */
 
 #endif
 
diff -u --recursive --new-file v1.1.34/linux/include/linux/minix_fs.h linux/include/linux/minix_fs.h
--- v1.1.34/linux/include/linux/minix_fs.h	Mon Jan  3 10:40:41 1994
+++ linux/include/linux/minix_fs.h	Sun Jul 24 17:39:46 1994
@@ -75,6 +75,8 @@
 	char name[0];
 };
 
+#ifdef __KERNEL__
+
 extern int minix_lookup(struct inode * dir,const char * name, int len,
 	struct inode ** result);
 extern int minix_create(struct inode * dir,const char * name, int len, int mode,
@@ -115,5 +117,7 @@
 extern struct inode_operations minix_file_inode_operations;
 extern struct inode_operations minix_dir_inode_operations;
 extern struct inode_operations minix_symlink_inode_operations;
+
+#endif /* __KERNEL__ */
 
 #endif
diff -u --recursive --new-file v1.1.34/linux/include/linux/mm.h linux/include/linux/mm.h
--- v1.1.34/linux/include/linux/mm.h	Sat Jul 23 17:38:38 1994
+++ linux/include/linux/mm.h	Sun Jul 24 17:39:46 1994
@@ -92,6 +92,8 @@
 };
 /* end of planning stage */
 
+#ifdef __KERNEL__
+
 /*
  * Free area management
  */
@@ -221,21 +223,76 @@
 #define GFP_NOBUFFER	0x04
 
 
-/* vm_ops not present page codes */
+/*
+ * vm_ops not present page codes for shared memory.
+ *
+ * Will go away eventually..
+ */
 #define SHM_SWP_TYPE 0x41
 extern void shm_no_page (ulong *);
 
-/* swap cache stuff (in swap.c) */
+/*
+ * swap cache stuff (in swap.c)
+ */
+#define SWAP_CACHE_INFO
+
 extern unsigned long * swap_cache;
 
+#ifdef SWAP_CACHE_INFO
+extern unsigned long swap_cache_add_total;
+extern unsigned long swap_cache_add_success;
+extern unsigned long swap_cache_del_total;
+extern unsigned long swap_cache_del_success;
+extern unsigned long swap_cache_find_total;
+extern unsigned long swap_cache_find_success;
+#endif
+
 extern inline unsigned long in_swap_cache(unsigned long addr)
 {
 	return swap_cache[addr >> PAGE_SHIFT]; 
 }
 
-extern inline void swap_cache_invalidate(unsigned long addr)
+extern inline long find_in_swap_cache (unsigned long addr)
+{
+	unsigned long entry;
+
+#ifdef SWAP_CACHE_INFO
+	swap_cache_find_total++;
+#endif
+	__asm__ __volatile__("xchgl %0,%1"
+		:"=m" (swap_cache[addr >> PAGE_SHIFT]),
+		 "=r" (entry)
+		:"0" (swap_cache[addr >> PAGE_SHIFT]),
+		 "1" (0));
+#ifdef SWAP_CACHE_INFO
+	if (entry)
+		swap_cache_find_success++;
+#endif	
+	return entry;
+}
+
+extern inline int delete_from_swap_cache(unsigned long addr)
 {
-	swap_cache[addr >> PAGE_SHIFT] = 0;
+	unsigned long entry;
+	
+#ifdef SWAP_CACHE_INFO
+	swap_cache_del_total++;
+#endif	
+	__asm__ __volatile__("xchgl %0,%1"
+		:"=m" (swap_cache[addr >> PAGE_SHIFT]),
+		 "=r" (entry)
+		:"0" (swap_cache[addr >> PAGE_SHIFT]),
+		 "1" (0));
+	if (entry)  {
+#ifdef SWAP_CACHE_INFO
+		swap_cache_del_success++;
+#endif
+		swap_free(entry);
+		return 1;
+	}
+	return 0;
 }
+
+#endif /* __KERNEL__ */
 
 #endif
diff -u --recursive --new-file v1.1.34/linux/include/linux/msdos_fs.h linux/include/linux/msdos_fs.h
--- v1.1.34/linux/include/linux/msdos_fs.h	Wed Dec  1 14:44:15 1993
+++ linux/include/linux/msdos_fs.h	Sun Jul 24 17:39:46 1994
@@ -4,7 +4,6 @@
 /*
  * The MS-DOS filesystem constants/structures
  */
-
 #include <linux/fs.h>
 #include <linux/stat.h>
 #include <linux/fd.h>
@@ -109,6 +108,7 @@
 
 #define MSDOS_MKATTR(m) ((m & S_IWUGO) ? ATTR_NONE : ATTR_RO)
 
+#ifdef __KERNEL__
 
 static inline struct buffer_head *msdos_sread(int dev,int sector,void **start)
 {
@@ -161,6 +161,7 @@
 extern int msdos_mkdir(struct inode *dir,const char *name,int len,int mode);
 extern int msdos_rmdir(struct inode *dir,const char *name,int len);
 extern int msdos_unlink(struct inode *dir,const char *name,int len);
+extern int msdos_unlink_umsdos(struct inode *dir,const char *name,int len);
 extern int msdos_rename(struct inode *old_dir,const char *old_name,int old_len,
 	struct inode *new_dir,const char *new_name,int new_len);
 
@@ -179,12 +180,21 @@
 /* dir.c */
 
 extern struct inode_operations msdos_dir_inode_operations;
-
+extern int msdos_readdir (struct inode *inode, struct file *filp,
+	struct dirent *dirent, int count);
 /* file.c */
 
 extern struct inode_operations msdos_file_inode_operations;
+extern int msdos_file_read(struct inode *, struct file *, char *, int);
+extern int msdos_file_write(struct inode *, struct file *, char *, int);
 extern struct inode_operations msdos_file_inode_operations_no_bmap;
 
 extern void msdos_truncate(struct inode *inode);
+
+/* mmap.c */
+extern int msdos_mmap (struct inode *, struct file *, unsigned long, size_t
+	,int , unsigned long);
+
+#endif /* __KERNEL__ */
 
 #endif
diff -u --recursive --new-file v1.1.34/linux/include/linux/msdos_fs_i.h linux/include/linux/msdos_fs_i.h
--- v1.1.34/linux/include/linux/msdos_fs_i.h	Wed Dec  1 14:44:15 1993
+++ linux/include/linux/msdos_fs_i.h	Sun Jul 24 17:39:46 1994
@@ -1,11 +1,33 @@
 #ifndef _MSDOS_FS_I
 #define _MSDOS_FS_I
 
+#ifndef _LINUX_CONFIG_H
+#include <linux/config.h>
+#endif
+#ifndef _LINUX_PIPE_FS_I_H
+#include <linux/pipe_fs_i.h>
+#endif
+
 /*
  * MS-DOS file system inode data in memory
  */
 
 struct msdos_inode_info {
+	/*
+		UMSDOS manage special file and fifo as normal empty
+		msdos file. fifo inode processing conflict with msdos
+		processing. So I insert the pipe_inode_info so the
+		information does not overlap. This increases the size of
+		the msdos_inode_info, but the clear winner here is
+		the ext2_inode_info. So it does not change anything to
+		the total size of a struct inode.
+
+		I have not put it conditionnal. With the advent of loadable
+		file system drivers, it would be very easy to compile
+		a MsDOS FS driver unaware of UMSDOS and then later to
+		load a (then incompatible) UMSDOS FS driver.
+	*/
+	struct pipe_inode_info reserved;
 	int i_start;	/* first cluster or 0 */
 	int i_attrs;	/* unused attribute bits */
 	int i_busy;	/* file is either deleted but still open, or
diff -u --recursive --new-file v1.1.34/linux/include/linux/mtio.h linux/include/linux/mtio.h
--- v1.1.34/linux/include/linux/mtio.h	Fri Jul  8 13:26:41 1994
+++ linux/include/linux/mtio.h	Sat Jul 23 12:58:37 1994
@@ -1,4 +1,4 @@
-/* $Id: mtio.h,v 1.4 1992/11/18 01:32:03 root Exp root $
+/* $Id: mtio.h,v 1.13 1994/07/19 19:35:52 root Exp $
  *
  * linux/mtio.h header file for Linux. Written by H. Bergman
  */
@@ -47,7 +47,7 @@
 #define MTSETBLK 20	/* set block length (SCSI) */
 #define MTSETDENSITY 21	/* set tape density (SCSI) */
 #define MTSEEK	22	/* seek to block (Tandberg, etc.) */
-#define MTTELL	23	/* tell block (Tandber, etc.) */
+#define MTTELL	23	/* tell block (Tandberg, etc.) */
 #define MTSETDRVBUFFER 24 /* set the drive buffering according to SCSI-2 */
 			/* ordinary buffered operation with code 1 */
 
@@ -70,8 +70,11 @@
 	daddr_t	mt_blkno;	/* current block number */
 };
 
+
+
 /*
- * Constants for mt_type. Not all of these are supported.
+ * Constants for mt_type. Not all of these are supported,
+ * and these are not all of the ones that are supported.
  */
 #define MT_ISUNKNOWN		0x01
 #define MT_ISQIC02		0x02	/* Generic QIC-02 tape streamer */
@@ -82,8 +85,10 @@
 #define MT_ISARCHIVE_VP60I	0x07	/* Archive VP60i, QIC-02 */
 #define MT_ISARCHIVE_2150L	0x08	/* Archive Viper 2150L */
 #define MT_ISARCHIVE_2060L	0x09	/* Archive Viper 2060L */
+#define MT_ISARCHIVESC499	0x0A	/* Archive SC-499 QIC-36 controller */
 #define MT_ISQIC02_ALL_FEATURES	0x0F	/* Generic QIC-02 with all features */
 #define MT_ISWT5099EEN24	0x11	/* Wangtek 5099-een24, 60MB, QIC-24 */
+#define MT_ISTEAC_MT2ST		0x12	/* Teac MT-2ST 155mb drive, Teac DC-1 card (Wangtek type) */
 #define MT_ISEVEREX_FT40A	0x32	/* Everex FT40A (QIC-40) */
 #define MT_ISDDS1		0x51	/* DDS device without partitions */
 #define MT_ISDDS2		0x52	/* DDS device with partitions */
@@ -93,20 +98,20 @@
 /* QIC-40/QIC-80 ftape supported drives.
  * 20bit vendor ID + 0x800000
  */
-#define MT_ISFTAPE_UNKNOWN      0x800000
-#define MT_ISCMSDJ10_DJ20       0x800047
-#define MT_ISCMSDJ10_DJ20_NEW   0x8011c4
-#define MT_ISARCHIVE_5580I      0x800005
-#define MT_ISARCHIVE_XL9250I    0x80014a
-#define MT_ISARCHIVE_31250Q     0x800146
-#define MT_ISINSIGHT_80	        0x810005
-#define MT_ISCONNER_C250MQT     0x80014c
-#define MT_ISWANGTEK_2040F      0x8001c1
-#define MT_ISWANGTEK_2080F      0x8001c8
-#define MT_ISIOMEGA_250         0x808880
-#define MT_ISSUMMIT_SE150       0x800180
-#define MT_ISSUMMIT_SE250       0x800181
-#define MT_ISESCOM_IDTBU120E    0x800140
+#define MT_ISFTAPE_UNKNOWN	0x800000
+#define MT_ISCMSDJ10_DJ20	0x800047
+#define MT_ISCMSDJ10_DJ20_NEW	0x8011c4
+#define MT_ISARCHIVE_5580I	0x800005
+#define MT_ISARCHIVE_XL9250I	0x80014a
+#define MT_ISARCHIVE_31250Q	0x800146
+#define MT_ISINSIGHT_80		0x810005
+#define MT_ISCONNER_C250MQT	0x80014c
+#define MT_ISWANGTEK_2040F	0x8001c1
+#define MT_ISWANGTEK_2080F	0x8001c8
+#define MT_ISIOMEGA_250		0x808880
+#define MT_ISSUMMIT_SE150	0x800180
+#define MT_ISSUMMIT_SE250	0x800181
+#define MT_ISESCOM_IDTBU120E	0x800140
 
 struct mt_tape_info {
 	long t_type;		/* device type id (mt_type) */
@@ -123,24 +128,27 @@
 	{MT_ISARCHIVE_VP60I,	"Archive VP60i, QIC-02"}, \
 	{MT_ISARCHIVE_2150L,	"Archive Viper 2150L"}, \
 	{MT_ISARCHIVE_2060L,	"Archive Viper 2060L"}, \
+	{MT_ISARCHIVESC499,	"Archive SC-499 QIC-36 controller"}, \
+	{MT_ISQIC02_ALL_FEATURES, "Generic QIC-02 tape, all features"}, \
 	{MT_ISWT5099EEN24,	"Wangtek 5099-een24, 60MB"}, \
+	{MT_ISTEAC_MT2ST,	"Teac MT-2ST 155mb data cassette drive"}, \
 	{MT_ISEVEREX_FT40A,	"Everex FT40A, QIC-40"}, \
 	{MT_ISSCSI1,		"Generic SCSI-1 tape"}, \
 	{MT_ISSCSI2,		"Generic SCSI-2 tape"}, \
 	{MT_ISFTAPE_UNKNOWN,	"Unknown floppy interface tape drive"},\
 	{MT_ISCMSDJ10_DJ20,	"Colorado DJ-10/DJ-20"},\
-	{MT_ISCMSDJ10_DJ20_NEW, "Colorado DJ-10/DJ-20 (new)"},\
+	{MT_ISCMSDJ10_DJ20_NEW,	"Colorado DJ-10/DJ-20 (new)"},\
 	{MT_ISARCHIVE_5580I,	"Archive 5580i"},\
-        {MT_ISARCHIVE_XL9250I,  "Archive XL9250i [Conner/Escom]"},\
+	{MT_ISARCHIVE_XL9250I,	"Archive XL9250i [Conner/Escom]"},\
 	{MT_ISARCHIVE_31250Q,	"Escom/Archive 31250Q"},\
-        {MT_ISINSIGHT_80,	"Insight 80 Mb"},\
-        {MT_ISCONNER_C250MQT,   "Conner C250MQT"},\
-        {MT_ISWANGTEK_2040F,	"Wangtek 3040F"},\
-        {MT_ISWANGTEK_2080F,	"Wangtek 3080F"},\
-        {MT_ISIOMEGA_250,       "Iomega 250"},\
-        {MT_ISSUMMIT_SE150,	"Summit SE 150"},\
-        {MT_ISSUMMIT_SE250,	"Summit SE 250/Mountain FS8000"},\
-        {MT_ISESCOM_IDTBU120E,  "Identity IDTBU120E, Escom?"},\
+	{MT_ISINSIGHT_80,	"Insight 80 Mb"},\
+	{MT_ISCONNER_C250MQT,	"Conner C250MQT"},\
+	{MT_ISWANGTEK_2040F,	"Wangtek 3040F"},\
+	{MT_ISWANGTEK_2080F,	"Wangtek 3080F"},\
+	{MT_ISIOMEGA_250,	"Iomega 250"},\
+	{MT_ISSUMMIT_SE150,	"Summit SE 150"},\
+	{MT_ISSUMMIT_SE250,	"Summit SE 250/Mountain FS8000"},\
+	{MT_ISESCOM_IDTBU120E,	"Identity IDTBU120E, Escom?"},\
 	{0, NULL} \
 }
 
@@ -157,6 +165,12 @@
 #define	MTIOCGET	_IOR('m', 2, struct mtget)	/* get tape status */
 #define	MTIOCPOS	_IOR('m', 3, struct mtpos)	/* get tape position */
 
+/* The next two are used by the QIC-02 driver for runtime reconfiguration.
+ * See tpqic02.h for struct mtconfiginfo.
+ */
+#define	MTIOCGETCONFIG	_IOR('m', 4, struct mtconfiginfo) /* get tape config */
+#define	MTIOCSETCONFIG	_IOW('m', 5, struct mtconfiginfo) /* set tape config */
+
 
 /* Generic Mag Tape (device independent) status macros for examining
  * mt_gstat -- HP-UX compatible.
@@ -206,4 +220,3 @@
 #define MT_ST_TWO_FM		0x10
 
 #endif /* _LINUX_MTIO_H */
-
diff -u --recursive --new-file v1.1.34/linux/include/linux/net.h linux/include/linux/net.h
--- v1.1.34/linux/include/linux/net.h	Tue May 24 00:34:56 1994
+++ linux/include/linux/net.h	Sun Jul 24 17:39:46 1994
@@ -54,7 +54,7 @@
 
 #define SO_ACCEPTCON	(1<<16)		/* performed a listen		*/
 
-
+#ifdef __KERNEL__
 /*
  * Internal representation of a socket. not all the fields are used by
  * all configurations:
@@ -130,5 +130,5 @@
 extern int	sock_awaitconn(struct socket *mysock, struct socket *servsock);
 extern int	sock_register(int family, struct proto_ops *ops);
 
-
+#endif /* __KERNEL__ */
 #endif	/* _LINUX_NET_H */
diff -u --recursive --new-file v1.1.34/linux/include/linux/nfs_fs.h linux/include/linux/nfs_fs.h
--- v1.1.34/linux/include/linux/nfs_fs.h	Thu Jul 14 00:48:26 1994
+++ linux/include/linux/nfs_fs.h	Sun Jul 24 17:39:46 1994
@@ -45,6 +45,8 @@
 #define NFS_SERVER(inode)		(&(inode)->i_sb->u.nfs_sb.s_server)
 #define NFS_FH(inode)			(&(inode)->u.nfs_i.fhandle)
 
+#ifdef __KERNEL__
+
 /* linux/fs/nfs/proc.c */
 
 extern int nfs_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
@@ -112,5 +114,7 @@
 
 extern int nfs_mmap(struct inode * inode, struct file * file,
                unsigned long addr, size_t len, int prot, unsigned long off);
+
+#endif /* __KERNEL__ */
 
 #endif
diff -u --recursive --new-file v1.1.34/linux/include/linux/sched.h linux/include/linux/sched.h
--- v1.1.34/linux/include/linux/sched.h	Mon Jul 18 14:48:30 1994
+++ linux/include/linux/sched.h	Sun Jul 24 17:39:46 1994
@@ -352,6 +352,8 @@
 /* mm */	{ INIT_MM } \
 }
 
+#ifdef __KERNEL__
+
 extern struct task_struct init_task;
 extern struct task_struct *task[NR_TASKS];
 extern struct task_struct *last_task_used_math;
@@ -619,5 +621,7 @@
 			: /* no output */ \
 			:"m" (current->debugreg[register]) \
 			:"dx");
+
+#endif /* __KERNEL__ */
 
 #endif
diff -u --recursive --new-file v1.1.34/linux/include/linux/timer.h linux/include/linux/timer.h
--- v1.1.34/linux/include/linux/timer.h	Sat Jul  9 16:30:47 1994
+++ linux/include/linux/timer.h	Sat Jul 23 12:58:37 1994
@@ -27,7 +27,7 @@
  *
  * COPRO_TIMER		387 timeout for buggy hardware..
  *
- * TAPE_QIC02_TIMER	timer for QIC-02 tape driver (it's not hardcoded)
+ * QIC02_TAPE_TIMER	timer for QIC-02 tape driver (it's not hardcoded)
  *
  * MCD_TIMER		Mitsumi CD-ROM Timer
  */
@@ -43,7 +43,7 @@
 #define SOUND_TIMER	20
 #define COPRO_TIMER	21
 
-#define TAPE_QIC02_TIMER	22	/* hhb */
+#define QIC02_TAPE_TIMER	22	/* hhb */
 #define MCD_TIMER	23
 
 #define HD_TIMER2	24
diff -u --recursive --new-file v1.1.34/linux/include/linux/tpqic02.h linux/include/linux/tpqic02.h
--- v1.1.34/linux/include/linux/tpqic02.h	Wed Dec  1 14:44:15 1993
+++ linux/include/linux/tpqic02.h	Sat Jul 23 12:58:38 1994
@@ -1,10 +1,10 @@
-/* $Id: tpqic02.h,v 0.16 1993/04/19 23:15:39 root Exp root $
+/* $Id: tpqic02.h,v 0.25 1994/07/21 02:16:30 root Exp root $
  *
  * Include file for QIC-02 driver for Linux.
  *
- * Copyright (c) 1992 by H. H. Bergman. All rights reserved.
+ * Copyright (c) 1992, 1993, 1994 by H. H. Bergman. All rights reserved.
  *
- * ******* USER CONFIG SECTION BELOW *******
+ * ******* USER CONFIG SECTION BELOW (Near line 70) *******
  */
 
 #ifndef _LINUX_TPQIC02_H
@@ -12,15 +12,25 @@
 
 #include <linux/config.h>
 
-#if CONFIG_TAPE_QIC02
+#if CONFIG_QIC02_TAPE
 
-/* need to have TAPE_QIC02_DRIVE and TAPE_QIC02_IFC expand to something */
+/* need to have QIC02_TAPE_DRIVE and QIC02_TAPE_IFC expand to something */
 #include <linux/mtio.h>
 
 
-/* make TAPE_QIC02_IFC expand to something */
+/* Make QIC02_TAPE_IFC expand to something.
+ *
+ * The only difference between WANGTEK and EVEREX is in the 
+ * handling of the DMA channel 3.
+ * Note that the driver maps EVEREX to WANGTEK internally for speed
+ * reasons. Externally WANGTEK==1, EVEREX==2, ARCHIVE==3.
+ * These must correspond to the values used in qic02config(1).
+ *
+ * Support for Mountain controllers was added by Erik Jacobson
+ * and severely hacked by me.   -- hhb
+ */
 #define WANGTEK		1		   /* don't know about Wangtek QIC-36 */
-#define EVEREX		WANGTEK      /* I heard *some* of these are identical */
+#define EVEREX		(WANGTEK+1)  /* I heard *some* of these are identical */
 #define EVEREX_811V	EVEREX			      /* With TEAC MT 2ST 45D */
 #define EVEREX_831V	EVEREX
 #define ARCHIVE		3
@@ -28,86 +38,153 @@
 #define ARCHIVE_SC402	ARCHIVE		       /* don't know much about SC400 */
 #define ARCHIVE_SC499	ARCHIVE       /* SC402 and SC499R should be identical */
 
+#define MOUNTAIN	5		       /* Mountain Computer Interface */
 
+
 /*********** START OF USER CONFIGURABLE SECTION ************/
 
-/* Tape configuration: 
+/* Tape configuration: Select DRIVE, IFC, PORT, IRQ and DMA below.
+ * Runtime (re)configuration is not supported yet.
  *
- * Tape drive configuration:	(MT_IS* constants are defined in sys/mtio.h)
+ * Tape drive configuration:	(MT_IS* constants are defined in mtio.h)
  *
- * TAPE_QIC02_DRIVE = MT_ISWT5150
+ * QIC02_TAPE_DRIVE = MT_ISWT5150
  *	- Wangtek 5150, format: up to QIC-150.
- * TAPE_QIC02_DRIVE = MT_ISQIC02_ALL_FEATURES
- *	- Enables some optional QIC commands that some drives may lack.
+ * QIC02_TAPE_DRIVE = MT_ISQIC02_ALL_FEATURES
+ *	- Enables some optional QIC02 commands that some drives may lack.
  *	  It is provided so you can check which are supported by your drive.
  *	  Refer to tpqic02.h for others.
  *
- * Supported interface cards: TAPE_QIC02_IFC =
+ * Supported interface cards: QIC02_TAPE_IFC =
  *	WANGTEK,
  *	ARCHIVE_SC402, ARCHIVE_SC499.	(both same programming interface)
  *
  * Make sure you have the I/O ports/DMA channels 
  * and IRQ stuff configured properly!
- * NOTE: Check for conflicts with TAPE_QIC02_TIMER in timer.h.
+ * NOTE: There may be other device drivers using the same major
+ *       number. This must be avoided. Check for timer.h conflicts too.
+ *
+ * If you have an EVEREX EV-831 card and you are using DMA channel 3,
+ * you will probably have to ``#define QIC02_TAPE_DMA3_FIX'' below.
  */
 
-#define TAPE_QIC02_DRIVE	MT_ISQIC02_ALL_FEATURES	/* drive type */
-/* #define TAPE_QIC02_DRIVE	MT_ISWT5150 */
-#define TAPE_QIC02_IFC		WANGTEK		/* interface card type */
-/* #define TAPE_QIC02_IFC		ARCHIVE */
-#define TAPE_QIC02_PORT 	0x300	/* controller port adress */
-#define TAPE_QIC02_IRQ		5	/* Muhammad, please don't use 2 here. -- Hennus */
-#define TAPE_QIC02_DMA		1	/* either 1 or 3, because 2 is used by the floppy */
+/* CONFIG_QIC02_DYNCONF can be defined in autoconf.h, by `make config' */
+
+/*** #undef CONFIG_QIC02_DYNCONF ***/
 
+#ifndef CONFIG_QIC02_DYNCONF
 
+#define QIC02_TAPE_DRIVE	MT_ISQIC02_ALL_FEATURES	 /* drive type */
+/* #define QIC02_TAPE_DRIVE	MT_ISWT5150 */
+/* #define QIC02_TAPE_DRIVE	MT_ISARCHIVE_5945L2 */
+/* #define QIC02_TAPE_DRIVE	MT_ISTEAC_MT2ST */
+/* #define QIC02_TAPE_DRIVE	MT_ISARCHIVE_2150L */
+/* #define QIC02_TAPE_DRIVE	MT_ISARCHIVESC499 */
+
+/* Either WANGTEK, ARCHIVE or MOUNTAIN. Not EVEREX. 
+ * If you have an EVEREX, use WANGTEK and try the DMA3_FIX below.
+ */
+#define QIC02_TAPE_IFC		WANGTEK	/* interface card type */
+/* #define QIC02_TAPE_IFC		ARCHIVE */
+/* #define QIC02_TAPE_IFC		MOUNTAIN */
+
+#define QIC02_TAPE_PORT 	0x300	/* controller port adress */
+#define QIC02_TAPE_IRQ		5	/* For IRQ2, use 9 here, others normal. */
+#define QIC02_TAPE_DMA		1	/* either 1 or 3, because 2 is used by the floppy */
+
+/* If DMA3 doesn't work, but DMA1 does, and you have a 
+ * Wangtek/Everex card, you can try #define-ing the flag
+ * below. Note that you should also change the DACK jumper
+ * for Wangtek/Everex cards when changing the DMA channel.
+ */
+#undef QIC02_TAPE_DMA3_FIX
+
 /************ END OF USER CONFIGURABLE SECTION *************/
 
+/* I put the stuff above in config.in, but a few recompiles, to
+ * verify different configurations, and several days later I decided
+ * to change it back again.
+ */
+
 
-/* NOTE: TP_HAVE_DENS should distinguish between available densities
+
+/* NOTE: TP_HAVE_DENS should distinguish between available densities (?)
  * NOTE: Drive select is not implemented -- I have only one tape streamer,
  *	 so I'm unable and unmotivated to test and implement that. ;-) ;-)
  */
-#if TAPE_QIC02_DRIVE == MT_ISWT5150
-#define TP_HAVE_DENS
-#define TP_HAVE_BSF	/* nope */
-#define TP_HAVE_FSR	/* nope */
-#define TP_HAVE_BSR	/* nope */
-#define TP_HAVE_EOD	/* most of the time */
-#define TP_HAVE_RAS1
-#define TP_HAVE_RAS2
-
-#elif TAPE_QIC02_DRIVE == MT_ISARCHIVESC499	/* Archive SC-499 QIC-36 controller */
-#define TP_HAVE_DENS		/* can do set density (QIC-11 / QIC-24) */
-#define TP_HAVE_FSR		/* can skip one block forwards */
-#define TP_HAVE_BSR		/* can skip one block backwards */
-#define TP_HAVE_EOD		/* can seek to end of recorded data */
-#define TP_HAVE_RAS1		/* can run selftest 1 */
-#define TP_HAVE_RAS2		/* can run selftest 2 */
+#if QIC02_TAPE_DRIVE == MT_ISWT5150
+#define TP_HAVE_DENS	1
+#define TP_HAVE_BSF	0	/* nope */
+#define TP_HAVE_FSR	0	/* nope */
+#define TP_HAVE_BSR	0	/* nope */
+#define TP_HAVE_EOD	0	/* most of the time */
+#define TP_HAVE_SEEK	0
+#define TP_HAVE_TELL	0
+#define TP_HAVE_RAS1	1
+#define TP_HAVE_RAS2	1
+
+#elif QIC02_TAPE_DRIVE == MT_ISARCHIVESC499	/* Archive SC-499 QIC-36 controller */
+#define TP_HAVE_DENS	1	/* can do set density (QIC-11 / QIC-24) */
+#define TP_HAVE_BSF	0
+#define TP_HAVE_FSR	1	/* can skip one block forwards */
+#define TP_HAVE_BSR	1	/* can skip one block backwards */
+#define TP_HAVE_EOD	1	/* can seek to end of recorded data */
+#define TP_HAVE_SEEK	0
+#define TP_HAVE_TELL	0
+#define TP_HAVE_RAS1	1	/* can run selftest 1 */
+#define TP_HAVE_RAS2	1	/* can run selftest 2 */
+/* These last two selftests shouldn't be used yet! */
+
+#elif (QIC02_TAPE_DRIVE == MT_ISARCHIVE_2060L) || (QIC02_TAPE_DRIVE == MT_ISARCHIVE_2150L)
+#define TP_HAVE_DENS	1	/* can do set density (QIC-24 / QIC-120 / QIC-150) */
+#define TP_HAVE_BSF	0
+#define TP_HAVE_FSR	1	/* can skip one block forwards */
+#define TP_HAVE_BSR	1	/* can skip one block backwards */
+#define TP_HAVE_EOD	1	/* can seek to end of recorded data */
+#define TP_HAVE_TELL	1	/* can read current block address */
+#define TP_HAVE_SEEK	1	/* can seek to block */
+#define TP_HAVE_RAS1	1	/* can run selftest 1 */
+#define TP_HAVE_RAS2	1	/* can run selftest 2 */
 /* These last two selftests shouldn't be used yet! */
 
-#elif (TAPE_QIC02_DRIVE == MT_ISARCHIVE_2060L) || (TAPE_QIC02_DRIVE == MT_ISARCHIVE_2150L)
-#define TP_HAVE_DENS		/* can do set density (QIC-24 / QIC-120 / QIC-150) */
-#define TP_HAVE_FSR		/* can skip one block forwards */
-#define TP_HAVE_BSR		/* can skip one block backwards */
-#define TP_HAVE_EOD		/* can seek to end of recorded data */
-#define TP_HAVE_TELL		/* can read current block address */
-#define TP_HAVE_SEEK		/* can seek to block */
-#define TP_HAVE_RAS1		/* can run selftest 1 */
-#define TP_HAVE_RAS2		/* can run selftest 2 */
+#elif QIC02_TAPE_DRIVE == MT_ISARCHIVE_5945L2
+/* can anyone verify this entry?? */
+#define TP_HAVE_DENS	1	/* can do set density?? (QIC-24??) */
+#define TP_HAVE_BSF	0
+#define TP_HAVE_FSR	1	/* can skip one block forwards */
+#define TP_HAVE_BSR	1	/* can skip one block backwards */
+#define TP_HAVE_EOD	1	/* can seek to end of recorded data */
+#define TP_HAVE_TELL	1	/* can read current block address */
+#define TP_HAVE_SEEK	1	/* can seek to block */
+#define TP_HAVE_RAS1	1	/* can run selftest 1 */
+#define TP_HAVE_RAS2	1	/* can run selftest 2 */
 /* These last two selftests shouldn't be used yet! */
 
-#elif TAPE_QIC02_DRIVE == MT_ISQIC02_ALL_FEATURES
-#define TP_HAVE_DENS		/* can do set density */
-#define TP_HAVE_BSF		/* can search filemark backwards */
-#define TP_HAVE_FSR		/* can skip one block forwards */
-#define TP_HAVE_BSR		/* can skip one block backwards */
-#define TP_HAVE_EOD		/* can seek to end of recorded data */
-#define TP_HAVE_SEEK		/* seek to block address */
-#define TP_HAVE_TELL		/* tell current block address */
-#define TP_HAVE_RAS1		/* can run selftest 1 */
-#define TP_HAVE_RAS2		/* can run selftest 2 */
+#elif QIC02_TAPE_DRIVE == MT_ISTEAC_MT2ST
+/* can anyone verify this entry?? */
+#define TP_HAVE_DENS	0	/* cannot do set density?? (QIC-150?) */
+#define TP_HAVE_BSF	0
+#define TP_HAVE_FSR	1	/* can skip one block forwards */
+#define TP_HAVE_BSR	1	/* can skip one block backwards */
+#define TP_HAVE_EOD	1	/* can seek to end of recorded data */
+#define TP_HAVE_SEEK	1	/* can seek to block */
+#define TP_HAVE_TELL	1	/* can read current block address */
+#define TP_HAVE_RAS1	1	/* can run selftest 1 */
+#define TP_HAVE_RAS2	1	/* can run selftest 2 */
 /* These last two selftests shouldn't be used yet! */
 
+#elif QIC02_TAPE_DRIVE == MT_ISQIC02_ALL_FEATURES
+#define TP_HAVE_DENS	1	/* can do set density */
+#define TP_HAVE_BSF	1	/* can search filemark backwards */
+#define TP_HAVE_FSR	1	/* can skip one block forwards */
+#define TP_HAVE_BSR	1	/* can skip one block backwards */
+#define TP_HAVE_EOD	1	/* can seek to end of recorded data */
+#define TP_HAVE_SEEK	1	/* seek to block address */
+#define TP_HAVE_TELL	1	/* tell current block address */
+#define TP_HAVE_RAS1	1	/* can run selftest 1 */
+#define TP_HAVE_RAS2	1	/* can run selftest 2 */
+/* These last two selftests shouldn't be used yet! */
+
 
 #else
 #error No QIC-02 tape drive type defined!
@@ -118,91 +195,245 @@
  */
 #endif
 
-
-/* NR_BLK_BUF is a `tuneable parameter'. If you're really low on
- * kernel space, you could decrease it to 1, or if you got a very
- * slow machine, you could increase it up to 128 blocks. Less kernel
- * buffer blocks result in more context-switching.
- */
-#define NR_BLK_BUF	20				    /* max 128 blocks */
-#define TAPE_BLKSIZE	512		  /* streamer tape block size (fixed) */
-#define TPQBUF_SIZE	(TAPE_BLKSIZE*NR_BLK_BUF)	       /* buffer size */
-
+#endif /* !CONFIG_QIC02_DYNCONF */
 
-#define BLOCKS_BEYOND_EW	2	/* nr of blocks after Early Warning hole */
 
-#if TAPE_QIC02_IFC == WANGTEK	
-  /* Wangtek interface card port locations */
-# define QIC_STAT_PORT	TAPE_QIC02_PORT
-# define QIC_CTL_PORT	TAPE_QIC02_PORT
-# define QIC_CMD_PORT	(TAPE_QIC02_PORT+1)
-# define QIC_DATA_PORT	(TAPE_QIC02_PORT+1)
+/* WANGTEK interface card specifics */
+#define WT_QIC02_STAT_PORT	(QIC02_TAPE_PORT)
+#define WT_QIC02_CTL_PORT	(QIC02_TAPE_PORT)
+#define WT_QIC02_CMD_PORT	(QIC02_TAPE_PORT+1)
+#define WT_QIC02_DATA_PORT	(QIC02_TAPE_PORT+1)
 
 /* status register bits (Active LOW!) */
-# define QIC_STAT_READY		0x01
-# define QIC_STAT_EXCEPTION	0x02
-# define QIC_STAT_MASK		(QIC_STAT_READY|QIC_STAT_EXCEPTION)
-
-# define QIC_STAT_RESETMASK	0x07
-# define QIC_STAT_RESETVAL	(QIC_STAT_RESETMASK & ~QIC_STAT_EXCEPTION)
-
-/* controller register (QIC_CTL_PORT) bits */
-# define WT_CTL_ONLINE		0x01
-# define QIC_CTL_RESET		0x02
-# define QIC_CTL_REQUEST	0x04
-# define WT_CTL_CMDOFF		0xC0 
-# if TAPE_QIC02_DMA == 3   /* dip-switches alone don't seem to cut it */
-#  define WT_CTL_DMA		0x10			  /* enable dma chan3 */
-# elif TAPE_QIC02_DMA == 1
-#  define WT_CTL_DMA		0x08	         /* enable dma chan1 or chan2 */
-# else
-#  error Unsupported or incorrect DMA configuration.
-# endif
+#define WT_QIC02_STAT_READY	0x01
+#define WT_QIC02_STAT_EXCEPTION	0x02
+#define WT_QIC02_STAT_MASK	(WT_QIC02_STAT_READY|WT_QIC02_STAT_EXCEPTION)
+
+#define WT_QIC02_STAT_RESETMASK	0x07
+#define WT_QIC02_STAT_RESETVAL	(WT_QIC02_STAT_RESETMASK & ~WT_QIC02_STAT_EXCEPTION)
+
+/* controller register (QIC02_CTL_PORT) bits */
+#define WT_QIC02_CTL_RESET	0x02
+#define WT_QIC02_CTL_REQUEST	0x04
+#define WT_CTL_ONLINE		0x01
+#define WT_CTL_CMDOFF		0xC0 
+
+#define WT_CTL_DMA3		0x10			  /* enable dma chan3 */
+#define WT_CTL_DMA1		0x08	         /* enable dma chan1 or chan2 */
 
-#elif TAPE_QIC02_IFC == ARCHIVE
- /* Archive interface card port locations */
-# define QIC_STAT_PORT		(TAPE_QIC02_PORT+1)
-# define QIC_CTL_PORT		(TAPE_QIC02_PORT+1)
-# define QIC_CMD_PORT		(TAPE_QIC02_PORT)
-# define QIC_DATA_PORT		(TAPE_QIC02_PORT)
-# define AR_START_DMA_PORT	(TAPE_QIC02_PORT+2)
-# define AR_RESET_DMA_PORT	(TAPE_QIC02_PORT+3)
-
-  /* STAT port bits */
-# define AR_STAT_IRQF		0x80	/* active high, interrupt request flag */
-# define QIC_STAT_READY		0x40	/* active low */
-# define QIC_STAT_EXCEPTION	0x20	/* active low */
-# define QIC_STAT_MASK		(QIC_STAT_READY|QIC_STAT_EXCEPTION)
-# define AR_STAT_DMADONE	0x10	/* active high, DMA done */
-# define AR_STAT_DIRC		0x08	/* active high, direction */
 
-# define QIC_STAT_RESETMASK	0x70	/* check RDY,EXC,DMADONE */
-# define QIC_STAT_RESETVAL	((QIC_STAT_RESETMASK & ~AR_STAT_IRQF & ~QIC_STAT_EXCEPTION) | AR_STAT_DMADONE)
 
-  /* CTL port bits */
-# define QIC_CTL_RESET		0x80	/* drive reset */
-# define QIC_CTL_REQUEST	0x40	/* notify of new command */
-# define AR_CTL_IEN		0x20	/* interrupt enable */
-# define AR_CTL_DNIEN		0x10	/* done-interrupt enable */
+
+/* ARCHIVE interface card specifics */
+#define AR_QIC02_STAT_PORT	(QIC02_TAPE_PORT+1)
+#define AR_QIC02_CTL_PORT	(QIC02_TAPE_PORT+1)
+#define AR_QIC02_CMD_PORT	(QIC02_TAPE_PORT)
+#define AR_QIC02_DATA_PORT	(QIC02_TAPE_PORT)
+
+#define AR_START_DMA_PORT	(QIC02_TAPE_PORT+2)
+#define AR_RESET_DMA_PORT	(QIC02_TAPE_PORT+3)
+
+/* STAT port bits */
+#define AR_STAT_IRQF		0x80	/* active high, interrupt request flag */
+#define AR_QIC02_STAT_READY	0x40	/* active low */
+#define AR_QIC02_STAT_EXCEPTION	0x20	/* active low */
+#define AR_QIC02_STAT_MASK	(AR_QIC02_STAT_READY|AR_QIC02_STAT_EXCEPTION)
+#define AR_STAT_DMADONE		0x10	/* active high, DMA done */
+#define AR_STAT_DIRC		0x08	/* active high, direction */
+
+#define AR_QIC02_STAT_RESETMASK	0x70	/* check RDY,EXC,DMADONE */
+#define AR_QIC02_STAT_RESETVAL	((AR_QIC02_STAT_RESETMASK & ~AR_STAT_IRQF & ~AR_QIC02_STAT_EXCEPTION) | AR_STAT_DMADONE)
+
+/* CTL port bits */
+#define AR_QIC02_CTL_RESET	0x80	/* drive reset */
+#define AR_QIC02_CTL_REQUEST	0x40	/* notify of new command */
+#define AR_CTL_IEN		0x20	/* interrupt enable */
+#define AR_CTL_DNIEN		0x10	/* done-interrupt enable */
   /* Note: All of these bits are cleared automatically when writing to
    * AR_RESET_DMA_PORT. So AR_CTL_IEN and AR_CTL_DNIEN must be
    * reprogrammed before the write to AR_START_DMA_PORT.
    */
 
-# if TAPE_QIC02_DMA > 3		/* channel 2 is used by the floppy driver */
-#  error DMA channels other than 1 and 3 are not supported.
+
+/* MOUNTAIN interface specifics */
+#define MTN_QIC02_STAT_PORT	(QIC02_TAPE_PORT+1)
+#define MTN_QIC02_CTL_PORT	(QIC02_TAPE_PORT+1)
+#define MTN_QIC02_CMD_PORT	(QIC02_TAPE_PORT)
+#define MTN_QIC02_DATA_PORT	(QIC02_TAPE_PORT)
+
+#define MTN_W_SELECT_DMA_PORT	(QIC02_TAPE_PORT+2)
+#define MTN_R_DESELECT_DMA_PORT	(QIC02_TAPE_PORT+2)
+#define MTN_W_DMA_WRITE_PORT	(QIC02_TAPE_PORT+3)
+
+/* STAT port bits */
+#define MTN_QIC02_STAT_READY	 0x02	/* active low */
+#define MTN_QIC02_STAT_EXCEPTION 0x04	/* active low */
+#define MTN_QIC02_STAT_MASK	 (MTN_QIC02_STAT_READY|MTN_QIC02_STAT_EXCEPTION)
+#define MTN_STAT_DMADONE	 0x01	/* active high, DMA done */
+
+#define MTN_QIC02_STAT_RESETMASK 0x07	/* check RDY,EXC,DMADONE */
+#define MTN_QIC02_STAT_RESETVAL	 ((MTN_QIC02_STAT_RESETMASK & ~MTN_QIC02_STAT_EXCEPTION) | MTN_STAT_DMADONE)
+
+  /* CTL port bits */
+#define MTN_QIC02_CTL_RESET_NOT	 0x80	/* drive reset, active low */
+#define MTN_QIC02_CTL_RESET	 0x80	/* Fodder #definition to keep gcc happy */
+
+#define MTN_QIC02_CTL_ONLINE	 0x40	/* Put drive on line  */
+#define MTN_QIC02_CTL_REQUEST	 0x20	/* notify of new command */
+#define MTN_QIC02_CTL_IRQ_DRIVER 0x10	/* Enable IRQ tristate driver */
+#define MTN_QIC02_CTL_DMA_DRIVER 0x08	/* Enable DMA tristate driver */
+#define MTN_CTL_EXC_IEN		 0x04	/* Exception interrupt enable */
+#define MTN_CTL_RDY_IEN		 0x02	/* Ready interrupt enable */
+#define MTN_CTL_DNIEN		 0x01	/* done-interrupt enable */
+
+#define MTN_CTL_ONLINE		(MTN_QIC02_CTL_RESET_NOT | MTN_QIC02_CTL_IRQ_DRIVER | MTN_QIC02_CTL_DMA_DRIVER)
+
+
+#ifndef CONFIG_QIC02_DYNCONF
+
+# define QIC02_TAPE_DEBUG	(qic02_tape_debug)
+
+# if QIC02_TAPE_IFC == WANGTEK	
+#  define QIC02_STAT_PORT	WT_QIC02_STAT_PORT
+#  define QIC02_CTL_PORT	WT_QIC02_CTL_PORT
+#  define QIC02_CMD_PORT	WT_QIC02_CMD_PORT
+#  define QIC02_DATA_PORT	WT_QIC02_DATA_PORT
+
+#  define QIC02_STAT_READY	WT_QIC02_STAT_READY
+#  define QIC02_STAT_EXCEPTION	WT_QIC02_STAT_EXCEPTION
+#  define QIC02_STAT_MASK	WT_QIC02_STAT_MASK
+#  define QIC02_STAT_RESETMASK	WT_QIC02_STAT_RESETMASK
+#  define QIC02_STAT_RESETVAL	WT_QIC02_STAT_RESETVAL
+
+#  define QIC02_CTL_RESET	WT_QIC02_CTL_RESET
+#  define QIC02_CTL_REQUEST	WT_QIC02_CTL_REQUEST
+
+#  if QIC02_TAPE_DMA == 3
+#   ifdef QIC02_TAPE_DMA3_FIX
+#    define WT_CTL_DMA		WT_CTL_DMA1
+#   else
+#    define WT_CTL_DMA		WT_CTL_DMA3
+#   endif
+#  elif QIC02_TAPE_DMA == 1
+#    define WT_CTL_DMA		WT_CTL_DMA1
+#  else
+#   error Unsupported or incorrect DMA configuration.
+#  endif
+
+# elif QIC02_TAPE_IFC == ARCHIVE
+#  define QIC02_STAT_PORT	AR_QIC02_STAT_PORT
+#  define QIC02_CTL_PORT	AR_QIC02_CTL_PORT
+#  define QIC02_CMD_PORT	AR_QIC02_CMD_PORT
+#  define QIC02_DATA_PORT	AR_QIC02_DATA_PORT
+
+#  define QIC02_STAT_READY	AR_QIC02_STAT_READY
+#  define QIC02_STAT_EXCEPTION	AR_QIC02_STAT_EXCEPTION
+#  define QIC02_STAT_MASK	AR_QIC02_STAT_MASK
+#  define QIC02_STAT_RESETMASK	AR_QIC02_STAT_RESETMASK
+#  define QIC02_STAT_RESETVAL	AR_QIC02_STAT_RESETVAL
+
+#  define QIC02_CTL_RESET	AR_QIC02_CTL_RESET
+#  define QIC02_CTL_REQUEST	AR_QIC02_CTL_REQUEST
+
+#  if QIC02_TAPE_DMA > 3	/* channel 2 is used by the floppy driver */
+#   error DMA channels other than 1 and 3 are not supported.
+#  endif
+
+# elif QIC02_TAPE_IFC == MOUNTAIN
+#  define QIC02_STAT_PORT	MTN_QIC02_STAT_PORT
+#  define QIC02_CTL_PORT	MTN_QIC02_CTL_PORT
+#  define QIC02_CMD_PORT	MTN_QIC02_CMD_PORT
+#  define QIC02_DATA_PORT	MTN_QIC02_DATA_PORT
+
+#  define QIC02_STAT_READY	MTN_QIC02_STAT_READY
+#  define QIC02_STAT_EXCEPTION	MTN_QIC02_STAT_EXCEPTION
+#  define QIC02_STAT_MASK	MTN_QIC02_STAT_MASK
+#  define QIC02_STAT_RESETMASK	MTN_QIC02_STAT_RESETMASK
+#  define QIC02_STAT_RESETVAL	MTN_QIC02_STAT_RESETVAL
+
+#  define QIC02_CTL_RESET	MTN_QIC02_CTL_RESET
+#  define QIC02_CTL_REQUEST	MTN_QIC02_CTL_REQUEST
+
+#  if QIC02_TAPE_DMA > 3	/* channel 2 is used by the floppy driver */
+#   error DMA channels other than 1 and 3 are not supported.
+#  endif
+
+# else
+#  error No valid interface card specified!
+# endif /* QIC02_TAPE_IFC */
+
+
+  /* An ugly hack to make sure WT_CTL_DMA is defined even for the
+   * static, non-Wangtek case. The alternative was even worse.
+   */ 
+# ifndef WT_CTL_DMA
+#  define WT_CTL_DMA		WT_CTL_DMA1
 # endif
 
-#else
-# error No valid interface card specified!
-#endif /* TAPE_QIC02_IFC */
+/*******************/
+
+#else /* !CONFIG_QIC02_DYNCONF */
+
+/* Now the runtime config version, using variables instead of constants.
+ *
+ * qic02_tape_dynconf is R/O for the kernel, set from userspace.
+ * qic02_tape_ccb is private to the driver, R/W.
+ */
+
+# define QIC02_TAPE_DRIVE	(qic02_tape_dynconf.mt_type)
+# define QIC02_TAPE_IFC		(qic02_tape_ccb.ifc_type)
+# define QIC02_TAPE_IRQ		(qic02_tape_dynconf.irqnr)
+# define QIC02_TAPE_DMA		(qic02_tape_dynconf.dmanr)
+# define QIC02_TAPE_PORT	(qic02_tape_dynconf.port)
+# define WT_CTL_DMA		(qic02_tape_ccb.dma_enable_value)
+# define QIC02_TAPE_DEBUG	(qic02_tape_dynconf.debug)
+
+# define QIC02_STAT_PORT	(qic02_tape_ccb.port_stat)
+# define QIC02_CTL_PORT 	(qic02_tape_ccb.port_ctl)
+# define QIC02_CMD_PORT 	(qic02_tape_ccb.port_cmd)
+# define QIC02_DATA_PORT 	(qic02_tape_ccb.port_data)
+
+# define QIC02_STAT_READY	(qic02_tape_ccb.stat_ready)
+# define QIC02_STAT_EXCEPTION	(qic02_tape_ccb.stat_exception)
+# define QIC02_STAT_MASK	(qic02_tape_ccb.stat_mask)
+
+# define QIC02_STAT_RESETMASK	(qic02_tape_ccb.stat_resetmask)
+# define QIC02_STAT_RESETVAL	(qic02_tape_ccb.stat_resetval)
+
+# define QIC02_CTL_RESET	(qic02_tape_ccb.ctl_reset)
+# define QIC02_CTL_REQUEST	(qic02_tape_ccb.ctl_request)
+
+# define TP_HAVE_DENS		(qic02_tape_dynconf.have_dens)
+# define TP_HAVE_BSF		(qic02_tape_dynconf.have_bsf)
+# define TP_HAVE_FSR		(qic02_tape_dynconf.have_fsr)
+# define TP_HAVE_BSR		(qic02_tape_dynconf.have_bsr)
+# define TP_HAVE_EOD		(qic02_tape_dynconf.have_eod)
+# define TP_HAVE_SEEK		(qic02_tape_dynconf.have_seek)
+# define TP_HAVE_TELL		(qic02_tape_dynconf.have_tell)
+# define TP_HAVE_RAS1		(qic02_tape_dynconf.have_ras1)
+# define TP_HAVE_RAS2		(qic02_tape_dynconf.have_ras2)
+
+#endif /* CONFIG_QIC02_DYNCONF */
+
+
+/* "Vendor Unique" codes */
+/* Archive seek & tell stuff */
+#define AR_QCMDV_TELL_BLK	0xAE	/* read current block address */
+#define AR_QCMDV_SEEK_BLK	0xAD	/* seek to specific block */
+#define AR_SEEK_BUF_SIZE	3	/* address is 3 bytes */
 
+
+
+/*
+ * Misc common stuff
+ */
+
 /* Standard QIC-02 commands -- rev F.  All QIC-02 drives must support these */
 #define QCMD_SEL_1	0x01		/* select drive 1 */
 #define QCMD_SEL_2	0x02		/* select drive 2 */
 #define QCMD_SEL_3	0x04		/* select drive 3 */
 #define QCMD_SEL_4	0x08		/* select drive 4 */
-#define	QCMD_REWIND	0x21		/* rewind tape*/
+#define	QCMD_REWIND	0x21		/* rewind tape */
 #define QCMD_ERASE	0x22		/* erase tape */
 #define QCMD_RETEN	0x24		/* retension tape */
 #define	QCMD_WRT_DATA	0x40		/* write data */
@@ -234,14 +465,6 @@
 
 
 
-/* "Vendor Unique" codes */
-#if defined(MT_ISARCHIVESC499) || defined(MT_ISARCHIVE_2150L)
-# define QCMDV_TELL_BLK		0xAE		/* read current block address */
-# define QCMDV_SEEK_BLK		0xAD		/* seek to specific block */
-# define SEEK_BUF_SIZE		3		/* address is 3 bytes */
-#endif
-
-
 /* Optional, QFA (Quick File Access) commands.
  * Not all drives support this, but those that do could use these commands
  * to implement semi-non-sequential access. `mt fsf` would benefit from this.
@@ -249,7 +472,7 @@
  * causing some incompatibility problems wrt std QIC-02 data exchange.
  * It would be useful to cache the directory info, but that might be tricky
  * to do in kernel-space. [Size constraints.]
- * Refer to QIC-02, appendix A for more information.
+ * Refer to the QIC-02 specs, appendix A for more information.
  * I have no idea how other *nix variants implement QFA.
  * I have no idea which drives support QFA and which don't.
  */
@@ -262,6 +485,30 @@
 
 
 
+
+/*
+ * Debugging flags
+ */
+#define TPQD_SENSE_TEXT	0x0001
+#define TPQD_SENSE_CNTS 0x0002
+#define TPQD_REWIND	0x0004
+#define TPQD_TERM_CYCLE	0x0008
+#define TPQD_IOCTLS	0x0010
+#define TPQD_DMAX	0x0020
+#define TPQD_BLKSZ	0x0040
+#define TPQD_MISC	0x0080
+
+#define TPQD_DEBUG	0x0100
+
+#define TPQD_DIAGS	0x1000
+
+#define TPQD_ALWAYS	0x8000
+
+#define TPQD_DEFAULT_FLAGS	0x01fc
+
+#define TPQDBG(f)	((QIC02_TAPE_DEBUG) & (TPQD_##f))
+
+
 /* Minor device codes for tapes:
  * |7|6|5|4|3|2|1|0|
  *  | \ | / \ | / |_____ 1=rewind on close, 0=no rewind on close
@@ -275,8 +522,9 @@
 			   /* rewind is only done if data has been transfered */
 #define	TP_DENS(dev)	((MINOR(dev) >> 1) & 0x07) 	      /* tape density */
 #define TP_UNIT(dev)	((MINOR(dev) >> 4) & 0x07)	       /* unit number */
-#define TP_DIAGS(dev)	(MINOR(dev) & 0x80)    /* print excessive diagnostics */
 
+/* print excessive diagnostics */
+#define TP_DIAGS(dev)	(QIC02_TAPE_DEBUG & TPQD_DIAGS)
 
 /* status codes returned by a WTS_RDSTAT call */
 struct tpstatus {	/* sizeof(short)==2), LSB first */
@@ -309,9 +557,10 @@
 #define REPORT_ERR1	(TP_ILL|TP_NDT|TP_MBD|TP_PAR)
 
 
+/* exception numbers */
 #define EXC_UNKNOWN	0	/* (extra) Unknown exception code */
-#define EXC_NCART	1	/* No cartridge */
-#define EXC_NDRV	2	/* No drive */
+#define EXC_NDRV	1	/* No drive */
+#define EXC_NCART	2	/* No cartridge */
 #define EXC_WP		3	/* Write protected */
 #define EXC_EOM		4	/* EOM */
 #define EXC_RWA		5	/* read/write abort */
@@ -345,10 +594,10 @@
 #define TIM_R	(8*60*HZ)	/* 8 minutes (retensioning) */
 #define TIM_F	(2*3600*HZ)	/* est. 1.2hr for full tape read/write+2 retens */
 
-#define TIMERON(t)	timer_table[TAPE_QIC02_TIMER].expires = jiffies + (t); \
-			timer_active |= (1<<TAPE_QIC02_TIMER)
-#define TIMEROFF	timer_active &= ~(1<<TAPE_QIC02_TIMER)
-#define TIMERCONT	timer_active |= (1<<TAPE_QIC02_TIMER)
+#define TIMERON(t)	timer_table[QIC02_TAPE_TIMER].expires = jiffies + (t); \
+			timer_active |= (1<<QIC02_TAPE_TIMER)
+#define TIMEROFF	timer_active &= ~(1<<QIC02_TAPE_TIMER)
+#define TIMERCONT	timer_active |= (1<<QIC02_TAPE_TIMER)
 
 
 typedef char flag;
@@ -365,10 +614,80 @@
 #endif
 
 
+/* NR_BLK_BUF is a `tuneable parameter'. If you're really low on
+ * kernel space, you could decrease it to 1, or if you got a very
+ * slow machine, you could increase it up to 128 blocks. Less kernel
+ * buffer blocks result in more context-switching.
+ */
+#define NR_BLK_BUF	20				    /* max 128 blocks */
+#define TAPE_BLKSIZE	512		  /* streamer tape block size (fixed) */
+#define TPQBUF_SIZE	(TAPE_BLKSIZE*NR_BLK_BUF)	       /* buffer size */
+
+
+#define BLOCKS_BEYOND_EW	2	/* nr of blocks after Early Warning hole */
+#define BOGUS_IRQ		32009
 
-extern long tape_qic02_init(long);			  /* for kernel/mem.c */
+/* structure for MTIOCGETCONFIG/MTIOCSETCONFIG primarily intended
+ * as an interim solution for QIC-02 until DDI is fully implemented.
+ */
+struct mtconfiginfo {
+	long	mt_type;	/* drive type */
+	long	ifc_type;	/* interface card type */
+	unsigned short	irqnr;	/* IRQ number to use */
+	unsigned short	dmanr;	/* DMA channel to use */
+	unsigned short	port;	/* IO port base address */
+
+	unsigned long	debug;	/* debugging flags */
+
+	unsigned	have_dens:1;
+	unsigned	have_bsf:1;
+	unsigned	have_fsr:1;
+	unsigned	have_bsr:1;
+	unsigned	have_eod:1;
+	unsigned	have_seek:1;
+	unsigned	have_tell:1;
+	unsigned	have_ras1:1;
+	unsigned	have_ras2:1;
+	unsigned	have_ras3:1;
+	unsigned	have_qfa:1;
+
+	unsigned	pad1:5;
+	char		reserved[10];
+};
+
+
+/* This is internal data, filled in based on the ifc_type field given
+ * by the user. Everex is mapped to Wangtek with a different
+ * `dma_enable_value', if dmanr==3.
+ */
+struct qic02_ccb {
+	long	ifc_type;
 
+	unsigned short	port_stat;	/* Status port address */
+	unsigned short	port_ctl;	/* Control port address */
+	unsigned short	port_cmd;	/* Command port address */
+	unsigned short	port_data;	/* Data port address */
+
+	/* status register bits */
+	unsigned short	stat_ready;	/* drive ready */
+	unsigned short	stat_exception;	/* drive signals exception */
+	unsigned short	stat_mask;
+	unsigned short	stat_resetmask;
+	unsigned short	stat_resetval;
+
+	/* control register bits */
+	unsigned short	ctl_reset;	/* reset drive */
+	unsigned short	ctl_request;	/* latch command */
+	
+	/* This is used to change the DMA3 behaviour */
+	unsigned short	dma_enable_value;
+};
+
+
+extern long qic02_tape_init(long);			  /* for mem.c */
 
-#endif /* CONFIG_TAPE_QIC02 */
 
+#endif /* CONFIG_QIC02_TAPE */
+
 #endif /* _LINUX_TPQIC02_H */
+
diff -u --recursive --new-file v1.1.34/linux/include/linux/tty.h linux/include/linux/tty.h
--- v1.1.34/linux/include/linux/tty.h	Tue May 31 12:48:19 1994
+++ linux/include/linux/tty.h	Sun Jul 24 17:39:46 1994
@@ -5,6 +5,7 @@
  * 'tty.h' defines some structures used by tty_io.c and some defines.
  */
 
+#ifdef __KERNEL__
 #include <linux/fs.h>
 #include <linux/termios.h>
 #include <linux/tqueue.h>
@@ -13,6 +14,7 @@
 
 #include <asm/system.h>
 
+
 /*
  * Note: don't mess with NR_PTYS until you understand the tty minor 
  * number allocation game...
@@ -322,4 +324,5 @@
 extern int vt_ioctl(struct tty_struct *tty, struct file * file,
 		    unsigned int cmd, unsigned long arg);
 
+#endif /* __KERNEL__ */
 #endif
diff -u --recursive --new-file v1.1.34/linux/include/linux/umsdos_fs.h linux/include/linux/umsdos_fs.h
--- v1.1.34/linux/include/linux/umsdos_fs.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/umsdos_fs.h	Sun Jul 24 22:27:55 1994
@@ -0,0 +1,108 @@
+#ifndef LINUX_UMSDOS_FS_H
+#define LINUX_UMSDOS_FS_H
+
+#define UMSDOS_VERSION	0
+#define UMSDOS_RELEASE	3
+
+#ifndef LINUX_FS_H
+#include <linux/fs.h>
+#endif
+
+/* This is the file acting as a directory extension */
+#define UMSDOS_EMD_FILE		"--linux-.---"
+#define UMSDOS_EMD_NAMELEN	12
+#define UMSDOS_PSDROOT_NAME	"linux"
+#define UMSDOS_PSDROOT_LEN	5
+
+struct umsdos_fake_info {
+	char fname[13];
+	int  len;
+};
+
+#define UMSDOS_MAXNAME	220
+/* This structure is 256 bytes large, depending on the name, only part */
+/* of it is written to disk */
+struct umsdos_dirent {
+	unsigned char name_len;	/* if == 0, then this entry is not used */
+	unsigned char  flags;	/* UMSDOS_xxxx */
+	unsigned short nlink;	/* How many hard links point to this entry */
+	uid_t		 uid;		/* Owner user id */
+	gid_t		 gid;		/* Group id */
+	time_t		atime;		/* Access time */
+	time_t		mtime;		/* Last modification time */
+	time_t		ctime;		/* Creation time */
+	dev_t		rdev;		/* major and minor number of a device */
+							/* special file */
+	umode_t		mode;		/* Standard UNIX permissions bits + type of */
+	char	spare[12];		/* unused bytes for future extensions */
+							/* file, see linux/stat.h */
+	char name[UMSDOS_MAXNAME];	/* Not '\0' terminated */
+							/* but '\0' padded, so it will allow */
+							/* for adding news fields in this record */
+							/* by reducing the size of name[] */
+};
+#define UMSDOS_HIDDEN	1	/* Never show this entry in directory search */
+#define UMSDOS_HLINK	2	/* It is a (pseudo) hard link */
+
+/* #Specification: EMD file / record size
+	Entry are 64 bytes wide in the EMD file. It allows for a 30 characters
+	name. If a name is longer, contiguous entries are allocated. So a
+	umsdos_dirent may span multiple records.
+*/
+#define UMSDOS_REC_SIZE		64
+
+/* Translation between MSDOS name and UMSDOS name */
+struct umsdos_info{
+	int msdos_reject;	/* Tell if the file name is invalid for MSDOS */
+						/* See umsdos_parse */
+	struct umsdos_fake_info fake;
+	struct umsdos_dirent entry;
+	off_t f_pos;		/* offset of the entry in the EMD file */
+						/* or offset where the entry may be store */
+						/* if it is a new entry */
+	int recsize;		/* Record size needed to store entry */
+};
+
+/* Definitions for ioctl (number randomly chosen) */
+/* The next ioctl commands operate only on the DOS directory */
+/* The file umsdos_progs/umsdosio.c contain a string table */
+/* based on the order of those definition. Keep it in sync */
+#define UMSDOS_READDIR_DOS	1234	/* Do a readdir of the DOS directory */
+#define UMSDOS_UNLINK_DOS	1235	/* Erase in the DOS directory only */
+#define UMSDOS_RMDIR_DOS	1236	/* rmdir in the DOS directory only */
+#define UMSDOS_STAT_DOS		1237	/* Get info about a file */
+/* The next ioctl commands operate only on the EMD file */
+#define UMSDOS_CREAT_EMD	1238	/* Create a file */
+#define UMSDOS_UNLINK_EMD	1239	/* unlink (rmdir) a file */
+#define UMSDOS_READDIR_EMD	1240	/* read the EMD file only. */
+#define UMSDOS_GETVERSION	1241	/* Get the release number of UMSDOS */
+#define UMSDOS_INIT_EMD		1242	/* Create the EMD file if not there */
+#define UMSDOS_DOS_SETUP	1243	/* Set the defaults of the MsDOS driver */
+
+#ifndef _SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+struct umsdos_ioctl{
+	struct dirent dos_dirent;
+	struct umsdos_dirent umsdos_dirent;
+	struct stat stat;
+	char version,release;
+};
+
+/* Different macros to access struct umsdos_dirent */
+#define EDM_ENTRY_ISUSED(e) ((e)->name_len!=0)
+
+#ifdef __KERNEL__
+
+extern struct inode_operations umsdos_dir_inode_operations;
+extern struct file_operations  umsdos_file_operations;
+extern struct inode_operations umsdos_file_inode_operations;
+extern struct inode_operations umsdos_file_inode_operations_no_bmap;
+extern struct inode_operations umsdos_symlink_inode_operations;
+
+#include <linux/umsdos_fs.p>
+
+#endif /* __KERNEL__ */
+
+#endif
diff -u --recursive --new-file v1.1.34/linux/include/linux/umsdos_fs.p linux/include/linux/umsdos_fs.p
--- v1.1.34/linux/include/linux/umsdos_fs.p	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/umsdos_fs.p	Sun Jul 24 17:39:47 1994
@@ -0,0 +1,133 @@
+/* check.c 20/07/94 10.08.36 */
+void check_page_tables (void);
+/* dir.c 22/07/94 01.06.58 */
+int UMSDOS_dir_read (struct inode *inode,
+	 struct file *filp,
+	 char *buf,
+	 int count);
+void umsdos_lookup_patch (struct inode *dir,
+	 struct inode *inode,
+	 struct umsdos_dirent *entry,
+	 off_t emd_pos);
+int umsdos_inode2entry (struct inode *dir,
+	 struct inode *inode,
+	 struct umsdos_dirent *entry);
+int umsdos_locate_path (struct inode *inode, char *path);
+int umsdos_is_pseudodos (struct inode *dir, const char *name, int len);
+int UMSDOS_lookup (struct inode *dir,
+	 const char *name,
+	 int len,
+	 struct inode **result);
+int umsdos_hlink2inode (struct inode *hlink, struct inode **result);
+/* emd.c 22/07/94 01.06.38 */
+int umsdos_readdir_kmem (struct inode *inode,
+	 struct file *filp,
+	 struct dirent *dirent,
+	 int count);
+int umsdos_file_read_kmem (struct inode *inode,
+	 struct file *filp,
+	 char *buf,
+	 int count);
+int umsdos_file_write_kmem (struct inode *inode,
+	 struct file *filp,
+	 char *buf,
+	 int count);
+int umsdos_emd_dir_write (struct inode *emd_dir,
+	 struct file *filp,
+	 char *buf,
+	 int count);
+int umsdos_emd_dir_read (struct inode *emd_dir,
+	 struct file *filp,
+	 char *buf,
+	 int count);
+struct inode *umsdos_emd_dir_lookup (struct inode *dir, int creat);
+int umsdos_emd_dir_readentry (struct inode *emd_dir,
+	 struct file *filp,
+	 struct umsdos_dirent *entry);
+int umsdos_writeentry (struct inode *dir,
+	 struct inode *emd_dir,
+	 struct umsdos_info *info,
+	 int free_entry);
+int umsdos_newentry (struct inode *dir, struct umsdos_info *info);
+int umsdos_newhidden (struct inode *dir, struct umsdos_info *info);
+int umsdos_delentry (struct inode *dir,
+	 struct umsdos_info *info,
+	 int isdir);
+int umsdos_isempty (struct inode *dir);
+int umsdos_findentry (struct inode *dir,
+	 struct umsdos_info *info,
+	 int expect);
+/* file.c 20/07/94 10.08.36 */
+/* inode.c 20/07/94 10.08.36 */
+void UMSDOS_put_inode (struct inode *inode);
+void UMSDOS_put_super (struct super_block *sb);
+void UMSDOS_statfs (struct super_block *sb, struct statfs *buf);
+int umsdos_real_lookup (struct inode *dir,
+	 const char *name,
+	 int len,
+	 struct inode **result);
+void umsdos_setup_dir_inode (struct inode *inode);
+void umsdos_set_dirinfo (struct inode *inode,
+	 struct inode *dir,
+	 off_t f_pos);
+int umsdos_isinit (struct inode *inode);
+void umsdos_patch_inode (struct inode *inode,
+	 struct inode *dir,
+	 off_t f_pos);
+int umsdos_get_dirowner (struct inode *inode, struct inode **result);
+void UMSDOS_read_inode (struct inode *inode);
+void UMSDOS_write_inode (struct inode *inode);
+int UMSDOS_notify_change (int flags, struct inode *inode);
+struct super_block *UMSDOS_read_super (struct super_block *s,
+	 void *data,
+	 int silent);
+/* ioctl.c 20/07/94 10.08.36 */
+int UMSDOS_ioctl_dir (struct inode *dir,
+	 struct file *filp,
+	 unsigned int cmd,
+	 unsigned long data);
+/* mangle.c 20/07/94 10.08.36 */
+void umsdos_manglename (struct umsdos_info *info);
+int umsdos_evalrecsize (int len);
+int umsdos_parse (const char *fname, int len, struct umsdos_info *info);
+/* namei.c 22/07/94 00.59.28 */
+void umsdos_lockcreate (struct inode *dir);
+void umsdos_startlookup (struct inode *dir);
+void umsdos_unlockcreate (struct inode *dir);
+void umsdos_endlookup (struct inode *dir);
+int UMSDOS_symlink (struct inode *dir,
+	 const char *name,
+	 int len,
+	 const char *symname);
+int UMSDOS_link (struct inode *oldinode,
+	 struct inode *dir,
+	 const char *name,
+	 int len);
+int UMSDOS_create (struct inode *dir,
+	 const char *name,
+	 int len,
+	 int mode,
+	 struct inode **result);
+int UMSDOS_mkdir (struct inode *dir,
+	 const char *name,
+	 int len,
+	 int mode);
+int UMSDOS_mknod (struct inode *dir,
+	 const char *name,
+	 int len,
+	 int mode,
+	 int rdev);
+int UMSDOS_rmdir (struct inode *dir, const char *name, int len);
+int UMSDOS_unlink (struct inode *dir, const char *name, int len);
+int UMSDOS_rename (struct inode *old_dir,
+	 const char *old_name,
+	 int old_len,
+	 struct inode *new_dir,
+	 const char *new_name,
+	 int new_len);
+/* rdir.c 20/07/94 10.08.38 */
+int UMSDOS_rlookup (struct inode *dir,
+	 const char *name,
+	 int len,
+	 struct inode **result);
+/* symlink.c 22/07/94 00.59.10 */
diff -u --recursive --new-file v1.1.34/linux/include/linux/umsdos_fs_i.h linux/include/linux/umsdos_fs_i.h
--- v1.1.34/linux/include/linux/umsdos_fs_i.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/umsdos_fs_i.h	Sun Jul 24 17:39:47 1994
@@ -0,0 +1,140 @@
+#ifndef UMSDOS_FS_I_H
+#define UMSDOS_FS_I_H
+
+#ifndef _LINUX_TYPES_H
+#include <linux/types.h>
+#endif
+#include <linux/msdos_fs_i.h>
+#include <linux/pipe_fs_i.h>
+
+/* #Specification: strategy / in memory inode
+	Here is the information specific to the inode of the UMSDOS file
+	system. This information is added to the end of the standard struct
+	inode. Each file system has its own extension to struct inode,
+	so do the umsdos file system.
+
+	The strategy is to have the umsdos_inode_info as a superset of
+	the msdos_inode_info, since most of the time the job is done
+	by the msdos fs code.
+
+	So we duplicate the msdos_inode_info, and add our own info at the
+	end.
+
+	For all file type (and directory) the inode has a reference to:
+		the directory which hold this entry: i_dir_owner
+		The EMD file of i_dir_owner: i_emd_owner
+		The offset in this EMD file of the entry: pos
+
+	For directory, we also have a reference to the inode of its
+	own EMD file. Also, we have dir_locking_info to help synchronise
+	file creation and file lookup. This data is sharing space with
+	the pipe_inode_info not used by directory. See also msdos_fs_i.h
+	for more information about pipe_inode_info and msdos_inode_info.
+
+	Special file and fifo do have an inode which correspond to an
+	empty MSDOS file.
+
+	symlink are processed mostly like regular file. The content is the
+	link.
+
+	fifos add there own extension to the inode. I have reserved some
+	space for fifos side by side with msdos_inode_info. This is just
+	to for the show, because msdos_inode_info already include the
+	pipe_inode_info.
+
+	The UMSDOS specific extension is placed after the union.
+*/
+struct dir_locking_info {
+	struct wait_queue *p;
+	short int looking;		/* How many process doing a lookup */
+	short int creating;		/* Is there any creation going on here */
+							/* Only one at a time, although one */
+							/* may recursivly lock, so it is a counter */
+	long pid;				/* pid of the process owning the creation */
+							/* lock */
+};
+struct umsdos_inode_info {
+	union {
+		struct msdos_inode_info msdos_info;
+		struct pipe_inode_info pipe_info;
+		struct dir_locking_info dir_info;
+	}u;	/* Simply a filler, never referenced by fs/umsdos/... */
+	unsigned long i_dir_owner;	/* Inode of the dir which hold this */
+								/* entry */
+	unsigned long i_emd_owner;	/* Inode of the EMD file of i_dir_owner */
+	off_t pos;					/* Entry offset in the emd_owner file */
+	/* The rest is used only if this inode describe a directory */
+	unsigned long i_emd_dir;	/* Inode of the EMD file of this inode */
+};
+
+#endif
+#ifndef UMSDOS_FS_I_H
+#define UMSDOS_FS_I_H
+
+#ifndef _LINUX_TYPES_H
+#include <linux/types.h>
+#endif
+#include <linux/msdos_fs_i.h>
+#include <linux/pipe_fs_i.h>
+
+/* #Specification: strategy / in memory inode
+	Here is the information specific to the inode of the UMSDOS file
+	system. This information is added to the end of the standard struct
+	inode. Each file system has its own extension to struct inode,
+	so do the umsdos file system.
+
+	The strategy is to have the umsdos_inode_info as a superset of
+	the msdos_inode_info, since most of the time the job is done
+	by the msdos fs code.
+
+	So we duplicate the msdos_inode_info, and add our own info at the
+	end.
+
+	For all file type (and directory) the inode has a reference to:
+		the directory which hold this entry: i_dir_owner
+		The EMD file of i_dir_owner: i_emd_owner
+		The offset in this EMD file of the entry: pos
+
+	For directory, we also have a reference to the inode of its
+	own EMD file. Also, we have dir_locking_info to help synchronise
+	file creation and file lookup. This data is sharing space with
+	the pipe_inode_info not used by directory. See also msdos_fs_i.h
+	for more information about pipe_inode_info and msdos_inode_info.
+
+	Special file and fifo do have an inode which correspond to an
+	empty MSDOS file.
+
+	symlink are processed mostly like regular file. The content is the
+	link.
+
+	fifos add there own extension to the inode. I have reserved some
+	space for fifos side by side with msdos_inode_info. This is just
+	to for the show, because msdos_inode_info already include the
+	pipe_inode_info.
+
+	The UMSDOS specific extension is placed after the union.
+*/
+struct dir_locking_info {
+	struct wait_queue *p;
+	short int looking;		/* How many process doing a lookup */
+	short int creating;		/* Is there any creation going on here */
+							/* Only one at a time, although one */
+							/* may recursivly lock, so it is a counter */
+	long pid;				/* pid of the process owning the creation */
+							/* lock */
+};
+struct umsdos_inode_info {
+	union {
+		struct msdos_inode_info msdos_info;
+		struct pipe_inode_info pipe_info;
+		struct dir_locking_info dir_info;
+	}u;	/* Simply a filler, never referenced by fs/umsdos/... */
+	unsigned long i_dir_owner;	/* Inode of the dir which hold this */
+								/* entry */
+	unsigned long i_emd_owner;	/* Inode of the EMD file of i_dir_owner */
+	off_t pos;					/* Entry offset in the emd_owner file */
+	/* The rest is used only if this inode describe a directory */
+	unsigned long i_emd_dir;	/* Inode of the EMD file of this inode */
+};
+
+#endif
diff -u --recursive --new-file v1.1.34/linux/init/main.c linux/init/main.c
--- v1.1.34/linux/init/main.c	Tue Jun 28 22:30:27 1994
+++ linux/init/main.c	Sun Jul 24 17:39:47 1994
@@ -500,6 +500,22 @@
 
 	setup();
 	sprintf(term, "TERM=con%dx%d", ORIG_VIDEO_COLS, ORIG_VIDEO_LINES);
+
+	#ifdef CONFIG_UMSDOS_FS
+	{
+		/*
+			When mounting a umsdos fs as root, we detect
+			the pseudo_root (/linux) and initialise it here.
+			pseudo_root is defined in fs/umsdos/inode.c
+		*/
+		extern struct inode *pseudo_root;
+		if (pseudo_root != NULL){
+			current->fs->root = pseudo_root;
+			current->fs->pwd  = pseudo_root;
+		}
+	}
+	#endif
+
 	(void) open("/dev/tty1",O_RDWR,0);
 	(void) dup(0);
 	(void) dup(0);
diff -u --recursive --new-file v1.1.34/linux/kernel/dma.c linux/kernel/dma.c
--- v1.1.34/linux/kernel/dma.c	Wed Dec  1 14:44:15 1993
+++ linux/kernel/dma.c	Sat Jul 23 12:58:38 1994
@@ -26,11 +26,11 @@
 
 
 /* Channel n is busy iff dma_chan_busy[n] != 0.
- * DMA0 is reserved for DRAM refresh, I think.
- * DMA4 is reserved for cascading (?).
+ * DMA0 used to be reserved for DRAM refresh, but apparently not any more...
+ * DMA4 is reserved for cascading.
  */
 static volatile unsigned int dma_chan_busy[MAX_DMA_CHANNELS] = {
-	1, 0, 0, 0, 1, 0, 0, 0
+	0, 0, 0, 0, 1, 0, 0, 0
 };
 
 
diff -u --recursive --new-file v1.1.34/linux/mm/memory.c linux/mm/memory.c
--- v1.1.34/linux/mm/memory.c	Sat Jul 23 17:38:38 1994
+++ linux/mm/memory.c	Sun Jul 24 17:39:47 1994
@@ -269,10 +269,8 @@
 			}
 			if (pg & PAGE_COW)
 				pg &= ~PAGE_RW;
-			if (in_swap_cache(pg)) {
-				swap_cache_invalidate(pg);
+			if (delete_from_swap_cache(pg))
 				pg |= PAGE_DIRTY;
-			}
 			*new_page_table = pg;
 			*old_page_table = pg;
 			mem_map[MAP_NR(pg)]++;
@@ -586,7 +584,7 @@
 	if (old_page & PAGE_RW)
 		goto end_wp_page;
 	vma->vm_task->mm->min_flt++;
-	prot = (old_page & ~PAGE_MASK) | PAGE_RW;
+	prot = (old_page & ~PAGE_MASK) | PAGE_RW | PAGE_DIRTY;
 	old_page &= PAGE_MASK;
 	if (mem_map[MAP_NR(old_page)] != 1) {
 		if (new_page) {
@@ -604,7 +602,7 @@
 		invalidate();
 		return;
 	}
-	*(unsigned long *) pte |= PAGE_RW;
+	*(unsigned long *) pte |= PAGE_RW | PAGE_DIRTY;
 	invalidate();
 	if (new_page)
 		free_page(new_page);
@@ -657,6 +655,8 @@
 		goto check_wp_fault_by_hand;
 	for (;;) {
 		struct vm_area_struct * next;
+		if (!(vma->vm_page_prot & PAGE_USER))
+			goto bad_area;
 		if (type != VERIFY_READ && !(vma->vm_page_prot & (PAGE_COW | PAGE_RW)))
 			goto bad_area;
 		if (vma->vm_end - start >= size)
@@ -765,7 +765,7 @@
 			return 0;
 		from |= PAGE_DIRTY;
 		*(unsigned long *) from_page = from;
-		swap_cache_invalidate(from);
+		delete_from_swap_cache(from);
 		invalidate();
 	}
 	mem_map[MAP_NR(from)]++;
@@ -1000,8 +1000,10 @@
 		if (bit < 32)
 			current->screen_bitmap |= 1 << bit;
 	}
+	if (!(vma->vm_page_prot & PAGE_USER))
+		goto bad_area;
 	if (error_code & PAGE_PRESENT) {
-		if ((vma->vm_page_prot & (PAGE_RW | PAGE_COW | PAGE_PRESENT)) == PAGE_PRESENT)
+		if (!(vma->vm_page_prot & (PAGE_RW | PAGE_COW)))
 			goto bad_area;
 #ifdef CONFIG_TEST_VERIFY_AREA
 		if (regs->cs == KERNEL_CS)
@@ -1010,8 +1012,6 @@
 		do_wp_page(vma, address, error_code);
 		return;
 	}
-	if (!(vma->vm_page_prot & PAGE_PRESENT))
-		goto bad_area;
 	do_no_page(vma, address, error_code);
 	return;
 
diff -u --recursive --new-file v1.1.34/linux/mm/mmap.c linux/mm/mmap.c
--- v1.1.34/linux/mm/mmap.c	Fri Jul 22 16:41:03 1994
+++ linux/mm/mmap.c	Sun Jul 24 17:39:47 1994
@@ -21,24 +21,21 @@
 		    unsigned long);
 /*
  * description of effects of mapping type and prot in current implementation.
- * this is due to the current handling of page faults in memory.c. the expected
+ * this is due to the limited x86 page protection hardware.  The expected
  * behavior is in parens:
  *
  * map_type	prot
  *		PROT_NONE	PROT_READ	PROT_WRITE	PROT_EXEC
- * MAP_SHARED	r: (no) yes	r: (yes) yes	r: (no) yes	r: (no) no
- *		w: (no) yes	w: (no) copy	w: (yes) yes	w: (no) no
- *		x: (no) no	x: (no) no	x: (no) no	x: (yes) no
+ * MAP_SHARED	r: (no) no	r: (yes) yes	r: (no) yes	r: (no) yes
+ *		w: (no) no	w: (no) no	w: (yes) yes	w: (no) no
+ *		x: (no) no	x: (no) yes	x: (no) yes	x: (yes) yes
  *		
- * MAP_PRIVATE	r: (no) yes	r: (yes) yes	r: (no) yes	r: (no) no
- *		w: (no) copy	w: (no) copy	w: (copy) copy	w: (no) no
- *		x: (no) no	x: (no) no	x: (no) no	x: (yes) no
+ * MAP_PRIVATE	r: (no) no	r: (yes) yes	r: (no) yes	r: (no) yes
+ *		w: (no) no	w: (no) no	w: (copy) copy	w: (no) no
+ *		x: (no) no	x: (no) yes	x: (no) yes	x: (yes) yes
  *
  */
 
-#define CODE_SPACE(addr)	\
- (PAGE_ALIGN(addr) < current->start_code + current->end_code)
-
 int do_mmap(struct file * file, unsigned long addr, unsigned long len,
 	unsigned long prot, unsigned long flags, unsigned long off)
 {
@@ -116,8 +113,8 @@
 			mask |= PAGE_COPY;
 		else
 			mask |= PAGE_SHARED;
-	if (!mask)
-		return -EINVAL;
+	if (!mask) /* PROT_NONE */
+		mask = PAGE_PRESENT;	/* none of PAGE_USER, PAGE_RW, PAGE_COW */
 
 	do_munmap(addr, len);	/* Clear old maps */
 
diff -u --recursive --new-file v1.1.34/linux/mm/swap.c linux/mm/swap.c
--- v1.1.34/linux/mm/swap.c	Sat Jul 23 17:38:38 1994
+++ linux/mm/swap.c	Sun Jul 24 17:39:47 1994
@@ -8,7 +8,6 @@
  * This file should contain most things doing the swapping from/to disk.
  * Started 18.12.91
  */
-#define SWAP_CACHE_INFO
 
 #include <linux/mm.h>
 #include <linux/sched.h>
@@ -18,6 +17,7 @@
 #include <linux/errno.h>
 #include <linux/string.h>
 #include <linux/stat.h>
+#include <linux/fs.h>
 
 #include <asm/system.h> /* for cli()/sti() */
 #include <asm/bitops.h>
@@ -49,68 +49,30 @@
 extern int shm_swap (int);
 
 unsigned long *swap_cache;
-static unsigned long swap_cache_size;
 
 #ifdef SWAP_CACHE_INFO
-static unsigned long add_calls_total = 0;
-static unsigned long add_calls_success = 0;
-static unsigned long del_calls_total = 0;
-static unsigned long del_calls_success = 0;
-static unsigned long find_calls_total = 0;
-static unsigned long find_calls_success = 0;
+unsigned long swap_cache_add_total = 0;
+unsigned long swap_cache_add_success = 0;
+unsigned long swap_cache_del_total = 0;
+unsigned long swap_cache_del_success = 0;
+unsigned long swap_cache_find_total = 0;
+unsigned long swap_cache_find_success = 0;
 
-extern inline void show_swap_cache_info (void)
+extern inline void show_swap_cache_info(void)
 {
 	printk("Swap cache: add %ld/%ld, delete %ld/%ld, find %ld/%ld\n",
-	       add_calls_total, add_calls_success, 
-	       del_calls_total, del_calls_success,
-	       find_calls_total, find_calls_success);
+		swap_cache_add_total, swap_cache_add_success, 
+		swap_cache_del_total, swap_cache_del_success,
+		swap_cache_find_total, swap_cache_find_success);
 }
 #endif
 
-extern inline unsigned long init_swap_cache (unsigned long mem_start,
-					    unsigned long mem_end)
+extern inline int add_to_swap_cache(unsigned long addr, unsigned long entry)
 {
-	mem_start = (mem_start + 15) & ~15;
-	swap_cache = (unsigned long *) mem_start;
-	swap_cache_size = mem_end >> PAGE_SHIFT;
-	memset(swap_cache, 0, swap_cache_size * sizeof (unsigned long));
-#ifdef SWAP_CACHE_INFO
-	printk("%ld bytes for swap cache allocated\n",
-	       swap_cache_size * sizeof(unsigned long));
-#endif	
-	
-	return (unsigned long) (swap_cache + swap_cache_size);
-}
-
-extern inline long find_in_swap_cache (unsigned long addr)
-{
-	unsigned long entry;
-
-#ifdef SWAP_CACHE_INFO
-	find_calls_total++;
-#endif
-	__asm__ __volatile__ (
-			      "xchgl %0,%1\n"
-			      : "=m" (swap_cache[addr >> PAGE_SHIFT]),
-			        "=r" (entry)
-			      : "0" (swap_cache[addr >> PAGE_SHIFT]),
-			        "1" (0)
-			      );
-#ifdef SWAP_CACHE_INFO
-	if (entry)
-		find_calls_success++;
-#endif	
-	
-	return entry;
-}
-
-extern  inline int add_to_swap_cache (unsigned long addr, unsigned long entry)
-{
 	struct swap_info_struct * p = &swap_info[SWP_TYPE(entry)];
 	
 #ifdef SWAP_CACHE_INFO
-	add_calls_total++;
+	swap_cache_add_total++;
 #endif
 	if ((p->flags & SWP_WRITEOK) == SWP_WRITEOK) { 
 		__asm__ __volatile__ (
@@ -124,39 +86,30 @@
 			printk("swap_cache: replacing non-NULL entry\n");
 		}
 #ifdef SWAP_CACHE_INFO
-		add_calls_success++;
+		swap_cache_add_success++;
 #endif
 		return 1;
 	}
 	return 0;
 }
 
-
-extern inline int delete_from_swap_cache(unsigned long addr)
+static unsigned long init_swap_cache(unsigned long mem_start,
+	unsigned long mem_end)
 {
-	unsigned long entry;
-	
+	unsigned long swap_cache_size;
+
+	mem_start = (mem_start + 15) & ~15;
+	swap_cache = (unsigned long *) mem_start;
+	swap_cache_size = mem_end >> PAGE_SHIFT;
+	memset(swap_cache, 0, swap_cache_size * sizeof (unsigned long));
 #ifdef SWAP_CACHE_INFO
-	del_calls_total++;
+	printk("%ld bytes for swap cache allocated\n",
+	       swap_cache_size * sizeof(unsigned long));
 #endif	
-	__asm__ __volatile__ (
-			      "xchgl %0,%1\n"
-			      : "=m" (swap_cache[addr >> PAGE_SHIFT]),
-			        "=r" (entry)
-			      : "0" (swap_cache[addr >> PAGE_SHIFT]),
-			        "1" (0)
-			      );
-	if (entry)  {
-#ifdef SWAP_CACHE_INFO
-		del_calls_success++;
-#endif
-		swap_free(entry);
-		return 1;
-	}
-	return 0;
+	
+	return (unsigned long) (swap_cache + swap_cache_size);
 }
 
-
 void rw_swap_page(int rw, unsigned long entry, char * buf)
 {
 	unsigned long type, offset;
@@ -186,18 +139,44 @@
 	if (p->swap_device) {
 		ll_rw_page(rw,p->swap_device,offset,buf);
 	} else if (p->swap_file) {
+		struct inode *swapf = p->swap_file;
 		unsigned int zones[8];
-		unsigned int block;
-		int i, j;
-
-		block = offset << (12 - p->swap_file->i_sb->s_blocksize_bits);
-
-		for (i=0, j=0; j< PAGE_SIZE ; i++, j +=p->swap_file->i_sb->s_blocksize)
-			if (!(zones[i] = bmap(p->swap_file,block++))) {
-				printk("rw_swap_page: bad swap file\n");
-				return;
+		int i;
+		if (swapf->i_op->bmap == NULL
+			&& swapf->i_op->smap != NULL){
+			/*
+				With MsDOS, we use msdos_smap which return
+				a sector number (not a cluster or block number).
+				It is a patch to enable the UMSDOS project.
+				Other people are working on better solution.
+
+				It sounds like ll_rw_swap_file defined
+				it operation size (sector size) based on
+				PAGE_SIZE and the number of block to read.
+				So using bmap ou smap should work even if
+				smap will requiered more blocks.
+			*/
+			int j;
+			unsigned int block = offset << 3;
+
+			for (i=0, j=0; j< PAGE_SIZE ; i++, j += 512){
+				if (!(zones[i] = swapf->i_op->smap(swapf,block++))) {
+					printk("rw_swap_page: bad swap file\n");
+					return;
+				}
 			}
-		ll_rw_swap_file(rw,p->swap_file->i_dev, zones, i,buf);
+		}else{
+			int j;
+			unsigned int block = offset
+				<< (12 - swapf->i_sb->s_blocksize_bits);
+
+			for (i=0, j=0; j< PAGE_SIZE ; i++, j +=swapf->i_sb->s_blocksize)
+				if (!(zones[i] = bmap(swapf,block++))) {
+					printk("rw_swap_page: bad swap file\n");
+					return;
+				}
+		}
+		ll_rw_swap_file(rw,swapf->i_dev, zones, i,buf);
 	} else
 		printk("re_swap_page: no swap file or device\n");
 	if (offset && !clear_bit(offset,p->swap_lockmap))
