]> git.neil.brown.name Git - history.git/commitdiff
Import 1.1.35 1.1.35
authorLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:09:35 +0000 (15:09 -0500)
committerLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:09:35 +0000 (15:09 -0500)
52 files changed:
CREDITS
Makefile
config.in
drivers/char/Makefile
drivers/char/console.c
drivers/char/mem.c
drivers/char/tpqic02.c
drivers/char/tty_io.c
drivers/net/auto_irq.c
drivers/scsi/ultrastor.c
fs/Makefile
fs/filesystems.c
fs/msdos/Makefile
fs/msdos/dir.c
fs/msdos/file.c
fs/msdos/mmap.c [new file with mode: 0644]
fs/msdos/namei.c
fs/umsdos/Makefile [new file with mode: 0644]
fs/umsdos/README [new file with mode: 0644]
fs/umsdos/check.c [new file with mode: 0644]
fs/umsdos/dir.c [new file with mode: 0644]
fs/umsdos/emd.c [new file with mode: 0644]
fs/umsdos/file.c [new file with mode: 0644]
fs/umsdos/inode.c [new file with mode: 0644]
fs/umsdos/ioctl.c [new file with mode: 0644]
fs/umsdos/mangle.c [new file with mode: 0644]
fs/umsdos/namei.c [new file with mode: 0644]
fs/umsdos/notes [new file with mode: 0644]
fs/umsdos/rdir.c [new file with mode: 0644]
fs/umsdos/symlink.c [new file with mode: 0644]
include/linux/ext_fs.h
include/linux/fs.h
include/linux/iso_fs.h
include/linux/minix_fs.h
include/linux/mm.h
include/linux/msdos_fs.h
include/linux/msdos_fs_i.h
include/linux/mtio.h
include/linux/net.h
include/linux/nfs_fs.h
include/linux/sched.h
include/linux/timer.h
include/linux/tpqic02.h
include/linux/tty.h
include/linux/umsdos_fs.h [new file with mode: 0644]
include/linux/umsdos_fs.p [new file with mode: 0644]
include/linux/umsdos_fs_i.h [new file with mode: 0644]
init/main.c
kernel/dma.c
mm/memory.c
mm/mmap.c
mm/swap.c

diff --git a/CREDITS b/CREDITS
index 53126bfccf04f0a32cae08c7dd8b11725a771ace..21297213da6003b89102cfc815b0e8b4db67436f 100644 (file)
--- a/CREDITS
+++ b/CREDITS
@@ -53,7 +53,6 @@ S: The Netherlands
 
 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
 
index 57ab1a5a7b7f789707d13c33c51253f2c4b0861e..637f8fdfa837a6dc7763341e2cb6c22069d870c2 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
 VERSION = 1
 PATCHLEVEL = 1
-SUBLEVEL = 34
+SUBLEVEL = 35
 
 all:   Version zImage
 
index 4ad54aec91a80a6fe2d0448834401769292631da..2171d49493628e881c6ede6a1fc1948c9175e997 100644 (file)
--- a/config.in
+++ b/config.in
@@ -150,6 +150,7 @@ bool 'Extended fs support' CONFIG_EXT_FS n
 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 @@ fi
 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
index eb85fdb24faf73db33aecf7f2d033839197ac3c6..e9c3d09be277eff1f8b38c2fa2c605757a6e351a 100644 (file)
@@ -58,7 +58,7 @@ OBJS := $(OBJS) psaux.o
 SRCS := $(SRCS) psaux.c
 endif
 
-ifdef CONFIG_TAPE_QIC02
+ifdef CONFIG_QIC02_TAPE
 OBJS := $(OBJS) tpqic02.o 
 SRCS := $(SRCS) tpqic02.c
 endif
index aa306a7e81d503927d0dd1c75584c25e4c23006b..1d189a9d25e3423c879af4746439e4f804c3bb73 100644 (file)
@@ -156,7 +156,7 @@ static struct {
        /* 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 @@ static void set_mode(int currcons, int on_off)
                                        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 @@ static inline short limit(const int v, const int l, const int u)
 }
 
 /* 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(). */
index e859a9a0f55bae6af0858c98e9da51bb7b7701f2..ffae4af1471f01264d0d476447c2825509dee437 100644 (file)
@@ -411,8 +411,8 @@ long chr_dev_init(long mem_start, long mem_end)
 #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
index 4d215c9e0614b2acc7dff961c4028feb66ebe4cf..01ca1fd1d79c28d6dd820d806685e890547ca557 100644 (file)
@@ -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.]
  * 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:
  *
  * 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.
  *
 #include <linux/config.h>
 
 /* skip this driver if not required for this configuration */
-#if CONFIG_TAPE_QIC02
+#if CONFIG_QIC02_TAPE
 
 /*
 #define TDEBUG
 #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 @@ static char rcs_date[] = "$Date: 1993/06/18 19:04:33 $";
  * 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_read_eof = NO;        /* set when we've done that */
 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 @@ static int  mode_access;    /* access mode: READ or WRITE */
 
 /* 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 @@ static int  mode_access;    /* access mode: READ or WRITE */
  * 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 @@ static char *format_names[] = {
  * "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 @@ static struct exception_list_type {
                /* 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 inline void byte_swap_w(volatile unsigned short * w)
  */
 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 @@ static void report_exception(unsigned n)
  * 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 @@ static int decode_exception_nr(short s)     /* s must be short, because of sign-exte
                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 @@ static void report_error(int s)
                                        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 @@ static void report_error(int s)
                                         * 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 @@ static void handle_exception(int exnr, int exbits)
                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 void handle_exception(int exnr, int exbits)
 
 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 @@ static int tape_reset(int verbose)
 {
        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 @@ static int tape_reset(int verbose)
        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 @@ static int tape_reset(int verbose)
 /* 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 @@ static int notify_cmd(char cmd, short ignore_ex)
 {
        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 @@ static int wait_for_ready(time_t timeout)
         * 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 @@ static int wait_for_ready(time_t timeout)
        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 @@ static int wait_for_ready(time_t timeout)
        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 @@ static int wait_for_ready(time_t timeout)
        }
 
        /* 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 @@ static int wait_for_ready(time_t timeout)
 } /* 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 @@ static int send_qic02_cmd(int cmd, time_t timeout, int ignore_ex)
 {
        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 @@ static int send_qic02_cmd(int cmd, time_t timeout, int ignore_ex)
 
        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 @@ static int rdstatus(char *stp, unsigned size, char qcmd)
         * 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 @@ static int rdstatus(char *stp, unsigned size, char qcmd)
         */
 
        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 @@ static int rdstatus(char *stp, unsigned size, char qcmd)
  * 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 @@ static int get_ext_status3(void)
        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 @@ static int tp_sense(int ignore)
        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 @@ static int tp_sense(int ignore)
        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 @@ static int wait_for_rewind(time_t timeout)
 {
        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 @@ static int ll_do_qic_cmd(int cmd, time_t timeout)
 {
        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 @@ static int ll_do_qic_cmd(int cmd, time_t timeout)
                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 @@ static int ll_do_qic_cmd(int cmd, time_t timeout)
        /* 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 @@ static int ll_do_qic_cmd(int cmd, time_t timeout)
                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 @@ static int ll_do_qic_cmd(int cmd, time_t timeout)
                }
        }
        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 @@ static void terminate_read(int cmd)
                        /* 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 @@ static void terminate_write(int cmd)
                        /* 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 terminate_write(int cmd)
 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 @@ static int do_qic_cmd(int cmd, time_t timeout)
        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 @@ static int do_ioctl_cmd(int cmd)
                        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 @@ static int do_ioctl_cmd(int cmd)
                        /* 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 @@ static int do_ioctl_cmd(int cmd)
                        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 @@ static int do_ioctl_cmd(int cmd)
                         * 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 @@ static int do_ioctl_cmd(int cmd)
                         * 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 @@ static int do_ioctl_cmd(int cmd)
                         * 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 int do_ioctl_cmd(int cmd)
 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 @@ static inline void dma_transfer(void)
 
 
 /* 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 @@ static int start_dma(short mode, unsigned long bytes_todo)
 {
        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 @@ static int start_dma(short mode, unsigned long bytes_todo)
                    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 @@ static int start_dma(short mode, unsigned long bytes_todo)
 #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 @@ static int start_dma(short mode, unsigned long bytes_todo)
                                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 @@ static int start_dma(short mode, unsigned long bytes_todo)
                 *
                 * ******** 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,22 +1693,31 @@ static void end_dma(unsigned long * bytes_done)
 
        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 @@ static void end_dma(unsigned long * bytes_done)
 /*********** 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 @@ static void tape_qic02_times_out(void)
                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 @@ static void tape_qic02_times_out(void)
  */
 
 
-/* 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 @@ static void tape_qic02_times_out(void)
  * 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 @@ static void tape_qic02_interrupt(int unused)
 
                        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 @@ static void tape_qic02_interrupt(int unused)
 /* 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 @@ static void tape_qic02_interrupt(int unused)
                        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 @@ static int tape_qic02_lseek(struct inode * inode, struct file * file, off_t offs
  * 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 @@ static int tape_qic02_read(struct inode * inode, struct file * filp, char * buf,
        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 @@ static int tape_qic02_read(struct inode * inode, struct file * filp, char * buf,
 
        /* 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 @@ static int tape_qic02_read(struct inode * inode, struct file * filp, char * buf,
 
                /* 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 @@ static int tape_qic02_read(struct inode * inode, struct file * filp, char * buf,
                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 @@ static int tape_qic02_read(struct inode * inode, struct file * filp, char * buf,
                        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 @@ static int tape_qic02_read(struct inode * inode, struct file * filp, char * buf,
                        }
                        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 @@ static int tape_qic02_read(struct inode * inode, struct file * filp, char * buf,
 #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 @@ static int tape_qic02_read(struct inode * inode, struct file * filp, char * buf,
                        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 @@ static int tape_qic02_read(struct inode * inode, struct file * filp, char * buf,
  * 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 @@ static int tape_qic02_write(struct inode * inode, struct file * filp, char * buf
         * 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 @@ static int tape_qic02_write(struct inode * inode, struct file * filp, char * buf
 
                        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 @@ static int tape_qic02_write(struct inode * inode, struct file * filp, char * buf
  ***** 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 @@ static int tape_qic02_write(struct inode * inode, struct file * filp, char * buf
                         */
                        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 @@ static int tape_qic02_write(struct inode * inode, struct file * filp, char * buf
                        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 @@ static int tape_qic02_write(struct inode * inode, struct file * filp, char * buf
  * 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 @@ static int tape_qic02_open(struct inode * inode, struct file * filp)
                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 @@ static int tape_qic02_open(struct inode * inode, struct file * filp)
                        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 @@ static int tape_qic02_open(struct inode * inode, struct file * filp)
                 * 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 @@ static int tape_qic02_open(struct inode * inode, struct file * filp)
                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 @@ static int tape_qic02_open(struct inode * inode, struct file * filp)
 
        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 @@ static int tape_qic02_open(struct inode * inode, struct file * filp)
                        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 @@ static void tape_qic02_release(struct inode * inode, struct file * filp)
        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 @@ static int tape_qic02_ioctl(struct inode * inode, struct file * filp,
        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 @@ static int tape_qic02_ioctl(struct inode * inode, struct file * filp,
        /* 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 @@ static int tape_qic02_ioctl(struct inode * inode, struct file * filp,
                        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 @@ static int tape_qic02_ioctl(struct inode * inode, struct file * filp,
 
                /* 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 @@ static int tape_qic02_ioctl(struct inode * inode, struct file * filp,
                        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 @@ static int tape_qic02_ioctl(struct inode * inode, struct file * filp,
                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 @@ static int tape_qic02_ioctl(struct inode * inode, struct file * filp,
                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 @@ static struct file_operations tape_qic02_fops = {
  * 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 @@ static inline unsigned long const align_buffer(unsigned long a, unsigned size)
 }
 
 
-/* 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 @@ long tape_qic02_init(long kmem_start)
        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 */
index 82b0d20d0b34f695d61aa3ba5303a81d2d66f1f1..cbed2d97ae961def130f73ebfc9c81efd69bdb2e 100644 (file)
@@ -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 @@ static int tty_ioctl(struct inode * inode, struct file * file,
                                        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;
index 9b70c7a2307dd69acccd65516f0825dcacac885b..ba5918734279c64027a5378b21fa7f2c8979eb41 100644 (file)
@@ -62,8 +62,6 @@ int autoirq_setup(int waittime)
     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 @@ int autoirq_setup(int waittime)
     }
     /* 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)
index a3addd9e99c60e6eeab0be6c7d71e39977f6e3f0..bf3d3e9345306c3e7486b5855aa64186b388450c 100644 (file)
@@ -305,7 +305,7 @@ static inline unsigned char xchgb(unsigned char reg,
                                  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;
 }
index 7d5031b2cf92978f30b793b42b0255293bc92a5e..91ea15a6add4638a2226bea87d4c64910230e41f 100644 (file)
@@ -33,6 +33,9 @@ endif
 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
index 958d1d96b8e456b40f25281c731706eb69210098..7bcc695c55cab9f3d8bb3bfb81a78847e4f1408b 100644 (file)
@@ -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>
@@ -52,6 +53,10 @@ asmlinkage int sys_setup(void)
        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
        register_filesystem(&(struct file_system_type)
index 820ec4356b9603083fb97f7d5fe217ae4ae8466f..c9d7acea7f215e3e2d16e994ceadde1ab85cc987 100644 (file)
@@ -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)
index e42cc5c639c6a583895e05cb1d969c331003ce13..bb9a6db09840abb8d2cfd8d85e00564c7f22e7f5 100644 (file)
@@ -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 @@ static int msdos_dir_read(struct inode * inode,struct file * filp, char * buf,in
        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 @@ struct inode_operations msdos_dir_inode_operations = {
        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 @@ static int msdos_readdir(struct inode *inode,struct file *filp,
 /* 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 @@ static int msdos_readdir(struct inode *inode,struct file *filp,
                                        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;
index 3a49ebad74c325eedf5796dcc1ff9eb8f51f21fc..e5cb37ee38f2dc2234a78aca0e526232b853103b 100644 (file)
 #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 @@ static struct file_operations msdos_file_operations = {
        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 @@ struct inode_operations msdos_file_inode_operations = {
        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 @@ struct inode_operations msdos_file_inode_operations_no_bmap = {
        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 @@ static int msdos_file_read(struct inode *inode,struct file *filp,char *buf,
                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 @@ static int msdos_file_read(struct inode *inode,struct file *filp,char *buf,
        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 @@ static int msdos_file_write(struct inode *inode,struct file *filp,char *buf,
                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;
        }
@@ -211,7 +221,6 @@ static int msdos_file_write(struct inode *inode,struct file *filp,char *buf,
        return buf-start;
 }
 
-
 void msdos_truncate(struct inode *inode)
 {
        int cluster;
diff --git a/fs/msdos/mmap.c b/fs/msdos/mmap.c
new file mode 100644 (file)
index 0000000..a8f5dc7
--- /dev/null
@@ -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;
+}
+
index 70fbfa1d2cf6a47a07f5f10533c4ed12db0f2343..2ff85d6c64a4155c5b5b494382d52359b2691601 100644 (file)
@@ -360,7 +360,11 @@ rmdir_done:
 }
 
 
-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 @@ int msdos_unlink(struct inode *dir,const char *name,int len)
                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 @@ unlink_done:
        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 --git a/fs/umsdos/Makefile b/fs/umsdos/Makefile
new file mode 100644 (file)
index 0000000..689c911
--- /dev/null
@@ -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 --git a/fs/umsdos/README b/fs/umsdos/README
new file mode 100644 (file)
index 0000000..20d6909
--- /dev/null
@@ -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 --git a/fs/umsdos/check.c b/fs/umsdos/check.c
new file mode 100644 (file)
index 0000000..d1102b4
--- /dev/null
@@ -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 --git a/fs/umsdos/dir.c b/fs/umsdos/dir.c
new file mode 100644 (file)
index 0000000..8bd2e0e
--- /dev/null
@@ -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 --git a/fs/umsdos/emd.c b/fs/umsdos/emd.c
new file mode 100644 (file)
index 0000000..c0e9803
--- /dev/null
@@ -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 --git a/fs/umsdos/file.c b/fs/umsdos/file.c
new file mode 100644 (file)
index 0000000..3836c43
--- /dev/null
@@ -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 --git a/fs/umsdos/inode.c b/fs/umsdos/inode.c
new file mode 100644 (file)
index 0000000..566539e
--- /dev/null
@@ -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 --git a/fs/umsdos/ioctl.c b/fs/umsdos/ioctl.c
new file mode 100644 (file)
index 0000000..2bf5eb1
--- /dev/null
@@ -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 --git a/fs/umsdos/mangle.c b/fs/umsdos/mangle.c
new file mode 100644 (file)
index 0000000..7b7dba7
--- /dev/null
@@ -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 --git a/fs/umsdos/namei.c b/fs/umsdos/namei.c
new file mode 100644 (file)
index 0000000..9df0832
--- /dev/null
@@ -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 --git a/fs/umsdos/notes b/fs/umsdos/notes
new file mode 100644 (file)
index 0000000..3c47d1f
--- /dev/null
@@ -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 --git a/fs/umsdos/rdir.c b/fs/umsdos/rdir.c
new file mode 100644 (file)
index 0000000..d7272ed
--- /dev/null
@@ -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 --git a/fs/umsdos/symlink.c b/fs/umsdos/symlink.c
new file mode 100644 (file)
index 0000000..aee2a48
--- /dev/null
@@ -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 */
+};
+
+
+
index c2380df3c1ea19a573dc0ed65dffd60d1c6538f8..58eae0b0aa780dd3c91b63905044d897fa6831dd 100644 (file)
@@ -59,6 +59,7 @@ struct ext_dir_entry {
        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_file_inode_operations;
 extern struct inode_operations ext_dir_inode_operations;
 extern struct inode_operations ext_symlink_inode_operations;
 
+#endif /*__KERNEL__ */
 #endif
index 89aeee7f4faa3d4f1bf097b4a36cdc8100959334..448e779e9c637768f788cd24731ca806c6f225c3 100644 (file)
@@ -158,11 +158,14 @@ struct buffer_head {
 #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 inode {
                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 @@ struct inode_operations {
        int (*bmap) (struct inode *,int);
        void (*truncate) (struct inode *);
        int (*permission) (struct inode *, int);
+       int (*smap) (struct inode *,int);
 };
 
 struct super_operations {
@@ -333,8 +338,6 @@ struct file_system_type {
        struct file_system_type * next;
 };
 
-#ifdef __KERNEL__
-
 extern int register_filesystem(struct file_system_type *);
 extern int unregister_filesystem(struct file_system_type *);
 
index 0ff0e5dcf5fa3bf29e5d2c7f48ffca0b9d6067bc..cf5efa7dd18c5ea12a710fabbd768071f56c44e7 100644 (file)
@@ -117,22 +117,6 @@ struct iso_directory_record {
        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,6 +144,22 @@ extern int find_rock_ridge_relocation(struct iso_directory_record *, struct inod
 #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.  */
@@ -212,6 +212,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
 
 
index 9ddfde70b4ccf475a25622633e5ed2f82295917b..971a50d8a067d033df2f7f730fe1a338422ed6c4 100644 (file)
@@ -75,6 +75,8 @@ struct minix_dir_entry {
        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,
@@ -116,4 +118,6 @@ 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
index df1cb9a0aedd3d18b290dd28e808967695e77cfc..64554bfedd9bcbc34375f9ba6f20ab6c206149d9 100644 (file)
@@ -92,6 +92,8 @@ struct page_info {
 };
 /* end of planning stage */
 
+#ifdef __KERNEL__
+
 /*
  * Free area management
  */
@@ -221,21 +223,76 @@ extern unsigned short * mem_map;
 #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)
 {
-       swap_cache[addr >> PAGE_SHIFT] = 0;
+       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)
+{
+       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
index 756abaa33eb51a53a3f50121b0d52ee72329941b..8b9e633268783a4c2bc9e876ceeea18d5c7b51b3 100644 (file)
@@ -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 @@ struct fat_cache {
 
 #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_create(struct inode *dir,const char *name,int len,int mode,
 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 @@ extern int msdos_notify_change(int flags,struct inode *inode);
 /* 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
index 21eb99977f9685bd5745722e15be8c61c795a03c..427c449a94ef06c9e2f95c6ff13e1cd534a4ba87 100644 (file)
@@ -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
index 5d417765fa7634d9eea62c7a1af4797883a00a1d..fafd90f88d7f82e211286761861ef8f1be3a30df 100644 (file)
@@ -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 @@ struct        mtop {
 #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 @@ struct       mtget {
        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 @@ struct       mtget {
 #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 @@ struct      mtget {
 /* 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 @@ struct mt_tape_info {
        {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 @@ struct     mtpos {
 #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 @@ struct      mtpos {
 #define MT_ST_TWO_FM           0x10
 
 #endif /* _LINUX_MTIO_H */
-
index ad11988157f4313746037721a8841ba2f7c7ba8e..18f1dfd130c2b943fbef23fb261e1839b8e4311d 100644 (file)
@@ -54,7 +54,7 @@ typedef enum {
 
 #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 @@ struct net_proto {
 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 */
index 358456007326b1b9d1f58504221e7328de4841dd..94ce5e48a346f1ee4f40532dfe5af6b1025d2897 100644 (file)
@@ -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,
@@ -113,4 +115,6 @@ extern struct inode_operations nfs_symlink_inode_operations;
 extern int nfs_mmap(struct inode * inode, struct file * file,
                unsigned long addr, size_t len, int prot, unsigned long off);
 
+#endif /* __KERNEL__ */
+
 #endif
index a7b4a0ab0d11ccec17d188c9ebeb6ffe12190b6b..8c0b153e2f2d93471da55789e73f34ae888b077e 100644 (file)
@@ -352,6 +352,8 @@ struct task_struct {
 /* 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;
@@ -620,4 +622,6 @@ extern struct desc_struct default_ldt;
                        :"m" (current->debugreg[register]) \
                        :"dx");
 
+#endif /* __KERNEL__ */
+
 #endif
index aecd966e69c8ae8131c19414b3b3b65609024e76..c177ec1ebb9e665ae9f91a1ea2fbeb8c941d436d 100644 (file)
@@ -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
index 56f676de3c9ffcb5a09c7a6bd089fcb9daa5e6f7..6b998bf549147b37561279418d7f722db521ee9c 100644 (file)
@@ -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
 
 #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
 #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 (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_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_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_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 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! */
 
 
  */
 #endif
 
+#endif /* !CONFIG_QIC02_DYNCONF */
 
-/* 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 */
 
+/* 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)
 
-#define BLOCKS_BEYOND_EW       2       /* nr of blocks after Early Warning hole */
+/* status register bits (Active LOW!) */
+#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)
 
-#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)
+#define WT_QIC02_STAT_RESETMASK        0x07
+#define WT_QIC02_STAT_RESETVAL (WT_QIC02_STAT_RESETMASK & ~WT_QIC02_STAT_EXCEPTION)
 
-/* 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
+/* 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 
 
-#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)
+#define WT_CTL_DMA3            0x10                      /* enable dma chan3 */
+#define WT_CTL_DMA1            0x08             /* enable dma chan1 or chan2 */
 
-  /* 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 */
 
 
 
-/* "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.
  * 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.
  */
 
 
 
+
+/*
+ * 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
                           /* 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 @@ struct tpstatus {  /* sizeof(short)==2), LSB first */
 #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 @@ struct tpstatus { /* sizeof(short)==2), LSB first */
 #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 @@ typedef char flag;
 #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 */
 
-extern long tape_qic02_init(long);                       /* for kernel/mem.c */
+
+#define BLOCKS_BEYOND_EW       2       /* nr of blocks after Early Warning hole */
+#define BOGUS_IRQ              32009
+
+/* 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;
+};
 
 
-#endif /* CONFIG_TAPE_QIC02 */
+extern long qic02_tape_init(long);                       /* for mem.c */
+
+
+#endif /* CONFIG_QIC02_TAPE */
 
 #endif /* _LINUX_TPQIC02_H */
+
index 89740b008d00ac29b2c259fd3cab36f22a94b305..999df7484c31eb81c7e58d3a214776eb199cbff4 100644 (file)
@@ -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 void unblank_screen(void);
 extern int vt_ioctl(struct tty_struct *tty, struct file * file,
                    unsigned int cmd, unsigned long arg);
 
+#endif /* __KERNEL__ */
 #endif
diff --git a/include/linux/umsdos_fs.h b/include/linux/umsdos_fs.h
new file mode 100644 (file)
index 0000000..7bf8efe
--- /dev/null
@@ -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 --git a/include/linux/umsdos_fs.p b/include/linux/umsdos_fs.p
new file mode 100644 (file)
index 0000000..053541a
--- /dev/null
@@ -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 --git a/include/linux/umsdos_fs_i.h b/include/linux/umsdos_fs_i.h
new file mode 100644 (file)
index 0000000..44eab28
--- /dev/null
@@ -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
index dd457e118990882cfa2b8325f1e5473204f91804..65f98d0e1fc6452ef66c3946ebc3efb15887a6a2 100644 (file)
@@ -500,6 +500,22 @@ void init(void)
 
        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);
index b0101632c6d45fdbffcb6b0dfe661d16111b135f..6b2299e9b81e81132ec1e15f64c81abee1284c70 100644 (file)
 
 
 /* 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
 };
 
 
index 4fd8f9f539b914eb597dfa97618577a7cea25b4e..1d9a185ba01cea0c4d266e700f0917282fe7ae67 100644 (file)
@@ -269,10 +269,8 @@ int copy_page_tables(struct task_struct * tsk)
                        }
                        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 @@ void do_wp_page(struct vm_area_struct * vma, unsigned long address,
        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 @@ void do_wp_page(struct vm_area_struct * vma, unsigned long address,
                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 @@ good_area:
                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 @@ static int try_to_share(unsigned long to_address, struct vm_area_struct * to_are
                        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 @@ good_area:
                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 @@ good_area:
                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;
 
index 9f43fe25206ea8895b717d4b19c38f9ae4981341..7999c484a0a6a14932aed9e283ec69840b9f70ab 100644 (file)
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -21,24 +21,21 @@ static int anon_map(struct inode *, struct file *,
                    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 @@ int do_mmap(struct file * file, unsigned long addr, unsigned long len,
                        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 */
 
index e11b89d160f3c70d49cc0d9bff0050c0fd99afda..07326a25aebcfd813a9c8a56f15da34eee7d4006 100644 (file)
--- a/mm/swap.c
+++ b/mm/swap.c
@@ -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 @@ static struct swap_info_struct {
 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;
-
-extern inline void show_swap_cache_info (void)
+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)
 {
        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)
-{
-       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)
+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 @@ extern  inline int add_to_swap_cache (unsigned long addr, unsigned long entry)
                        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 @@ void rw_swap_page(int rw, unsigned long entry, char * buf)
        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))