diff -u --recursive --new-file v2.1.122/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v2.1.122/linux/Documentation/Configure.help	Thu Sep 17 17:53:34 1998
+++ linux/Documentation/Configure.help	Sun Sep 20 23:22:45 1998
@@ -2457,8 +2457,9 @@
   be inserted in and removed from the running kernel whenever you
   want). The module will be called unix.o. If you want to compile it
   as a module, say M here and read Documentation/modules.txt. If you
-  try building this as a module and you are running kerneld, be sure
-  to add 'alias net-pf-1 unix' to your /etc/conf.module file. 
+  try building this as a module and you have said Y to "Kernel module
+  loader support" above, be sure to add 'alias net-pf-1 unix' to your
+  /etc/conf.modules file.
 
   If unsure, say Y.
 
@@ -7033,8 +7034,8 @@
   Documentation/modules.txt. The module will be called serial.o.
   [WARNING: Do not compile this driver as a module if you are using
   non-standard serial ports, since the configuration information will
-  be lost when kerneld automatically unloads the driver. This
-  limitation may be lifted in the future.] 
+  be lost if you unload the driver. This limitation may be lifted in
+  the future.]
 
   BTW1: If you have a mouseman serial mouse which is not recognized by
   the X window system, try running gpm first. 
diff -u --recursive --new-file v2.1.122/linux/Documentation/sound/AWE32 linux/Documentation/sound/AWE32
--- v2.1.122/linux/Documentation/sound/AWE32	Sun Jul 26 11:57:14 1998
+++ linux/Documentation/sound/AWE32	Fri Sep 18 22:33:55 1998
@@ -51,7 +51,7 @@
 but the wavetable needs THREE! My working string is:
 
 "(CONFIGURE CTL044/1132685 (LD 2 (IO 0 (BASE 0x0620)) (IO 1 (BASE 0x0A20)) 
-(IO 3 (BASE 0x0E20)) (ACT Y) ))"
+(IO 2 (BASE 0x0E20)) (ACT Y) ))"
 
 Resources 0x0620, 0x0A20 and 0x0E20 should work. Other on-board devices: 
 Gameport and StereoEnhance are not required to be inited.
diff -u --recursive --new-file v2.1.122/linux/MAINTAINERS linux/MAINTAINERS
--- v2.1.122/linux/MAINTAINERS	Thu Sep 17 17:53:34 1998
+++ linux/MAINTAINERS	Tue Sep 22 23:31:18 1998
@@ -331,13 +331,7 @@
 P:	Paul Russell
 M:	Paul.Russell@rustcorp.com.au
 W:	http://www.adelaide.net.au/~rustcorp/ipfwchains/ipfwchains.html
-S:	Maintained
-
-IP FIREWALL
-P:	Paul Russell
-M:	Paul.Russell@rustcorp.com.au
-W:	http://www.adelaide.net.au/~rustcorp/ipfwchains/ipfwchains.html
-S:	Maintained
+S:	Supported
 
 IPX/SPX NETWORK LAYER
 P:	Jay Schulist
diff -u --recursive --new-file v2.1.122/linux/Makefile linux/Makefile
--- v2.1.122/linux/Makefile	Thu Sep 17 17:53:34 1998
+++ linux/Makefile	Sun Sep 20 11:38:59 1998
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 1
-SUBLEVEL = 122
+SUBLEVEL = 123
 
 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
 
diff -u --recursive --new-file v2.1.122/linux/arch/i386/defconfig linux/arch/i386/defconfig
--- v2.1.122/linux/arch/i386/defconfig	Wed Sep  9 14:51:05 1998
+++ linux/arch/i386/defconfig	Sat Sep 26 23:22:31 1998
@@ -40,6 +40,7 @@
 CONFIG_BINFMT_ELF=y
 CONFIG_BINFMT_MISC=y
 # CONFIG_PARPORT is not set
+# CONFIG_APM is not set
 
 #
 # Plug and Play support
@@ -236,9 +237,7 @@
 CONFIG_PSMOUSE=y
 CONFIG_82C710_MOUSE=y
 # CONFIG_PC110_PAD is not set
-# CONFIG_UMISC is not set
 # CONFIG_QIC02_TAPE is not set
-# CONFIG_APM is not set
 # CONFIG_WATCHDOG is not set
 # CONFIG_RTC is not set
 # CONFIG_VIDEO_DEV is not set
@@ -277,6 +276,9 @@
 # CONFIG_ROMFS_FS is not set
 CONFIG_AUTOFS_FS=y
 # CONFIG_UFS_FS is not set
+# CONFIG_BSD_DISKLABEL is not set
+# CONFIG_SMD_DISKLABEL is not set
+# CONFIG_SOLARIS_X86_PARTITION is not set
 CONFIG_DEVPTS_FS=y
 # CONFIG_MAC_PARTITION is not set
 # CONFIG_NLS is not set
diff -u --recursive --new-file v2.1.122/linux/arch/i386/kernel/bios32.c linux/arch/i386/kernel/bios32.c
--- v2.1.122/linux/arch/i386/kernel/bios32.c	Wed Sep  9 14:51:05 1998
+++ linux/arch/i386/kernel/bios32.c	Sat Sep 26 10:34:49 1998
@@ -1,7 +1,7 @@
 /*
  * bios32.c - Low-Level PCI Access
  *
- * $Id: bios32.c,v 1.45 1998/08/15 10:41:04 mj Exp $
+ * $Id: bios32.c,v 1.48 1998/09/26 08:06:55 mj Exp $
  *
  * Copyright 1993, 1994 Drew Eckhardt
  *      Visionary Computing
@@ -170,6 +170,7 @@
 #define PCI_PROBE_CONF2 4
 #define PCI_NO_SORT 0x100
 #define PCI_BIOS_SORT 0x200
+#define PCI_NO_CHECKS 0x400
 
 static unsigned int pci_probe = PCI_PROBE_BIOS | PCI_PROBE_CONF1 | PCI_PROBE_CONF2;
 
@@ -343,14 +344,21 @@
  * whether bus 00 contains a host bridge (this is similar to checking
  * techniques used in XFree86, but ours should be more reliable since we
  * attempt to make use of direct access hints provided by the PCI BIOS).
+ *
+ * This should be close to trivial, but it isn't, because there are buggy
+ * chipsets (yes, you guessed it, by Intel) that have no class ID.
  */
 __initfunc(int pci_sanity_check(struct pci_access *a))
 {
-	u16 dfn, class;
+	u16 dfn, x;
 
+	if (pci_probe & PCI_NO_CHECKS)
+		return 1;
 	for(dfn=0; dfn < 0x100; dfn++)
-		if (!a->read_config_word(0, dfn, PCI_CLASS_DEVICE, &class) &&
-		    class == PCI_CLASS_BRIDGE_HOST)
+		if ((!a->read_config_word(0, dfn, PCI_CLASS_DEVICE, &x) &&
+		     x == PCI_CLASS_BRIDGE_HOST) ||
+		    (!a->read_config_word(0, dfn, PCI_VENDOR_ID, &x) &&
+		     x == PCI_VENDOR_ID_INTEL))
 			return 1;
 	DBG("PCI: Sanity check failed\n");
 	return 0;
@@ -945,7 +953,7 @@
 __initfunc(void pcibios_fixup_peer_bridges(void))
 {
 	struct pci_bus *b = &pci_root;
-	int i, cnt=-1;
+	int i, n, cnt=-1;
 	struct pci_dev *d;
 
 #ifdef CONFIG_PCI_DIRECT
@@ -960,8 +968,8 @@
 	for(d=b->devices; d; d=d->sibling)
 		if ((d->class >> 8) == PCI_CLASS_BRIDGE_HOST)
 			cnt++;
-	do {
-		int n = b->subordinate+1;
+	n = b->subordinate + 1;
+	while (n <= 0xff) {
 		int found = 0;
 		u16 l;
 		for(i=0; i<256; i += 8)
@@ -973,8 +981,9 @@
 				    l == PCI_CLASS_BRIDGE_HOST)
 					cnt++;
 			}
-		if (found && cnt > 0) {
-			cnt--;
+		if (cnt-- <= 0)
+			break;
+		if (found) {
 			printk("PCI: Discovered primary peer bus %02x\n", n);
 			b = kmalloc(sizeof(*b), GFP_KERNEL);
 			memset(b, 0, sizeof(*b));
@@ -983,9 +992,10 @@
 			b->number = b->secondary = n;
 			b->subordinate = 0xff;
 			b->subordinate = pci_scan_bus(b);
-			break;
+			n = b->subordinate;
 		}
-	} while (i < 256);
+		n++;
+	}
 }
 
 /*
@@ -1146,11 +1156,11 @@
 #endif
 #ifdef CONFIG_PCI_DIRECT
 	else if (!strcmp(str, "conf1")) {
-		pci_probe = PCI_PROBE_CONF1;
+		pci_probe = PCI_PROBE_CONF1 | PCI_NO_CHECKS;
 		return NULL;
 	}
 	else if (!strcmp(str, "conf2")) {
-		pci_probe = PCI_PROBE_CONF2;
+		pci_probe = PCI_PROBE_CONF2 | PCI_NO_CHECKS;
 		return NULL;
 	}
 #endif
diff -u --recursive --new-file v2.1.122/linux/arch/i386/kernel/io_apic.c linux/arch/i386/kernel/io_apic.c
--- v2.1.122/linux/arch/i386/kernel/io_apic.c	Thu Sep 17 17:53:34 1998
+++ linux/arch/i386/kernel/io_apic.c	Sun Sep 27 19:20:20 1998
@@ -291,6 +291,43 @@
 }
 
 /*
+ * ISA Edge/Level control register, ELCR
+ */
+static int __init ISA_ELCR(unsigned int irq)
+{
+	if (irq < 16) {
+		unsigned int port = 0x4d0 + (irq >> 3);
+		return (inb(port) >> (irq & 7)) & 1;
+	}
+	printk("Broken MPtable reports ISA irq %d\n", irq);
+	return 0;
+}	
+
+/*
+ * ISA interrupts can be:
+ *  - level triggered, active low (ELCR = 1)
+ *  - edge triggered, active high (ELCR = 0)
+ *  - edge triggered, active low (magic irq 8)
+ */
+static int __init default_ISA_trigger(int idx)
+{
+	unsigned int irq = mp_irqs[idx].mpc_dstirq;
+
+	if (irq == 8)
+		return 0;
+	return ISA_ELCR(irq);
+}
+
+static int __init default_ISA_polarity(int idx)
+{
+	unsigned int irq = mp_irqs[idx].mpc_dstirq;
+
+	if (irq == 8)
+		return 1;
+	return ISA_ELCR(irq);
+}
+
+/*
  * There are broken mptables which register ISA+high-active+level IRQs,
  * these are illegal and are converted here to ISA+high-active+edge
  * IRQ sources. Careful, ISA+low-active+level is another broken entry
@@ -314,7 +351,7 @@
 			{
 				case MP_BUS_ISA: /* ISA pin */
 				{
-					polarity = 0;
+					polarity = default_ISA_polarity(idx);
 					break;
 				}
 				case MP_BUS_PCI: /* PCI pin */
@@ -371,9 +408,9 @@
 		{
 			switch (mp_bus_id_to_type[bus])
 			{
-				case MP_BUS_ISA: /* ISA pin, edge */
+				case MP_BUS_ISA:
 				{
-					trigger = 0;
+					trigger = default_ISA_trigger(idx);
 					break;
 				}
 				case MP_BUS_PCI: /* PCI pin, level */
@@ -549,7 +586,7 @@
 		entry.delivery_mode = dest_LowestPrio;
 		entry.dest_mode = 1;			/* logical delivery */
 		entry.mask = 0;				/* enable IRQ */
-		entry.dest.logical.logical_dest = 0xff;	/* all CPUs */
+		entry.dest.logical.logical_dest = 0;	/* but no route */
 
 		idx = find_irq_entry(pin,mp_INT);
 		if (idx == -1) {
@@ -564,6 +601,12 @@
 		entry.trigger = irq_trigger(idx);
 		entry.polarity = irq_polarity(idx);
 
+		if (irq_trigger(idx)) {
+			entry.trigger = 1;
+			entry.mask = 1;
+			entry.dest.logical.logical_dest = 0xff;
+		}
+
 		irq = pin_2_irq(idx,pin);
 		irq_2_pin[irq] = pin;
 
@@ -577,37 +620,14 @@
 		if (trigger_flag_broken (idx))
 			printk("broken BIOS, changing pin %d to edge\n", pin);
 
-		io_apic_write(0x10+2*pin, *(((int *)&entry)+0));
 		io_apic_write(0x11+2*pin, *(((int *)&entry)+1));
+		io_apic_write(0x10+2*pin, *(((int *)&entry)+0));
 	}
 
 	if (!first_notcon)
 		printk(" not connected.\n");
 }
 
-void __init setup_IO_APIC_irq_ISA_default(unsigned int irq)
-{
-	struct IO_APIC_route_entry entry;
-
-	/*
-	 * add it to the IO-APIC irq-routing table:
-	 */
-	memset(&entry,0,sizeof(entry));
-
-	entry.delivery_mode = dest_LowestPrio;		/* lowest prio */
-	entry.dest_mode = 1;				/* logical delivery */
-	entry.mask = 0;					/* unmask IRQ now */
-	entry.dest.logical.logical_dest = 0xff;		/* all CPUs */
-
-	entry.vector = assign_irq_vector(irq);
-
-	entry.polarity = 0;
-	entry.trigger = 0;
-
-	io_apic_write(0x10+2*irq, *(((int *)&entry)+0));
-	io_apic_write(0x11+2*irq, *(((int *)&entry)+1));
-}
-
 /*
  * Set up a certain pin as ExtINT delivered interrupt
  */
@@ -623,7 +643,7 @@
 	entry.delivery_mode = dest_ExtINT;
 	entry.dest_mode = 1;				/* logical delivery */
 	entry.mask = 0;					/* unmask IRQ now */
-	entry.dest.logical.logical_dest = 0x01;		/* all CPUs */
+	entry.dest.logical.logical_dest = 0x01;		/* logical CPU #0 */
 
 	entry.vector = 0;				/* it's ignored */
 
@@ -1141,8 +1161,9 @@
 
 	pin1 = find_timer_pin(mp_INT);
 	pin2 = find_timer_pin(mp_ExtINT);
-
+	enable_IO_APIC_irq(0);
 	if (!timer_irq_works()) {
+
 		if (pin1 != -1)
 			printk("..MP-BIOS bug: 8254 timer not connected to IO-APIC\n");
 		printk("...trying to set up timer as ExtINT... ");
diff -u --recursive --new-file v2.1.122/linux/arch/i386/kernel/process.c linux/arch/i386/kernel/process.c
--- v2.1.122/linux/arch/i386/kernel/process.c	Thu Sep 17 17:53:34 1998
+++ linux/arch/i386/kernel/process.c	Thu Sep 17 17:41:51 1998
@@ -540,10 +540,10 @@
 static inline void unlazy_fpu(struct task_struct *tsk)
 {
 	if (tsk->flags & PF_USEDFPU) {
-		tsk->flags &= ~PF_USEDFPU;
 		__asm__("fnsave %0":"=m" (tsk->tss.i387));
-		stts();
 		asm volatile("fwait");
+		tsk->flags &= ~PF_USEDFPU;
+		stts();
 	}
 }
 
@@ -737,8 +737,11 @@
 		asm volatile("lldt %0": :"g" (*(unsigned short *)&next->tss.ldt));
 
 	/* Re-load page tables */
-	if (next->tss.cr3 != prev->tss.cr3)
-		asm volatile("movl %0,%%cr3": :"r" (next->tss.cr3));
+	{
+		unsigned long new_cr3 = next->tss.cr3;
+		if (new_cr3 != prev->tss.cr3) 
+			asm volatile("movl %0,%%cr3": :"r" (new_cr3));
+	}
 
 	/*
 	 * Restore %fs and %gs.
diff -u --recursive --new-file v2.1.122/linux/drivers/block/genhd.c linux/drivers/block/genhd.c
--- v2.1.122/linux/drivers/block/genhd.c	Thu Sep 17 17:53:35 1998
+++ linux/drivers/block/genhd.c	Tue Sep 22 16:41:05 1998
@@ -426,8 +426,7 @@
 				   && (q->sector & 63) == 1
 				   && (q->end_sector & 63) == 63) {
 					unsigned int heads = q->end_head + 1;
-					if (heads == 15 || heads == 16 ||
-					    heads == 32 || heads == 64 ||
+					if (heads == 32 || heads == 64 ||
 					    heads == 128 || heads == 240 ||
 					    heads == 255) {
 						(void) ide_xlate_1024(dev, heads, " [PTBL]");
diff -u --recursive --new-file v2.1.122/linux/drivers/char/Config.in linux/drivers/char/Config.in
--- v2.1.122/linux/drivers/char/Config.in	Thu Sep 17 17:53:35 1998
+++ linux/drivers/char/Config.in	Sat Sep 26 23:21:34 1998
@@ -57,14 +57,8 @@
 	tristate 'Logitech busmouse support' CONFIG_BUSMOUSE
 	tristate 'Microsoft busmouse support' CONFIG_MS_BUSMOUSE
 	tristate 'PS/2 mouse (aka "auxiliary device") support' CONFIG_PSMOUSE
-	if [ "$CONFIG_PSMOUSE" != "n" ]; then
-	  bool 'C&T 82C710 mouse port support (as on TI Travelmate)' CONFIG_82C710_MOUSE
-	fi
+	tristate 'C&T 82C710 mouse port support (as on TI Travelmate)' CONFIG_82C710_MOUSE
 	tristate 'PC110 digitizer pad support' CONFIG_PC110_PAD
-fi
-
-if [ "$CONFIG_MODULES" = "y" ]; then
-	bool 'Support for user misc device modules' CONFIG_UMISC
 fi
 
 tristate 'QIC-02 tape support' CONFIG_QIC02_TAPE
diff -u --recursive --new-file v2.1.122/linux/drivers/char/Makefile linux/drivers/char/Makefile
--- v2.1.122/linux/drivers/char/Makefile	Thu Sep 17 17:53:35 1998
+++ linux/drivers/char/Makefile	Sat Sep 26 23:28:08 1998
@@ -21,7 +21,7 @@
 L_TARGET := char.a
 M_OBJS   :=
 L_OBJS   := tty_io.o n_tty.o tty_ioctl.o mem.o random.o
-LX_OBJS  := pty.o
+LX_OBJS  := pty.o misc.o
 
 ifdef CONFIG_VT
 L_OBJS += vt.o vc_screen.o consolemap.o consolemap_deftbl.o
@@ -136,22 +136,18 @@
 endif
 
 ifeq ($(CONFIG_ATIXL_BUSMOUSE),y)
-M = y
 L_OBJS += atixlmouse.o
 else
   ifeq ($(CONFIG_ATIXL_BUSMOUSE),m)
   M_OBJS += atixlmouse.o
-  MM = m
   endif
 endif
 
 ifeq ($(CONFIG_BUSMOUSE),y)
-M = y
 L_OBJS += busmouse.o
 else
   ifeq ($(CONFIG_BUSMOUSE),m)
   M_OBJS += busmouse.o
-  MM = m
   endif
 endif
 
@@ -172,135 +168,109 @@
 endif
 
 ifeq ($(CONFIG_MS_BUSMOUSE),y)
-M = y
 L_OBJS += msbusmouse.o
 else
   ifeq ($(CONFIG_MS_BUSMOUSE),m)
   M_OBJS += msbusmouse.o
-  MM = m
+  endif
+endif
+
+ifeq ($(CONFIG_82C710_MOUSE),y)
+L_OBJS += qpmouse.o
+else
+  ifeq ($(CONFIG_82C710_MOUSE),m)
+  M_OBJS += qpmouse.o
   endif
 endif
 
 ifeq ($(CONFIG_PSMOUSE),y)
-M = y
 L_OBJS += psaux.o
 else
   ifeq ($(CONFIG_PSMOUSE),m)
   M_OBJS += psaux.o
-  MM = m
   endif
 endif
 
-ifeq ($(CONFIG_UMISC),y)
-# To support third-party modules, misc.c must reside in the kernel
-M = y
-endif
-
 ifeq ($(CONFIG_SOFT_WATCHDOG),y)
-M = y
 L_OBJS += softdog.o
 else
   ifeq ($(CONFIG_SOFT_WATCHDOG),m)
   M_OBJS += softdog.o
-  MM = m
   endif   
 endif
 
 ifeq ($(CONFIG_PCWATCHDOG),y)
-M = y
 L_OBJS += pcwd.o
 else
   ifeq ($(CONFIG_PCWATCHDOG),m)
   M_OBJS += pcwd.o
-  MM = m
   endif
 endif
 
 ifeq ($(CONFIG_ACQUIRE_WDT),y)
-M = y
 L_OBJS += acquirewdt.o
 else
   ifeq ($(CONFIG_ACQUIRE_WDT),m)
   M_OBJS += acquirewdt.o
-  MM = m
   endif
 endif
 
 ifeq ($(CONFIG_AMIGAMOUSE),y)
-M = y
 L_OBJS += amigamouse.o
 else
   ifeq ($(CONFIG_AMIGAMOUSE),m)
     M_OBJS += amigamouse.o
-    MM = m
   endif
 endif
 
 ifeq ($(CONFIG_ATARIMOUSE),y)
-M = y
 L_OBJS += atarimouse.o
 else
   ifeq ($(CONFIG_ATARIMOUSE),m)
     M_OBJS += atarimouse.o
-    MM = m
   endif
 endif
 
 ifeq ($(CONFIG_MACMOUSE),y)
-M = y
 L_OBJS += macmouse.o
 else
   ifeq ($(CONFIG_MACMOUSE),m)
     M_OBJS += macmouse.o
-    MM = m
   endif
 endif
 
-ifdef CONFIG_SUN_MOUSE
-M = y
-endif
-
 ifeq ($(CONFIG_PC110_PAD),y)
-M = y
 L_OBJS += pc110pad.o
 else
   ifeq ($(CONFIG_PC110_PAD),m)
     M_OBJS += pc110pad.o
-    MM = m
   endif
 endif
 
 ifeq ($(CONFIG_SUN_OPENPROMIO),y)
-M = y
 else
   ifeq ($(CONFIG_SUN_OPENPROMIO),m)
-  MM = m
   endif
 endif
 
 ifeq ($(CONFIG_WDT),y)
-M = y
 L_OBJS += wdt.o
 else
   ifeq ($(CONFIG_WDT),m)
     M_OBJS += wdt.o
-    MM = m
   endif
 endif
 
 ifeq ($(CONFIG_RTC),y)
-M = y
 L_OBJS += rtc.o
 endif
 
 ifeq ($(CONFIG_NVRAM),y)
-M = y
   ifeq ($(CONFIG_PMAC)$(CONFIG_CHRP),)
   L_OBJS += nvram.o
   endif
 else
   ifeq ($(CONFIG_NVRAM),m)
-    MM = m
     ifeq ($(CONFIG_PMAC)$(CONFIG_CHRP),)
     M_OBJS += nvram.o
     endif
@@ -437,19 +407,6 @@
 
 ifdef CONFIG_H8
 LX_OBJS += h8.o
-M = y
-endif
-
-ifdef CONFIG_APM
-M = y
-endif
-
-ifdef M
-LX_OBJS += misc.o
-else
-  ifdef MM
-  MX_OBJS += misc.o
-  endif
 endif
 
 ifeq ($(L_I2C),y)
diff -u --recursive --new-file v2.1.122/linux/drivers/char/console.c linux/drivers/char/console.c
--- v2.1.122/linux/drivers/char/console.c	Thu Aug  6 14:06:31 1998
+++ linux/drivers/char/console.c	Thu Sep 17 09:42:28 1998
@@ -47,7 +47,6 @@
  *     - video_num_columns
  *     - video_num_lines
  *     - video_size_row
- *     - video_screen_size
  *     - can_do_color
  *
  *   The abstract console driver provides a generic interface for a text
@@ -108,8 +107,6 @@
 
 struct consw *conswitchp = NULL;
 
-static int vesa_blank_mode = 0; /* 0:none 1:suspendV 2:suspendH 3:powerdown */
-
 /* A bitmap for codes <32. A bit of 1 indicates that the code
  * corresponding to that bit number invokes some special action
  * (such as cursor movement) and should not be displayed as a
@@ -133,21 +130,26 @@
 static struct termios *console_termios_locked[MAX_NR_CONSOLES];
 struct vc vc_cons [MAX_NR_CONSOLES];
 
+static struct consw *con_driver_map[MAX_NR_CONSOLES];
+
 static int con_open(struct tty_struct *, struct file *);
 static void vc_init(unsigned int console, unsigned int rows,
 		    unsigned int cols, int do_clear);
 static void blank_screen(void);
-static void unblank_screen(void);
 static void gotoxy(int currcons, int new_x, int new_y);
 static void save_cur(int currcons);
 static void reset_terminal(int currcons, int do_clear);
 static void con_flush_chars(struct tty_struct *tty);
+static void set_vesa_blanking(unsigned long arg);
+static void set_cursor(int currcons);
+static void hide_cursor(int currcons);
 
 static int printable = 0;		/* Is console ready for printing? */
 
 int do_poke_blanked_console = 0;
 int console_blanked = 0;
 
+static int vesa_blank_mode = 0; /* 0:none 1:suspendV 2:suspendH 3:powerdown */
 static int blankinterval = 10*60*HZ;
 static int vesa_off_interval = 0;
 
@@ -179,17 +181,16 @@
 DECLARE_TASK_QUEUE(con_task_queue);
 
 /*
+ * For the same reason, we defer scrollback to the console_bh.
+ */
+static int scrollback_delta = 0;
+
+/*
  *	Low-Level Functions
  */
 
 #define IS_FG (currcons == fg_console)
-#define IS_VISIBLE (*display_fg == vc_cons[currcons].d)
-
-#ifdef VT_BUF_VRAM_ONLY
-#define DO_UPDATE 0
-#else
-#define DO_UPDATE IS_VISIBLE
-#endif
+#define IS_VISIBLE CON_IS_VISIBLE(vc_cons[currcons].d)
 
 static inline unsigned short *screenpos(int currcons, int offset, int viewed)
 {
@@ -197,13 +198,10 @@
 	return p;
 }
 
-static void scrolldelta(int lines)
+static inline void scrolldelta(int lines)
 {
-	int currcons = fg_console;
-
-	clear_selection();
-	if (vcmode == KD_TEXT)
-		sw->con_scrolldelta(vc_cons[currcons].d, lines);
+	scrollback_delta += lines;
+	mark_bh(CONSOLE_BH);
 }
 
 static void scrup(int currcons, unsigned int t, unsigned int b, int nr)
@@ -242,7 +240,6 @@
 
 static void do_update_region(int currcons, unsigned long start, int count)
 {
-#ifndef VT_BUF_VRAM_ONLY
 	unsigned int xx, yy, offset;
 	u16 *p;
 
@@ -279,13 +276,15 @@
 		xx = 0;
 		yy++;
 	}
-#endif
 }
 
 void update_region(int currcons, unsigned long start, int count)
 {
-	if (DO_UPDATE)
+	if (IS_VISIBLE) {
+		hide_cursor(currcons);
 		do_update_region(currcons, start, count);
+		set_cursor(currcons);
+	}
 }
 
 /* Structure of attributes is hardware-dependent */
@@ -295,7 +294,6 @@
 	if (sw->con_build_attr)
 		return sw->con_build_attr(vc_cons[currcons].d, _color, _intensity, _blink, _underline, _reverse);
 
-#ifndef VT_BUF_VRAM_ONLY
 /*
  * ++roman: I completely changed the attribute format for monochrome
  * mode (!can_do_color). The formerly used MDA (monochrome display
@@ -327,9 +325,6 @@
 		a <<= 1;
 	return a;
 	}
-#else
-	return 0;
-#endif
 }
 
 static void update_attr(int currcons)
@@ -348,7 +343,6 @@
 	p = screenpos(currcons, offset, viewed);
 	if (sw->con_invert_region)
 		sw->con_invert_region(vc_cons[currcons].d, p, count);
-#ifndef VT_BUF_VRAM_ONLY
 	else {
 		u16 *q = p;
 		int cnt = count;
@@ -369,8 +363,8 @@
 			}
 		}
 	}
-#endif
-	update_region(currcons, (unsigned long) p, count);
+	if (IS_VISIBLE)
+		do_update_region(currcons, (unsigned long) p, count);
 }
 
 /* used by selection: complement pointer position */
@@ -382,7 +376,7 @@
 
 	if (p) {
 		scr_writew(old, p);
-		if (DO_UPDATE)
+		if (IS_VISIBLE)
 			sw->con_putc(vc_cons[currcons].d, old, oldy, oldx);
 	}
 	if (offset == -1)
@@ -393,7 +387,7 @@
 		old = scr_readw(p);
 		new = old ^ complement_mask;
 		scr_writew(new, p);
-		if (DO_UPDATE) {
+		if (IS_VISIBLE) {
 			oldx = (offset >> 1) % video_num_columns;
 			oldy = (offset >> 1) / video_num_columns;
 			sw->con_putc(vc_cons[currcons].d, new, oldy, oldx);
@@ -410,7 +404,7 @@
 		scr_writew(scr_readw(p), p + nr);
 	scr_memsetw(q, video_erase_char, nr*2);
 	need_wrap = 0;
-	if (DO_UPDATE) {
+	if (IS_VISIBLE) {
 		unsigned short oldattr = attr;
 		sw->con_bmove(vc_cons[currcons].d,y,x,y,x+nr,1,
 			      video_num_columns-x-nr);
@@ -433,7 +427,7 @@
 	}
 	scr_memsetw(p, video_erase_char, nr*2);
 	need_wrap = 0;
-	if (DO_UPDATE) {
+	if (IS_VISIBLE) {
 		unsigned short oldattr = attr;
 		sw->con_bmove(vc_cons[currcons].d, y, x+nr, y, x, 1,
 			      video_num_columns-x-nr);
@@ -461,7 +455,7 @@
 	if ((type & 0x20) && ((softcursor_original & 0x7000) == (i & 0x7000))) i ^= 0x7000;
 	if ((type & 0x40) && ((i & 0x700) == ((i & 0x7000) >> 4))) i ^= 0x0700;
 	scr_writew(i, (u16 *) pos);
-	if (DO_UPDATE)
+	if (IS_VISIBLE)
 		sw->con_putc(vc_cons[currcons].d, i, y, x);
 }
 
@@ -471,14 +465,14 @@
 		clear_selection();
 	if (softcursor_original != -1) {
 		scr_writew(softcursor_original,(u16 *) pos);
-		if (DO_UPDATE)
+		if (IS_VISIBLE)
 			sw->con_putc(vc_cons[currcons].d, softcursor_original, y, x);
 		softcursor_original = -1;
 	}
 	sw->con_cursor(vc_cons[currcons].d,CM_ERASE);
 }
 
-void set_cursor(int currcons)
+static void set_cursor(int currcons)
 {
     if (!IS_FG || console_blanked || vcmode == KD_GRAPHICS)
 	return;
@@ -503,9 +497,8 @@
 	pos = origin + video_size_row*y + 2*x;
 }
 
-static inline void save_screen(void)
+static inline void save_screen(int currcons)
 {
-	int currcons = fg_console;
 	if (sw->con_save_screen)
 		sw->con_save_screen(vc_cons[currcons].d);
 }
@@ -514,45 +507,55 @@
  *	Redrawing of screen
  */
 
-void update_screen(int new_console)
+void redraw_screen(int new_console, int is_switch)
 {
-	int currcons = fg_console;
 	int redraw = 1;
-	int old_console;
+	int currcons, old_console;
 	static int lock = 0;
-	struct vc_data **display;
 
 	if (lock)
 		return;
 	if (!vc_cons_allocated(new_console)) {
 		/* strange ... */
-		printk("update_screen: tty %d not allocated ??\n", new_console+1);
+		printk("redraw_screen: tty %d not allocated ??\n", new_console+1);
 		return;
 	}
 	lock = 1;
 
-	hide_cursor(currcons);
-	if (fg_console != new_console) {
-		display = vc_cons[new_console].d->vc_display_fg;
-		old_console = (*display) ? (*display)->vc_num : fg_console;
-		*display = vc_cons[new_console].d;
-		fg_console = new_console;
-		currcons = old_console;
-		if (!IS_VISIBLE)
-			set_origin(currcons);
+	if (is_switch) {
+		currcons = fg_console;
+		hide_cursor(currcons);
+		if (fg_console != new_console) {
+			struct vc_data **display = vc_cons[new_console].d->vc_display_fg;
+			old_console = (*display) ? (*display)->vc_num : fg_console;
+			*display = vc_cons[new_console].d;
+			fg_console = new_console;
+			currcons = old_console;
+			if (!IS_VISIBLE) {
+				save_screen(currcons);
+				set_origin(currcons);
+			}
+			currcons = new_console;
+			if (old_console == new_console)
+				redraw = 0;
+		}
+	} else {
 		currcons = new_console;
-		if (old_console == new_console)
-			redraw = 0;
+		hide_cursor(currcons);
 	}
+
 	if (redraw) {
 		set_origin(currcons);
-		if (sw->con_switch(vc_cons[currcons].d))
+		set_palette(currcons);
+		if (sw->con_switch(vc_cons[currcons].d) && vcmode != KD_GRAPHICS)
 			/* Update the screen contents */
 			do_update_region(currcons, origin, screenbuf_size/2);
 	}
 	set_cursor(currcons);
-	set_leds();
-	compute_shiftstate();
+	if (is_switch) {
+		set_leds();
+		compute_shiftstate();
+	}
 	lock = 0;
 }
 
@@ -565,27 +568,31 @@
 	return (i < MAX_NR_CONSOLES && vc_cons[i].d);
 }
 
-void visual_init(int currcons)
+static void visual_init(int currcons, int init)
 {
     /* ++Geert: sw->con_init determines console size */
     sw = conswitchp;
+    if (con_driver_map[currcons])
+	sw = con_driver_map[currcons];
     cons_num = currcons;
     display_fg = &master_display_fg;
     vc_cons[currcons].d->vc_uni_pagedir_loc = &vc_cons[currcons].d->vc_uni_pagedir;
     vc_cons[currcons].d->vc_uni_pagedir = 0;
     hi_font_mask = 0;
     complement_mask = 0;
-    sw->con_init(vc_cons[currcons].d, 1);
+    can_do_color = 0;
+    sw->con_init(vc_cons[currcons].d, init);
     if (!complement_mask)
         complement_mask = can_do_color ? 0x7700 : 0x0800;
+    s_complement_mask = complement_mask;
     video_size_row = video_num_columns<<1;
-    video_screen_size = video_num_lines*video_size_row;
+    screenbuf_size = video_num_lines*video_size_row;
 }
 
-int vc_allocate(unsigned int currcons, int init)	/* return 0 on success */
+int vc_allocate(unsigned int currcons)	/* return 0 on success */
 {
 	if (currcons >= MAX_NR_CONSOLES)
-	  return -ENXIO;
+		return -ENXIO;
 	if (!vc_cons[currcons].d) {
 	    long p, q;
 
@@ -595,7 +602,7 @@
 
 	    /* due to the granularity of kmalloc, we waste some memory here */
 	    /* the alloc is done in two steps, to optimize the common situation
-	       of a 25x80 console (structsize=216, video_screen_size=4000) */
+	       of a 25x80 console (structsize=216, screenbuf_size=4000) */
 	    /* although the numbers above are not valid since long ago, the
 	       point is still up-to-date and the comment still has its value
 	       even if only as a historical artifact.  --mj, July 1998 */
@@ -604,22 +611,19 @@
 		return -ENOMEM;
 	    vc_cons[currcons].d = (struct vc_data *)p;
 	    vt_cons[currcons] = (struct vt_struct *)(p+sizeof(struct vc_data));
-	    visual_init(currcons);
-	    q = (long)kmalloc(video_screen_size, GFP_KERNEL);
+	    visual_init(currcons, 1);
+	    if (!*vc_cons[currcons].d->vc_uni_pagedir_loc)
+		con_set_default_unimap(currcons);
+	    q = (long)kmalloc(screenbuf_size, GFP_KERNEL);
 	    if (!q) {
 		kfree_s((char *) p, structsize);
 		vc_cons[currcons].d = NULL;
 		vt_cons[currcons] = NULL;
 		return -ENOMEM;
 	    }
-	    con_set_default_unimap(currcons);
 	    screenbuf = (unsigned short *) q;
 	    kmalloced = 1;
-	    screenbuf_size = video_screen_size;
-	    if (!sw->con_save_screen)
-	    	init = 0; /* If console does not have save_screen routine,
-	    		     we should clear the screen */
-	    vc_init(currcons, video_num_lines, video_num_columns, !init);
+	    vc_init(currcons, video_num_lines, video_num_columns, 1);
 	}
 	return 0;
 }
@@ -669,12 +673,12 @@
 		oll = video_num_lines;
 		occ = video_num_columns;
 		osr = video_size_row;
-		oss = video_screen_size;
+		oss = screenbuf_size;
 
 		video_num_lines = ll;
 		video_num_columns = cc;
 		video_size_row = sr;
-		video_screen_size = ss;
+		screenbuf_size = ss;
 
 		rlth = MIN(osr, sr);
 		rrem = sr - rlth;
@@ -719,11 +723,10 @@
 			*cws = ws;
 		}
 
-		if (IS_FG && vt_cons[fg_console]->vc_mode == KD_TEXT)
-			update_screen(fg_console);
+		if (IS_VISIBLE)
+			update_screen(currcons);
 	}
 
-	set_cursor(fg_console);
 	return 0;
 }
 
@@ -884,7 +887,7 @@
 		case 0:	/* erase from cursor to end of display */
 			count = (scr_end-pos)>>1;
 			start = (unsigned short *) pos;
-			if (DO_UPDATE) {
+			if (IS_VISIBLE) {
 				/* do in two stages */
 				sw->con_clear(vc_cons[currcons].d, y, x, 1,
 					      video_num_columns-x);
@@ -896,7 +899,7 @@
 		case 1:	/* erase from start to cursor */
 			count = ((pos-origin)>>1)+1;
 			start = (unsigned short *) origin;
-			if (DO_UPDATE) {
+			if (IS_VISIBLE) {
 				/* do in two stages */
 				sw->con_clear(vc_cons[currcons].d, 0, 0, y,
 					      video_num_columns);
@@ -907,7 +910,7 @@
 		case 2: /* erase whole display */
 			count = video_num_columns * video_num_lines;
 			start = (unsigned short *) origin;
-			if (DO_UPDATE)
+			if (IS_VISIBLE)
 				sw->con_clear(vc_cons[currcons].d, 0, 0,
 					      video_num_lines,
 					      video_num_columns);
@@ -928,21 +931,21 @@
 		case 0:	/* erase from cursor to end of line */
 			count = video_num_columns-x;
 			start = (unsigned short *) pos;
-			if (DO_UPDATE)
+			if (IS_VISIBLE)
 				sw->con_clear(vc_cons[currcons].d, y, x, 1,
 					      video_num_columns-x);
 			break;
 		case 1:	/* erase from start of line to cursor */
 			start = (unsigned short *) (pos - (x<<1));
 			count = x+1;
-			if (DO_UPDATE)
+			if (IS_VISIBLE)
 				sw->con_clear(vc_cons[currcons].d, y, 0, 1,
 					      x + 1);
 			break;
 		case 2: /* erase whole line */
 			start = (unsigned short *) (pos - (x<<1));
 			count = video_num_columns;
-			if (DO_UPDATE)
+			if (IS_VISIBLE)
 				sw->con_clear(vc_cons[currcons].d, y, 0, 1,
 					      video_num_columns);
 			break;
@@ -962,7 +965,7 @@
 	count = (vpar > video_num_columns-x) ? (video_num_columns-x) : vpar;
 
 	scr_memsetw((unsigned short *) pos, video_erase_char, 2 * count);
-	if (DO_UPDATE)
+	if (IS_VISIBLE)
 		sw->con_clear(vc_cons[currcons].d, y, x, 1, count);
 	need_wrap = 0;
 }
@@ -1138,7 +1141,7 @@
 			case 5:			/* Inverted screen on/off */
 				if (decscnm != on_off) {
 					decscnm = on_off;
-					invert_screen(currcons, 0, video_screen_size, 0);
+					invert_screen(currcons, 0, screenbuf_size, 0);
 					update_attr(currcons);
 				}
 				break;
@@ -1350,6 +1353,7 @@
 	set_leds();
 
 	cursor_type = CUR_DEFAULT;
+	complement_mask = s_complement_mask;
 
 	default_attr(currcons);
 	update_attr(currcons);
@@ -1487,7 +1491,7 @@
 			vc_state = ESpalette;
 			return;
 		} else if (c=='R') {   /* reset palette */
-			reset_palette (currcons);
+			reset_palette(currcons);
 			vc_state = ESnormal;
 		} else
 			vc_state = ESnormal;
@@ -1503,7 +1507,7 @@
 				palette[i++] += par[j++];
 				palette[i] = 16*par[j++];
 				palette[i] += par[j];
-				set_palette() ;
+				set_palette(currcons);
 				vc_state = ESnormal;
 			}
 		} else
@@ -1548,6 +1552,16 @@
 				return;
 			}
 			break;
+		case 'm':
+			if (ques) {
+				clear_selection();
+				if (par[0])
+					complement_mask = par[0]<<8 | par[1];
+				else
+					complement_mask = s_complement_mask;
+				return;
+			}
+			break;
 		case 'n':
 			if (!ques) {
 				if (par[0] == 5)
@@ -1729,14 +1743,10 @@
 static int do_con_write(struct tty_struct * tty, int from_user,
 			const unsigned char *buf, int count)
 {
-#ifdef VT_BUF_VRAM_ONLY
-#define FLUSH do { } while(0);
-#else
 #define FLUSH if (draw_x >= 0) { \
 	sw->con_putcs(vc_cons[currcons].d, (u16 *)draw_from, (u16 *)draw_to-(u16 *)draw_from, y, draw_x); \
 	draw_x = -1; \
 	}
-#endif
 
 	int c, tc, ok, n = 0, draw_x = -1;
 	unsigned int currcons;
@@ -1864,7 +1874,7 @@
 				     ((attr & ~himask) << 8) + ((tc & 0x100) ? himask : 0) + (tc & 0xff) :
 				     (attr << 8) + tc,
 				   (u16 *) pos);
-			if (DO_UPDATE && draw_x < 0) {
+			if (IS_VISIBLE && draw_x < 0) {
 				draw_x = x;
 				draw_from = pos;
 			}
@@ -1901,7 +1911,6 @@
 	if (want_console >= 0) {
 		if (want_console != fg_console && vc_cons_allocated(want_console)) {
 			hide_cursor(fg_console);
-			save_screen();
 			change_console(want_console);
 			/* we only changed when the console had already
 			   been allocated - a new console is not created
@@ -1913,6 +1922,13 @@
 		do_poke_blanked_console = 0;
 		poke_blanked_console();
 	}
+	if (scrollback_delta) {
+		int currcons = fg_console;
+		clear_selection();
+		if (vcmode == KD_TEXT)
+			sw->con_scrolldelta(vc_cons[currcons].d, scrollback_delta);
+		scrollback_delta = 0;
+	}
 }
 
 /*
@@ -2015,7 +2031,7 @@
 	NULL,
 	vt_console_device,
 	keyboard_wait_for_keypress,
-	do_unblank_screen,
+	unblank_screen,
 	NULL,
 	CON_PRINTBUFFER,
 	-1,
@@ -2045,7 +2061,7 @@
 		case 3:
 			return paste_selection(tty);
 		case 4:
-			do_unblank_screen();
+			unblank_screen();
 			return 0;
 		case 5:
 			return sel_loadlut(arg);
@@ -2173,7 +2189,7 @@
 
 	currcons = MINOR(tty->device) - tty->driver.minor_start;
 
-	i = vc_allocate(currcons, 0);
+	i = vc_allocate(currcons);
 	if (i)
 		return i;
 
@@ -2194,7 +2210,7 @@
 	video_num_columns = cols;
 	video_num_lines = rows;
 	video_size_row = cols<<1;
-	video_screen_size = video_num_lines * video_size_row;
+	screenbuf_size = video_num_lines * video_size_row;
 
 	set_origin(currcons);
 	pos = origin;
@@ -2279,11 +2295,10 @@
 		kmem_start += sizeof(struct vc_data);
 		vt_cons[currcons] = (struct vt_struct *) kmem_start;
 		kmem_start += sizeof(struct vt_struct);
-		visual_init(currcons);
+		visual_init(currcons, 1);
 		screenbuf = (unsigned short *) kmem_start;
-		kmem_start += video_screen_size;
+		kmem_start += screenbuf_size;
 		kmalloced = 0;
-		screenbuf_size = video_screen_size;
 		vc_init(currcons, video_num_lines, video_num_columns, 
 			currcons || !sw->con_save_screen);
 		for (j=k=0; j<16; j++) {
@@ -2295,11 +2310,10 @@
 	currcons = fg_console = 0;
 	master_display_fg = vc_cons[currcons].d;
 	set_origin(currcons);
-	save_screen();
+	save_screen(currcons);
 	gotoxy(currcons,x,y);
 	csi_J(currcons, 0);
 	update_screen(fg_console);
-	set_cursor(currcons);
 	printk("Console: %s %s %dx%d",
 		can_do_color ? "colour" : "mono",
 		display_desc, video_num_columns, video_num_lines);
@@ -2315,47 +2329,85 @@
 	return kmem_start;
 }
 
+static void clear_buffer_attributes(int currcons)
+{
+	unsigned short *p = (unsigned short *) origin;
+	int count = screenbuf_size/2;
+	int mask = hi_font_mask | 0xff;
+
+	for (; count > 0; count--, p++) {
+		scr_writew((scr_readw(p)&mask) | (video_erase_char&~mask), p);
+	}
+}
+
 /*
  *	If we support more console drivers, this function is used
  *	when a driver wants to take over some existing consoles
  *	and become default driver for newly opened ones.
  */
 
-#ifndef VT_BUF_VRAM_ONLY
-
 void take_over_console(struct consw *csw, int first, int last, int deflt)
 {
-	int i;
+	int i, j = -1;
 	const char *desc;
 
-	if (deflt)
-		conswitchp = csw;
 	desc = csw->con_startup();
 	if (!desc) return;
+	if (deflt)
+		conswitchp = csw;
 
 	for (i = first; i <= last; i++) {
+		int old_was_color;
+		int currcons = i;
+
+		con_driver_map[i] = csw;
+
 		if (!vc_cons[i].d || !vc_cons[i].d->vc_sw)
 			continue;
-		if (i == fg_console &&
-		    vc_cons[i].d->vc_sw->con_save_screen)
-			vc_cons[i].d->vc_sw->con_save_screen(vc_cons[i].d);
+
+		j = i;
+		if (IS_VISIBLE)
+			save_screen(i);
+		old_was_color = vc_cons[i].d->vc_can_do_color;
 		vc_cons[i].d->vc_sw->con_deinit(vc_cons[i].d);
-		vc_cons[i].d->vc_sw = csw;
-		vc_cons[i].d->vc_sw->con_init(vc_cons[i].d, 0);
-		}
-	printk("Console: switching to %s %s %dx%d\n",
-	       vc_cons[fg_console].d->vc_can_do_color ? "colour" : "mono",
-	       desc, vc_cons[fg_console].d->vc_cols, vc_cons[fg_console].d->vc_rows);
-	set_palette();
+		visual_init(i, 0);
+		update_attr(i);
+
+		/* If the console changed between mono <-> color, then
+		 * the attributes in the screenbuf will be wrong.  The
+		 * following resets all attributes to something sane.
+		 */
+		if (old_was_color != vc_cons[i].d->vc_can_do_color)
+			clear_buffer_attributes(i);
+
+		if (IS_VISIBLE)
+			update_screen(i);
+	}
+	printk("Console: switching ");
+	if (!deflt)
+		printk("consoles %d-%d ", first, last);
+	if (j >= 0)
+		printk("to %s %s %dx%d\n",
+		       vc_cons[j].d->vc_can_do_color ? "colour" : "mono",
+		       desc, vc_cons[j].d->vc_cols, vc_cons[j].d->vc_rows);
+	else
+		printk("to %s\n", desc);
 }
 
-#endif
+void give_up_console(struct consw *csw)
+{
+	int i;
+
+	for(i = 0; i < MAX_NR_CONSOLES; i++)
+		if (con_driver_map[i] == csw)
+			con_driver_map[i] = NULL;
+}
 
 /*
  *	Screen blanking
  */
 
-void set_vesa_blanking(unsigned long arg)
+static void set_vesa_blanking(unsigned long arg)
 {
     char *argp = (char *)arg + 1;
     unsigned int mode;
@@ -2363,13 +2415,7 @@
     vesa_blank_mode = (mode < 4) ? mode : 0;
 }
 
-void vesa_blank(void)
-{
-    struct vc_data *c = vc_cons[fg_console].d;
-    c->vc_sw->con_blank(c, vesa_blank_mode + 1);
-}
-
-void vesa_powerdown(void)
+static void vesa_powerdown(void)
 {
     struct vc_data *c = vc_cons[fg_console].d;
     /*
@@ -2389,7 +2435,7 @@
     }
 }
 
-void vesa_powerdown_screen(void)
+static void vesa_powerdown_screen(void)
 {
 	timer_active &= ~(1<<BLANK_TIMER);
 	timer_table[BLANK_TIMER].fn = unblank_screen;
@@ -2397,7 +2443,7 @@
 	vesa_powerdown();
 }
 
-void do_blank_screen(int nopowersave)
+void do_blank_screen(int entering_gfx)
 {
 	int currcons = fg_console;
 	int i;
@@ -2406,9 +2452,9 @@
 		return;
 
 	/* entering graphics mode? */
-	if (nopowersave) {
+	if (entering_gfx) {
 		hide_cursor(currcons);
-		save_screen();
+		save_screen(currcons);
 		sw->con_blank(vc_cons[currcons].d, -1);
 		console_blanked = fg_console + 1;
 		set_origin(currcons);
@@ -2416,13 +2462,13 @@
 	}
 
 	/* don't blank graphics */
-	if (vt_cons[fg_console]->vc_mode != KD_TEXT) {
+	if (vcmode != KD_TEXT) {
 		console_blanked = fg_console + 1;
 		return;
 	}
 
-	hide_cursor(fg_console);
-	if(vesa_off_interval && !nopowersave) {
+	hide_cursor(currcons);
+	if (vesa_off_interval) {
 		timer_table[BLANK_TIMER].fn = vesa_powerdown_screen;
 		timer_table[BLANK_TIMER].expires = jiffies + vesa_off_interval;
 		timer_active |= (1<<BLANK_TIMER);
@@ -2431,26 +2477,25 @@
 		timer_table[BLANK_TIMER].fn = unblank_screen;
 	}
 
-	save_screen();
+	save_screen(currcons);
 	/* In case we need to reset origin, blanking hook returns 1 */
 	i = sw->con_blank(vc_cons[currcons].d, 1);
 	console_blanked = fg_console + 1;
 	if (i)
 		set_origin(currcons);
 
-	if(!nopowersave)
-	{
 #ifdef CONFIG_APM
-		if (apm_display_blank())
-			return;
+	if (apm_display_blank())
+		return;
 #endif
-		vesa_blank();
-	}
+    	if (vesa_blank_mode)
+		sw->con_blank(vc_cons[currcons].d, vesa_blank_mode + 1);
 }
 
-void do_unblank_screen(void)
+void unblank_screen(void)
 {
 	int currcons;
+
 	if (!console_blanked)
 		return;
 	if (!vc_cons_allocated(fg_console)) {
@@ -2480,11 +2525,6 @@
 	do_blank_screen(0);
 }
 
-static void unblank_screen(void)
-{
-	do_unblank_screen();
-}
-
 void poke_blanked_console(void)
 {
 	timer_active &= ~(1<<BLANK_TIMER);
@@ -2504,13 +2544,13 @@
  *	Palettes
  */
 
-void set_palette(void)
+void set_palette(int currcons)
 {
-    if (vt_cons[fg_console]->vc_mode != KD_GRAPHICS)
-	vc_cons[fg_console].d->vc_sw->con_set_palette(vc_cons[fg_console].d, color_table);
+	if (vcmode != KD_GRAPHICS)
+		sw->con_set_palette(vc_cons[currcons].d, color_table);
 }
 
-int set_get_cmap(unsigned char *arg, int set)
+static int set_get_cmap(unsigned char *arg, int set)
 {
     int i, j, k;
 
@@ -2526,13 +2566,14 @@
 	}
     if (set) {
 	for (i = 0; i < MAX_NR_CONSOLES; i++)
-	    if (vc_cons_allocated(i))
+	    if (vc_cons_allocated(i)) {
 		for (j = k = 0; j < 16; j++) {
 		    vc_cons[i].d->vc_palette[k++] = default_red[j];
 		    vc_cons[i].d->vc_palette[k++] = default_grn[j];
 		    vc_cons[i].d->vc_palette[k++] = default_blu[j];
 		}
-	set_palette();
+		set_palette(i);
+	    }
     }
     return 0;
 }
@@ -2542,25 +2583,25 @@
  * map, 3 bytes per colour, 16 colours, range from 0 to 255.
  */
 
-int con_set_cmap (unsigned char *arg)
+int con_set_cmap(unsigned char *arg)
 {
 	return set_get_cmap (arg,1);
 }
 
-int con_get_cmap (unsigned char *arg)
+int con_get_cmap(unsigned char *arg)
 {
 	return set_get_cmap (arg,0);
 }
 
-void reset_palette (int currcons)
+void reset_palette(int currcons)
 {
-	int j, k ;
+	int j, k;
 	for (j=k=0; j<16; j++) {
 		palette[k++] = default_red[j];
 		palette[k++] = default_grn[j];
 		palette[k++] = default_blu[j];
 	}
-	set_palette() ;
+	set_palette(currcons);
 }
 
 /*
@@ -2635,7 +2676,9 @@
 		}
 		op->data = temp;
 	}
+	disable_bh(CONSOLE_BH);
 	rc = sw->con_font_op(vc_cons[currcons].d, op);
+	enable_bh(CONSOLE_BH);
 	op->data = old_op.data;
 	if (!rc && !set) {
 		int c = (op->width+7)/8 * 32 * op->charcount;
@@ -2721,7 +2764,7 @@
 EXPORT_SYMBOL(default_blu);
 EXPORT_SYMBOL(video_font_height);
 EXPORT_SYMBOL(video_scan_lines);
+EXPORT_SYMBOL(vc_resize);
 
-#ifndef VT_BUF_VRAM_ONLY
 EXPORT_SYMBOL(take_over_console);
-#endif
+EXPORT_SYMBOL(give_up_console);
diff -u --recursive --new-file v2.1.122/linux/drivers/char/console_macros.h linux/drivers/char/console_macros.h
--- v2.1.122/linux/drivers/char/console_macros.h	Sun Jul 26 11:57:15 1998
+++ linux/drivers/char/console_macros.h	Thu Sep 17 09:35:03 1998
@@ -26,8 +26,6 @@
 #define utf		(vc_cons[currcons].d->vc_utf)
 #define utf_count	(vc_cons[currcons].d->vc_utf_count)
 #define utf_char	(vc_cons[currcons].d->vc_utf_char)
-#define video_mem_start (vc_cons[currcons].d->vc_video_mem_start)
-#define video_mem_end	(vc_cons[currcons].d->vc_video_mem_end)
 #define video_erase_char (vc_cons[currcons].d->vc_video_erase_char)
 #define disp_ctrl	(vc_cons[currcons].d->vc_disp_ctrl)
 #define toggle_meta	(vc_cons[currcons].d->vc_toggle_meta)
@@ -64,6 +62,7 @@
 #define cursor_type	(vc_cons[currcons].d->vc_cursor_type)
 #define display_fg	(vc_cons[currcons].d->vc_display_fg)
 #define complement_mask (vc_cons[currcons].d->vc_complement_mask)
+#define s_complement_mask (vc_cons[currcons].d->vc_s_complement_mask)
 #define hi_font_mask	(vc_cons[currcons].d->vc_hi_font_mask)
 
 #define vcmode		(vt_cons[currcons]->vc_mode)
diff -u --recursive --new-file v2.1.122/linux/drivers/char/consolemap.c linux/drivers/char/consolemap.c
--- v2.1.122/linux/drivers/char/consolemap.c	Tue Jul 28 14:21:08 1998
+++ linux/drivers/char/consolemap.c	Thu Sep 17 09:35:03 1998
@@ -576,6 +576,24 @@
 }
 
 int
+con_copy_unimap(int dstcon, int srccon)
+{
+	struct vc_data *sconp = vc_cons[srccon].d;
+	struct vc_data *dconp = vc_cons[dstcon].d;
+	struct uni_pagedir *q;
+	
+	if (!vc_cons_allocated(srccon) || !*sconp->vc_uni_pagedir_loc)
+		return -EINVAL;
+	if (*dconp->vc_uni_pagedir_loc == *sconp->vc_uni_pagedir_loc)
+		return 0;
+	con_free_unimap(dstcon);
+	q = (struct uni_pagedir *)*sconp->vc_uni_pagedir_loc;
+	q->refcount++;
+	*dconp->vc_uni_pagedir_loc = (long)q;
+	return 0;
+}
+
+int
 con_get_unimap(int con, ushort ct, ushort *uct, struct unipair *list)
 {
 	int i, j, k, ect;
diff -u --recursive --new-file v2.1.122/linux/drivers/char/esp.c linux/drivers/char/esp.c
--- v2.1.122/linux/drivers/char/esp.c	Tue Jul 28 14:21:08 1998
+++ linux/drivers/char/esp.c	Fri Sep 25 17:14:58 1998
@@ -102,7 +102,7 @@
 #define WAKEUP_CHARS 1024
 
 static char *serial_name = "ESP serial driver";
-static char *serial_version = "2.1";
+static char *serial_version = "2.2";
 
 static DECLARE_TASK_QUEUE(tq_esp);
 
@@ -2615,6 +2615,9 @@
 	}
 
 	memset((void *)info, 0, sizeof(struct esp_struct));
+	/* rx_trigger, tx_trigger are needed by autoconfig */
+	info->config.rx_trigger = rx_trigger;
+	info->config.tx_trigger = tx_trigger;
 
 	i = 0;
 	offset = 0;
@@ -2644,8 +2647,6 @@
 		info->callout_termios = esp_callout_driver.init_termios;
 		info->normal_termios = esp_driver.init_termios;
 		info->config.rx_timeout = rx_timeout;
-		info->config.rx_trigger = rx_trigger;
-		info->config.tx_trigger = tx_trigger;
 		info->config.flow_on = flow_on;
 		info->config.flow_off = flow_off;
 		info->config.pio_threshold = pio_threshold;
@@ -2681,6 +2682,9 @@
 		}
 
 		memset((void *)info, 0, sizeof(struct esp_struct));
+		/* rx_trigger, tx_trigger are needed by autoconfig */
+		info->config.rx_trigger = rx_trigger;
+		info->config.tx_trigger = tx_trigger;
 
 		if (offset == 56) {
 			i++;
diff -u --recursive --new-file v2.1.122/linux/drivers/char/mem.c linux/drivers/char/mem.c
--- v2.1.122/linux/drivers/char/mem.c	Wed Aug 26 11:37:37 1998
+++ linux/drivers/char/mem.c	Sat Sep 26 23:21:14 1998
@@ -549,15 +549,7 @@
 #ifdef CONFIG_PRINTER
 	lp_init();
 #endif
-#if defined (CONFIG_BUSMOUSE) || defined(CONFIG_UMISC) || \
-    defined (CONFIG_PSMOUSE) || defined (CONFIG_MS_BUSMOUSE) || \
-    defined (CONFIG_ATIXL_BUSMOUSE) || defined(CONFIG_SOFT_WATCHDOG) || \
-    defined (CONFIG_AMIGAMOUSE) || defined (CONFIG_ATARIMOUSE) || \
-    defined (CONFIG_MACMOUSE) || defined (CONFIG_PCWATCHDOG) || \
-    defined (CONFIG_APM) || defined (CONFIG_RTC) || \
-    defined (CONFIG_SUN_MOUSE) || defined (CONFIG_NVRAM)
 	misc_init();
-#endif
 #ifdef CONFIG_SOUND
 	soundcore_init();
 #ifdef CONFIG_SOUND_OSS	
diff -u --recursive --new-file v2.1.122/linux/drivers/char/misc.c linux/drivers/char/misc.c
--- v2.1.122/linux/drivers/char/misc.c	Wed Aug 26 11:37:37 1998
+++ linux/drivers/char/misc.c	Sat Sep 26 23:29:47 1998
@@ -31,8 +31,8 @@
  	Cyrus Durgin <cider@speakeasy.org>
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
+#include <linux/config.h>
 
 #include <linux/fs.h>
 #include <linux/errno.h>
@@ -64,10 +64,10 @@
 #define DYNAMIC_MINORS 64 /* like dynamic majors */
 static unsigned char misc_minors[DYNAMIC_MINORS / 8];
 
-#ifndef MODULE
 extern int adbdev_init(void);
 extern int bus_mouse_init(void);
 extern int psaux_init(void);
+extern int qpmouse_init(void);
 extern int ms_bus_mouse_init(void);
 extern int atixl_busmouse_init(void);
 extern int amiga_mouse_init(void);
@@ -101,7 +101,6 @@
 }
 
 #endif /* PROC_FS */
-#endif /* !MODULE */
 
 static int misc_open(struct inode * inode, struct file * file)
 {
@@ -157,7 +156,6 @@
 	}
 	if (misc->minor < DYNAMIC_MINORS)
 		misc_minors[misc->minor >> 3] |= 1 << (misc->minor & 7);
-	MOD_INC_USE_COUNT;
 	misc->next = &misc_list;
 	misc->prev = misc_list.prev;
 	misc->prev->next = misc;
@@ -170,7 +168,6 @@
 	int i = misc->minor;
 	if (!misc->next || !misc->prev)
 		return -EINVAL;
-	MOD_DEC_USE_COUNT;
 	misc->prev->next = misc->next;
 	misc->next->prev = misc->prev;
 	misc->next = NULL;
@@ -181,27 +178,15 @@
 	return 0;
 }
 
-#ifdef MODULE
-
-#define misc_init init_module
-
-void cleanup_module(void)
-{
-	unregister_chrdev(MISC_MAJOR, "misc");
-}
-
-#endif
-
 EXPORT_SYMBOL(misc_register);
 EXPORT_SYMBOL(misc_deregister);
 
-#if defined(CONFIG_PROC_FS) && !defined(MODULE)
+#if defined(CONFIG_PROC_FS)
 static struct proc_dir_entry *proc_misc;	
 #endif
 
-__initfunc(int misc_init(void))
+int __init misc_init(void)
 {
-#ifndef MODULE
 #ifdef CONFIG_PROC_FS
 	proc_misc = create_proc_entry("misc", 0, 0);
 	if (proc_misc)
@@ -213,6 +198,9 @@
 #ifdef CONFIG_BUSMOUSE
 	bus_mouse_init();
 #endif
+#if defined CONFIG_82C710_MOUSE
+	qpmouse_init();		/* This must be before psaux_init */
+#endif
 #if defined CONFIG_PSMOUSE
 	psaux_init();
 #endif
@@ -285,7 +273,6 @@
 #ifdef CONFIG_PMAC_PBOOK
 	pmu_device_init();
 #endif
-#endif /* !MODULE */
 	if (register_chrdev(MISC_MAJOR,"misc",&misc_fops)) {
 		printk("unable to get major %d for misc devices\n",
 		       MISC_MAJOR);
diff -u --recursive --new-file v2.1.122/linux/drivers/char/pc_keyb.c linux/drivers/char/pc_keyb.c
--- v2.1.122/linux/drivers/char/pc_keyb.c	Wed Sep  9 14:51:07 1998
+++ linux/drivers/char/pc_keyb.c	Sun Sep 27 19:20:20 1998
@@ -611,6 +611,9 @@
 
 void __init pckbd_init_hw(void)
 {
+	/* Get the keyboard controller registers (incomplete decode) */
+	request_region(0x60, 16, "keyboard");
+
 	/* Flush any pending input. */
 	kbd_clear_input();
 
@@ -620,6 +623,6 @@
 			printk(KERN_WARNING "initialize_kbd: %s\n", msg);
 	}
 
+	/* Ok, finally allocate the IRQ, and off we go.. */
 	request_irq(KEYBOARD_IRQ, keyboard_interrupt, 0, "keyboard", NULL);
-	request_region(0x60, 16, "keyboard");
 }
diff -u --recursive --new-file v2.1.122/linux/drivers/char/psaux.c linux/drivers/char/psaux.c
--- v2.1.122/linux/drivers/char/psaux.c	Sat Sep  5 16:46:40 1998
+++ linux/drivers/char/psaux.c	Sat Sep 26 23:13:18 1998
@@ -12,14 +12,6 @@
  * Changed to prevent keyboard lockups on AST Power Exec.
  * 28Jul93  Brad Bosch - brad@lachman.com
  *
- * Modified by Johan Myreen (jem@pandora.pp.fi) 04Aug93
- *   to include support for QuickPort mouse.
- *
- * Changed references to "QuickPort" with "82C710" since "QuickPort"
- * is not what this driver is all about -- QuickPort is just a
- * connector type, and this driver is for the mouse port on the Chips
- * & Technologies 82C710 interface chip. 15Nov93 jem@pandora.pp.fi
- *
  * Added support for SIGIO. 28Jul95 jem@pandora.pp.fi
  *
  * Rearranged SIGIO support to use code from tty_io.  9Sept95 ctm@ardi.com
@@ -35,6 +27,18 @@
  * 13-Jan-1998, Richard Gooch <rgooch@atnf.csiro.au>
  */
 
+/*
+ * This really should be part of the pc_kbd driver - they share the same
+ * controller, and right now we have ridiculous synchronization problems.
+ * Some of the SMP bootup problems may be due to not getting synchronization
+ * right.
+ *
+ * I moved the C&T mouse driver to a file of its own, hopefully that will
+ * make it easier to eventually fix this all.
+ *
+ *		Linus
+ */
+
 /* Uncomment the following line if your mouse needs initialization. */
 
 /* #define INITIALIZE_DEVICE */
@@ -78,13 +82,7 @@
 };
 
 static struct aux_queue *queue;
-static int aux_ready = 0;
 static int aux_count = 0;
-static int aux_present = 0;
-
-/*
- *	Shared subroutines
- */
 
 static unsigned int get_from_queue(void)
 {
@@ -234,7 +232,6 @@
 		head &= AUX_BUF_SIZE-1;
 	}
 	queue->head = head;
-	aux_ready = 1;
 	if (queue->fasync)
 		kill_fasync(queue->fasync, SIGIO);
 	wake_up_interruptible(&queue->proc_list);
@@ -270,8 +267,6 @@
 
 static int open_aux(struct inode * inode, struct file * file)
 {
-	if (!aux_present)
-		return -ENODEV;
 	aux_start_atomic();
 	if (aux_count++) {
 		aux_end_atomic();
@@ -304,7 +299,6 @@
 	pckbd_read_mask = AUX_STAT_OBF;
 #endif
 
-	aux_ready = 0;
 	return 0;
 }
 
@@ -344,196 +338,6 @@
 }
 
 /*
- *	82C710 Interface
- */
-
-#ifdef CONFIG_82C710_MOUSE
-
-#define QP_DATA         0x310		/* Data Port I/O Address */
-#define QP_STATUS       0x311		/* Status Port I/O Address */
-
-#define QP_DEV_IDLE     0x01		/* Device Idle */
-#define QP_RX_FULL      0x02		/* Device Char received */
-#define QP_TX_IDLE      0x04		/* Device XMIT Idle */
-#define QP_RESET        0x08		/* Device Reset */
-#define QP_INTS_ON      0x10		/* Device Interrupt On */
-#define QP_ERROR_FLAG   0x20		/* Device Error */
-#define QP_CLEAR        0x40		/* Device Clear */
-#define QP_ENABLE       0x80		/* Device Enable */
-
-#define QP_IRQ          12
-
-static int qp_present = 0;
-static int qp_count = 0;
-static int qp_data = QP_DATA;
-static int qp_status = QP_STATUS;
-
-static int poll_qp_status(void);
-static int probe_qp(void);
-
-/*
- * Interrupt handler for the 82C710 mouse port. A character
- * is waiting in the 82C710.
- */
-
-static void qp_interrupt(int cpl, void *dev_id, struct pt_regs * regs)
-{
-	int head = queue->head;
-	int maxhead = (queue->tail-1) & (AUX_BUF_SIZE-1);
-
-	add_mouse_randomness(queue->buf[head] = inb(qp_data));
-	if (head != maxhead) {
-		head++;
-		head &= AUX_BUF_SIZE-1;
-	}
-	queue->head = head;
-	aux_ready = 1;
-	if (queue->fasync)
-		kill_fasync(queue->fasync, SIGIO);
-	wake_up_interruptible(&queue->proc_list);
-}
-
-static int release_qp(struct inode * inode, struct file * file)
-{
-	unsigned char status;
-
-	fasync_aux(-1, file, 0);
-	if (!--qp_count) {
-		if (!poll_qp_status())
-			printk("Warning: Mouse device busy in release_qp()\n");
-		status = inb_p(qp_status);
-		outb_p(status & ~(QP_ENABLE|QP_INTS_ON), qp_status);
-		if (!poll_qp_status())
-			printk("Warning: Mouse device busy in release_qp()\n");
-		free_irq(QP_IRQ, NULL);
-		MOD_DEC_USE_COUNT;
-	}
-	return 0;
-}
-
-/*
- * Install interrupt handler.
- * Enable the device, enable interrupts. 
- */
-
-static int open_qp(struct inode * inode, struct file * file)
-{
-	unsigned char status;
-
-	if (!qp_present)
-		return -EINVAL;
-
-	if (qp_count++)
-		return 0;
-
-	if (request_irq(QP_IRQ, qp_interrupt, 0, "PS/2 Mouse", NULL)) {
-		qp_count--;
-		return -EBUSY;
-	}
-
-	status = inb_p(qp_status);
-	status |= (QP_ENABLE|QP_RESET);
-	outb_p(status, qp_status);
-	status &= ~(QP_RESET);
-	outb_p(status, qp_status);
-
-	queue->head = queue->tail = 0;          /* Flush input queue */
-	status |= QP_INTS_ON;
-	outb_p(status, qp_status);              /* Enable interrupts */
-
-	while (!poll_qp_status()) {
-		printk("Error: Mouse device busy in open_qp()\n");
-		qp_count--;
-		status &= ~(QP_ENABLE|QP_INTS_ON);
-		outb_p(status, qp_status);
-		free_irq(QP_IRQ, NULL);
-		return -EBUSY;
-	}
-
-	outb_p(AUX_ENABLE_DEV, qp_data);	/* Wake up mouse */
-	MOD_INC_USE_COUNT;
-	return 0;
-}
-
-/*
- * Write to the 82C710 mouse device.
- */
-
-static ssize_t write_qp(struct file * file, const char * buffer,
-			size_t count, loff_t *ppos)
-{
-	ssize_t i = count;
-
-	while (i--) {
-		char c;
-		if (!poll_qp_status())
-			return -EIO;
-		get_user(c, buffer++);
-		outb_p(c, qp_data);
-	}
-	file->f_dentry->d_inode->i_mtime = CURRENT_TIME;
-	return count;
-}
-
-/*
- * Wait for device to send output char and flush any input char.
- */
-
-static int poll_qp_status(void)
-{
-	int retries=0;
-
-	while ((inb(qp_status)&(QP_RX_FULL|QP_TX_IDLE|QP_DEV_IDLE))
-		       != (QP_DEV_IDLE|QP_TX_IDLE)
-		       && retries < MAX_RETRIES) {
-
-		if (inb_p(qp_status)&(QP_RX_FULL))
-			inb_p(qp_data);
-		current->state = TASK_INTERRUPTIBLE;
-		current->timeout = jiffies + (5*HZ + 99) / 100;
-		schedule();
-		retries++;
-	}
-	return !(retries==MAX_RETRIES);
-}
-
-/*
- * Function to read register in 82C710.
- */
-
-static inline unsigned char read_710(unsigned char index)
-{
-	outb_p(index, 0x390);			/* Write index */
-	return inb_p(0x391);			/* Read the data */
-}
-
-/*
- * See if we can find a 82C710 device. Read mouse address.
- */
-
-__initfunc(static int probe_qp(void))
-{
-	outb_p(0x55, 0x2fa);			/* Any value except 9, ff or 36 */
-	outb_p(0xaa, 0x3fa);			/* Inverse of 55 */
-	outb_p(0x36, 0x3fa);			/* Address the chip */
-	outb_p(0xe4, 0x3fa);			/* 390/4; 390 = config address */
-	outb_p(0x1b, 0x2fa);			/* Inverse of e4 */
-	if (read_710(0x0f) != 0xe4)		/* Config address found? */
-	  return 0;				/* No: no 82C710 here */
-	qp_data = read_710(0x0d)*4;		/* Get mouse I/O address */
-	qp_status = qp_data+1;
-	outb_p(0x0f, 0x390);
-	outb_p(0x0f, 0x391);			/* Close config mode */
-	return 1;
-}
-
-#endif
-
-/*
- *	Generic part continues...
- */
-
-/*
  * Put bytes from input queue to buffer.
  */
 
@@ -562,7 +366,6 @@
 		put_user(c, buffer++);
 		i--;
 	}
-	aux_ready = !queue_empty();
 	if (count-i) {
 		file->f_dentry->d_inode->i_atime = CURRENT_TIME;
 		return count-i;
@@ -575,7 +378,7 @@
 static unsigned int aux_poll(struct file *file, poll_table * wait)
 {
 	poll_wait(file, &queue->proc_list, wait);
-	if (aux_ready)
+	if (!queue_empty())
 		return POLLIN | POLLRDNORM;
 	return 0;
 }
@@ -596,8 +399,7 @@
 };
 
 /*
- * Initialize driver. First check for a 82C710 chip; if found
- * forget about the Aux port and use the *_qp functions.
+ * Initialize driver.
  */
 static struct miscdevice psaux_mouse = {
 	PSMOUSE_MINOR, "psaux", &psaux_fops
@@ -605,48 +407,33 @@
 
 __initfunc(int psaux_init(void))
 {
-	int qp_found = 0;
-
-#ifdef CONFIG_82C710_MOUSE
-	if ((qp_found = probe_qp())) {
-		printk(KERN_INFO "82C710 type pointing device detected -- driver installed.\n");
-/*		printk("82C710 address = %x (should be 0x310)\n", qp_data); */
-		qp_present = 1;
-		psaux_fops.write = write_qp;
-		psaux_fops.open = open_qp;
-		psaux_fops.release = release_qp;
-	} else
-#endif
-	if (aux_device_present == 0xaa) {
-		printk(KERN_INFO "PS/2 auxiliary pointing device detected -- driver installed.\n");
-	 	aux_present = 1;
-	} else {
+	if (aux_device_present != 0xaa)
 		return -EIO;
-	}
+
+	printk(KERN_INFO "PS/2 auxiliary pointing device detected -- driver installed.\n");
 	misc_register(&psaux_mouse);
 	queue = (struct aux_queue *) kmalloc(sizeof(*queue), GFP_KERNEL);
 	memset(queue, 0, sizeof(*queue));
 	queue->head = queue->tail = 0;
 	queue->proc_list = NULL;
-	if (!qp_found) {
-		aux_start_atomic();
+
+	aux_start_atomic();
 #ifdef INITIALIZE_DEVICE
-		outb_p(KBD_CCMD_MOUSE_ENABLE, KBD_CNTL_REG); /* Enable Aux */
-		aux_write_ack(AUX_SET_SAMPLE);
-		aux_write_ack(100);			/* 100 samples/sec */
-		aux_write_ack(AUX_SET_RES);
-		aux_write_ack(3);			/* 8 counts per mm */
-		aux_write_ack(AUX_SET_SCALE21);		/* 2:1 scaling */
-		poll_aux_status();
+	outb_p(KBD_CCMD_MOUSE_ENABLE, KBD_CNTL_REG); /* Enable Aux */
+	aux_write_ack(AUX_SET_SAMPLE);
+	aux_write_ack(100);			/* 100 samples/sec */
+	aux_write_ack(AUX_SET_RES);
+	aux_write_ack(3);			/* 8 counts per mm */
+	aux_write_ack(AUX_SET_SCALE21);		/* 2:1 scaling */
+	poll_aux_status();
 #endif /* INITIALIZE_DEVICE */
-		outb_p(KBD_CCMD_MOUSE_DISABLE, KBD_CNTL_REG); /* Disable Aux device */
-		poll_aux_status();
-		outb_p(KBD_CCMD_WRITE_MODE, KBD_CNTL_REG);    /* Disable controller interrupts */
-		poll_aux_status();
-		outb_p(AUX_INTS_OFF, KBD_DATA_REG);
-		poll_aux_status();
-		aux_end_atomic();
-	}
+	outb_p(KBD_CCMD_MOUSE_DISABLE, KBD_CNTL_REG); /* Disable Aux device */
+	poll_aux_status();
+	outb_p(KBD_CCMD_WRITE_MODE, KBD_CNTL_REG);    /* Disable controller interrupts */
+	poll_aux_status();
+	outb_p(AUX_INTS_OFF, KBD_DATA_REG);
+	poll_aux_status();
+	aux_end_atomic();
 
 	return 0;
 }
diff -u --recursive --new-file v2.1.122/linux/drivers/char/qpmouse.c linux/drivers/char/qpmouse.c
--- v2.1.122/linux/drivers/char/qpmouse.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/char/qpmouse.c	Sat Sep 26 23:30:51 1998
@@ -0,0 +1,374 @@
+/*
+ * linux/drivers/char/qpmouse.c
+ *
+ * Driver for a 82C710 C&T mouse interface chip.
+ *
+ * Based on the PS/2 driver by Johan Myreen.
+ *
+ * Corrections in device setup for some laptop mice & trackballs.
+ * 02Feb93  (troyer@saifr00.cfsat.Honeywell.COM,mch@wimsey.bc.ca)
+ *
+ * Modified by Johan Myreen (jem@pandora.pp.fi) 04Aug93
+ *   to include support for QuickPort mouse.
+ *
+ * Changed references to "QuickPort" with "82C710" since "QuickPort"
+ * is not what this driver is all about -- QuickPort is just a
+ * connector type, and this driver is for the mouse port on the Chips
+ * & Technologies 82C710 interface chip. 15Nov93 jem@pandora.pp.fi
+ *
+ * Added support for SIGIO. 28Jul95 jem@pandora.pp.fi
+ *
+ * Rearranged SIGIO support to use code from tty_io.  9Sept95 ctm@ardi.com
+ *
+ * Modularised 8-Sep-95 Philip Blundell <pjb27@cam.ac.uk>
+ */
+
+#include <linux/module.h>
+ 
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/fcntl.h>
+#include <linux/errno.h>
+#include <linux/timer.h>
+#include <linux/malloc.h>
+#include <linux/miscdevice.h>
+#include <linux/random.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/semaphore.h>
+
+#include "pc_keyb.h"		/* mouse enable command.. */
+
+
+/*
+ * We use the same minor number as the PS/2 mouse for (bad) historical
+ * reasons..
+ */
+#define PSMOUSE_MINOR      1	       		/* Minor device # for this mouse */
+#define QP_BUF_SIZE	2048
+
+struct qp_queue {
+	unsigned long head;
+	unsigned long tail;
+	struct wait_queue *proc_list;
+	struct fasync_struct *fasync;
+	unsigned char buf[QP_BUF_SIZE];
+};
+
+static struct qp_queue *queue;
+
+static unsigned int get_from_queue(void)
+{
+	unsigned int result;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	result = queue->buf[queue->tail];
+	queue->tail = (queue->tail + 1) & (QP_BUF_SIZE-1);
+	restore_flags(flags);
+	return result;
+}
+
+
+static inline int queue_empty(void)
+{
+	return queue->head == queue->tail;
+}
+
+static int fasync_qp(int fd, struct file *filp, int on)
+{
+	int retval;
+
+	retval = fasync_helper(fd, filp, on, &queue->fasync);
+	if (retval < 0)
+		return retval;
+	return 0;
+}
+
+/*
+ *	82C710 Interface
+ */
+
+#define QP_DATA         0x310		/* Data Port I/O Address */
+#define QP_STATUS       0x311		/* Status Port I/O Address */
+
+#define QP_DEV_IDLE     0x01		/* Device Idle */
+#define QP_RX_FULL      0x02		/* Device Char received */
+#define QP_TX_IDLE      0x04		/* Device XMIT Idle */
+#define QP_RESET        0x08		/* Device Reset */
+#define QP_INTS_ON      0x10		/* Device Interrupt On */
+#define QP_ERROR_FLAG   0x20		/* Device Error */
+#define QP_CLEAR        0x40		/* Device Clear */
+#define QP_ENABLE       0x80		/* Device Enable */
+
+#define QP_IRQ          12
+
+static int qp_present = 0;
+static int qp_count = 0;
+static int qp_data = QP_DATA;
+static int qp_status = QP_STATUS;
+
+static int poll_qp_status(void);
+static int probe_qp(void);
+
+/*
+ * Interrupt handler for the 82C710 mouse port. A character
+ * is waiting in the 82C710.
+ */
+
+static void qp_interrupt(int cpl, void *dev_id, struct pt_regs * regs)
+{
+	int head = queue->head;
+	int maxhead = (queue->tail-1) & (QP_BUF_SIZE-1);
+
+	add_mouse_randomness(queue->buf[head] = inb(qp_data));
+	if (head != maxhead) {
+		head++;
+		head &= QP_BUF_SIZE-1;
+	}
+	queue->head = head;
+	if (queue->fasync)
+		kill_fasync(queue->fasync, SIGIO);
+	wake_up_interruptible(&queue->proc_list);
+}
+
+static int release_qp(struct inode * inode, struct file * file)
+{
+	unsigned char status;
+
+	fasync_qp(-1, file, 0);
+	if (!--qp_count) {
+		if (!poll_qp_status())
+			printk("Warning: Mouse device busy in release_qp()\n");
+		status = inb_p(qp_status);
+		outb_p(status & ~(QP_ENABLE|QP_INTS_ON), qp_status);
+		if (!poll_qp_status())
+			printk("Warning: Mouse device busy in release_qp()\n");
+		free_irq(QP_IRQ, NULL);
+		MOD_DEC_USE_COUNT;
+	}
+	return 0;
+}
+
+/*
+ * Install interrupt handler.
+ * Enable the device, enable interrupts. 
+ */
+
+static int open_qp(struct inode * inode, struct file * file)
+{
+	unsigned char status;
+
+	if (!qp_present)
+		return -EINVAL;
+
+	if (qp_count++)
+		return 0;
+
+	if (request_irq(QP_IRQ, qp_interrupt, 0, "PS/2 Mouse", NULL)) {
+		qp_count--;
+		return -EBUSY;
+	}
+
+	status = inb_p(qp_status);
+	status |= (QP_ENABLE|QP_RESET);
+	outb_p(status, qp_status);
+	status &= ~(QP_RESET);
+	outb_p(status, qp_status);
+
+	queue->head = queue->tail = 0;          /* Flush input queue */
+	status |= QP_INTS_ON;
+	outb_p(status, qp_status);              /* Enable interrupts */
+
+	while (!poll_qp_status()) {
+		printk("Error: Mouse device busy in open_qp()\n");
+		qp_count--;
+		status &= ~(QP_ENABLE|QP_INTS_ON);
+		outb_p(status, qp_status);
+		free_irq(QP_IRQ, NULL);
+		return -EBUSY;
+	}
+
+	outb_p(AUX_ENABLE_DEV, qp_data);	/* Wake up mouse */
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+/*
+ * Write to the 82C710 mouse device.
+ */
+
+static ssize_t write_qp(struct file * file, const char * buffer,
+			size_t count, loff_t *ppos)
+{
+	ssize_t i = count;
+
+	while (i--) {
+		char c;
+		if (!poll_qp_status())
+			return -EIO;
+		get_user(c, buffer++);
+		outb_p(c, qp_data);
+	}
+	file->f_dentry->d_inode->i_mtime = CURRENT_TIME;
+	return count;
+}
+
+static unsigned int poll_qp(struct file *file, poll_table * wait)
+{
+	poll_wait(file, &queue->proc_list, wait);
+	if (!queue_empty())
+		return POLLIN | POLLRDNORM;
+	return 0;
+}
+
+/*
+ * Wait for device to send output char and flush any input char.
+ */
+
+#define MAX_RETRIES (60)
+
+static int poll_qp_status(void)
+{
+	int retries=0;
+
+	while ((inb(qp_status)&(QP_RX_FULL|QP_TX_IDLE|QP_DEV_IDLE))
+		       != (QP_DEV_IDLE|QP_TX_IDLE)
+		       && retries < MAX_RETRIES) {
+
+		if (inb_p(qp_status)&(QP_RX_FULL))
+			inb_p(qp_data);
+		current->state = TASK_INTERRUPTIBLE;
+		current->timeout = jiffies + (5*HZ + 99) / 100;
+		schedule();
+		retries++;
+	}
+	return !(retries==MAX_RETRIES);
+}
+
+/*
+ * Put bytes from input queue to buffer.
+ */
+
+static ssize_t read_qp(struct file * file, char * buffer,
+			size_t count, loff_t *ppos)
+{
+	struct wait_queue wait = { current, NULL };
+	ssize_t i = count;
+	unsigned char c;
+
+	if (queue_empty()) {
+		if (file->f_flags & O_NONBLOCK)
+			return -EAGAIN;
+		add_wait_queue(&queue->proc_list, &wait);
+repeat:
+		current->state = TASK_INTERRUPTIBLE;
+		if (queue_empty() && !signal_pending(current)) {
+			schedule();
+			goto repeat;
+		}
+		current->state = TASK_RUNNING;
+		remove_wait_queue(&queue->proc_list, &wait);
+	}
+	while (i > 0 && !queue_empty()) {
+		c = get_from_queue();
+		put_user(c, buffer++);
+		i--;
+	}
+	if (count-i) {
+		file->f_dentry->d_inode->i_atime = CURRENT_TIME;
+		return count-i;
+	}
+	if (signal_pending(current))
+		return -ERESTARTSYS;
+	return 0;
+}
+
+struct file_operations qp_fops = {
+	NULL,		/* seek */
+	read_qp,
+	write_qp,
+	NULL, 		/* readdir */
+	poll_qp,
+	NULL, 		/* ioctl */
+	NULL,		/* mmap */
+	open_qp,
+	NULL,		/* flush */
+	release_qp,
+	NULL,
+	fasync_qp,
+};
+
+/*
+ * Initialize driver.
+ */
+static struct miscdevice qp_mouse = {
+	PSMOUSE_MINOR, "QPmouse", &qp_fops
+};
+
+/*
+ * Function to read register in 82C710.
+ */
+
+static inline unsigned char read_710(unsigned char index)
+{
+	outb_p(index, 0x390);			/* Write index */
+	return inb_p(0x391);			/* Read the data */
+}
+
+
+/*
+ * See if we can find a 82C710 device. Read mouse address.
+ */
+
+static int __init probe_qp(void)
+{
+	outb_p(0x55, 0x2fa);			/* Any value except 9, ff or 36 */
+	outb_p(0xaa, 0x3fa);			/* Inverse of 55 */
+	outb_p(0x36, 0x3fa);			/* Address the chip */
+	outb_p(0xe4, 0x3fa);			/* 390/4; 390 = config address */
+	outb_p(0x1b, 0x2fa);			/* Inverse of e4 */
+	if (read_710(0x0f) != 0xe4)		/* Config address found? */
+	  return 0;				/* No: no 82C710 here */
+	qp_data = read_710(0x0d)*4;		/* Get mouse I/O address */
+	qp_status = qp_data+1;
+	outb_p(0x0f, 0x390);
+	outb_p(0x0f, 0x391);			/* Close config mode */
+	return 1;
+}
+
+int __init qpmouse_init(void)
+{
+	if (!probe_qp())
+		return -EIO;
+
+	printk(KERN_INFO "82C710 type pointing device detected -- driver installed.\n");
+/*	printk("82C710 address = %x (should be 0x310)\n", qp_data); */
+	qp_present = 1;
+	misc_register(&qp_mouse);
+	queue = (struct qp_queue *) kmalloc(sizeof(*queue), GFP_KERNEL);
+	memset(queue, 0, sizeof(*queue));
+	queue->head = queue->tail = 0;
+	queue->proc_list = NULL;
+
+	return 0;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+	return qpmouse_init();
+}
+
+void cleanup_module(void)
+{
+	misc_deregister(&qp_mouse);
+	kfree(queue);
+}
+#endif
diff -u --recursive --new-file v2.1.122/linux/drivers/char/selection.c linux/drivers/char/selection.c
--- v2.1.122/linux/drivers/char/selection.c	Tue Jul 28 14:21:08 1998
+++ linux/drivers/char/selection.c	Thu Sep 17 09:35:03 1998
@@ -123,7 +123,7 @@
 	int i, ps, pe;
 	unsigned int currcons = fg_console;
 
-	do_unblank_screen();
+	unblank_screen();
 	poke_blanked_console();
 
 	{ unsigned short *args, xs, ys, xe, ye;
diff -u --recursive --new-file v2.1.122/linux/drivers/char/sysrq.c linux/drivers/char/sysrq.c
--- v2.1.122/linux/drivers/char/sysrq.c	Tue Jul 28 14:21:08 1998
+++ linux/drivers/char/sysrq.c	Thu Sep 17 09:35:03 1998
@@ -1,6 +1,6 @@
 /* -*- linux-c -*-
  *
- *	$Id: sysrq.c,v 1.7 1997/11/06 15:57:09 mj Exp $
+ *	$Id: sysrq.c,v 1.15 1998/08/23 14:56:41 mj Exp $
  *
  *	Linux Magic System Request Key Hacks
  *
@@ -70,12 +70,14 @@
 			printk("Keyboard mode set to XLATE\n");
 		}
 		break;
+#ifdef CONFIG_VT
 	case 'k':					    /* K -- SAK */
 		printk("SAK\n");
 		if (tty)
 			do_SAK(tty);
 		reset_vc(fg_console);
 		break;
+#endif
 	case 'b':					    /* B -- boot immediately */
 		printk("Resetting\n");
 		machine_restart(NULL);
@@ -131,8 +133,10 @@
 	default:					    /* Unknown: help */
 		if (kbd)
 			printk("unRaw ");
+#ifdef CONFIG_VT
 		if (tty)
 			printk("saK ");
+#endif
 		printk("Boot "
 #ifdef CONFIG_APM
 		       "Off "
diff -u --recursive --new-file v2.1.122/linux/drivers/char/tty_io.c linux/drivers/char/tty_io.c
--- v2.1.122/linux/drivers/char/tty_io.c	Wed Aug 26 11:37:37 1998
+++ linux/drivers/char/tty_io.c	Sat Sep 19 15:13:19 1998
@@ -126,11 +126,11 @@
 static unsigned int tty_poll(struct file *, poll_table *);
 static int tty_open(struct inode *, struct file *);
 static int tty_release(struct inode *, struct file *);
-static int tty_ioctl(struct inode * inode, struct file * file,
-		     unsigned int cmd, unsigned long arg);
+int tty_ioctl(struct inode * inode, struct file * file,
+	      unsigned int cmd, unsigned long arg);
 static int tty_fasync(int fd, struct file * filp, int on);
 #ifdef CONFIG_8xx
-extern long console_8xx_init(void);
+extern long console_8xx_init(long, long);
 extern int rs_8xx_init(void);
 #endif /* CONFIG_8xx */
 
@@ -640,7 +640,13 @@
 	size_t count)
 {
 	ssize_t ret = 0, written = 0;
-
+	struct inode *inode = file->f_dentry->d_inode;
+	
+	up(&inode->i_sem);
+	if (down_interruptible(&inode->i_atomic_write)) {
+		down(&inode->i_sem);
+		return -ERESTARTSYS;
+	}
 	for (;;) {
 		unsigned long size = PAGE_SIZE*2;
 		if (size > count)
@@ -663,6 +669,8 @@
 		file->f_dentry->d_inode->i_mtime = CURRENT_TIME;
 		ret = written;
 	}
+	up(&inode->i_atomic_write);
+	down(&inode->i_sem);
 	return ret;
 }
 
@@ -1604,8 +1612,8 @@
 /*
  * Split this up, as gcc can choke on it otherwise..
  */
-static int tty_ioctl(struct inode * inode, struct file * file,
-		     unsigned int cmd, unsigned long arg)
+int tty_ioctl(struct inode * inode, struct file * file,
+	      unsigned int cmd, unsigned long arg)
 {
 	struct tty_struct *tty, *real_tty;
 	int retval;
@@ -2004,7 +2012,11 @@
 	kmem_start = con_init(kmem_start);
 #endif
 #ifdef CONFIG_SERIAL_CONSOLE
+#ifdef CONFIG_8xx
+	kmem_start = console_8xx_init(kmem_start, kmem_end);
+#else 	
 	kmem_start = serial_console_init(kmem_start, kmem_end);
+#endif /* CONFIG_8xx */
 #endif
 	return kmem_start;
 }
diff -u --recursive --new-file v2.1.122/linux/drivers/char/vt.c linux/drivers/char/vt.c
--- v2.1.122/linux/drivers/char/vt.c	Thu Aug  6 14:06:31 1998
+++ linux/drivers/char/vt.c	Thu Sep 17 09:35:03 1998
@@ -74,45 +74,6 @@
 #define GPNUM (GPLAST - GPFIRST + 1)
 
 /*
- * This function is called when the size of the physical screen has been
- * changed.  If either the row or col argument is nonzero, set the appropriate
- * entry in each winsize structure for all the virtual consoles, then
- * send SIGWINCH to all processes with a virtual console as controlling
- * tty.
- */
-
-static int
-kd_size_changed(int row, int col)
-{
-  struct task_struct *p;
-  int i;
-
-  if ( !row && !col ) return 0;
-
-  for ( i = 0 ; i < MAX_NR_CONSOLES ; i++ )
-    {
-      if ( console_driver.table[i] )
-	{
-	  if ( row ) console_driver.table[i]->winsize.ws_row = row;
-	  if ( col ) console_driver.table[i]->winsize.ws_col = col;
-	}
-    }
-
-  read_lock(&tasklist_lock);
-  for_each_task(p)
-    {
-      if ( p->tty && MAJOR(p->tty->device) == TTY_MAJOR &&
-	   MINOR(p->tty->device) <= MAX_NR_CONSOLES && MINOR(p->tty->device) )
-	{
-	  send_sig(SIGWINCH, p, 1);
-	}
-    }
-  read_unlock(&tasklist_lock);
-
-  return 0;
-}
-
-/*
  * Generates sound of some frequency for some number of clock ticks
  *
  * If freq is 0, will turn off sound, else will turn it on for that time.
@@ -553,7 +514,7 @@
 		 * explicitly blank/unblank the screen if switching modes
 		 */
 		if (arg == KD_TEXT)
-			do_unblank_screen();
+			unblank_screen();
 		else
 			do_blank_screen(1);
 		return 0;
@@ -781,7 +742,7 @@
 		if (arg == 0 || arg > MAX_NR_CONSOLES)
 			return -ENXIO;
 		arg--;
-		i = vc_allocate(arg, 0);
+		i = vc_allocate(arg);
 		if (i)
 			return i;
 		set_console(arg);
@@ -833,7 +794,7 @@
 				 */
 				int newvt = vt_cons[console]->vt_newvt;
 				vt_cons[console]->vt_newvt = -1;
-				i = vc_allocate(newvt, 0);
+				i = vc_allocate(newvt);
 				if (i)
 					return i;
 				/*
@@ -893,8 +854,7 @@
 			return i;
 		__get_user(ll, &vtsizes->v_rows);
 		__get_user(cc, &vtsizes->v_cols);
-		i = vc_resize_all(ll, cc);
-		return i ? i : 	kd_size_changed(ll, cc);
+		return vc_resize_all(ll, cc);
 	}
 
 	case VT_RESIZEX:
@@ -942,12 +902,7 @@
 		if ( clin )
 		  video_font_height = clin;
 		
-		i = vc_resize_all(ll, cc);
-		if (i)
-			return i;
-
-		kd_size_changed(ll, cc);
-		return 0;
+		return vc_resize_all(ll, cc);
   	}
 
 	case PIO_FONT: {
@@ -1201,7 +1156,7 @@
 	 * unblank the screen later.
 	 */
 	old_vc_mode = vt_cons[fg_console]->vc_mode;
-	update_screen(new_console);
+	switch_screen(new_console);
 
 	/*
 	 * If this new console is under process control, send it a signal
@@ -1239,14 +1194,10 @@
 	if (old_vc_mode != vt_cons[new_console]->vc_mode)
 	{
 		if (vt_cons[new_console]->vc_mode == KD_TEXT)
-			do_unblank_screen();
+			unblank_screen();
 		else
 			do_blank_screen(1);
 	}
-
-	/* Set the colour palette for this VT */
-	if (vt_cons[new_console]->vc_mode == KD_TEXT)
-		set_palette() ;
 
 	/*
 	 * Wake anyone waiting for their VT to activate
diff -u --recursive --new-file v2.1.122/linux/drivers/net/ppp.c linux/drivers/net/ppp.c
--- v2.1.122/linux/drivers/net/ppp.c	Thu Aug 20 17:05:16 1998
+++ linux/drivers/net/ppp.c	Tue Sep 22 21:52:33 1998
@@ -1386,6 +1386,7 @@
 			break;
 		if (temp_i < PPP_MRU)
 			temp_i = PPP_MRU;
+		ppp->mru = temp_i;
 		if (ppp->flags & SC_DEBUG)
 			printk(KERN_INFO
 			       "ppp_ioctl: set mru to %x\n", temp_i);
diff -u --recursive --new-file v2.1.122/linux/drivers/scsi/aic7xxx.c linux/drivers/scsi/aic7xxx.c
--- v2.1.122/linux/drivers/scsi/aic7xxx.c	Thu Aug  6 14:06:32 1998
+++ linux/drivers/scsi/aic7xxx.c	Sun Sep 20 16:21:05 1998
@@ -6791,6 +6791,7 @@
       p->type = temp->type;
       p->unpause = temp->unpause;
       p->pause = temp->pause;
+      p->pdev = temp->pdev;
       p->pci_bus = temp->pci_bus;
       p->pci_device_fn = temp->pci_device_fn;
       p->bios_address = temp->bios_address;
diff -u --recursive --new-file v2.1.122/linux/drivers/scsi/imm.c linux/drivers/scsi/imm.c
--- v2.1.122/linux/drivers/scsi/imm.c	Thu Sep 17 17:53:36 1998
+++ linux/drivers/scsi/imm.c	Tue Sep 22 16:42:14 1998
@@ -65,8 +65,7 @@
 
 #define IMM_BASE(x)	imm_hosts[(x)].base
 
-int base[NO_HOSTS] =
-{0x03bc, 0x0378, 0x0278, 0x0000};
+int parbus_base[NO_HOSTS] = {0x03bc, 0x0378, 0x0278, 0x0000};
 
 void imm_wakeup(void *ref)
 {
diff -u --recursive --new-file v2.1.122/linux/drivers/scsi/ppa.c linux/drivers/scsi/ppa.c
--- v2.1.122/linux/drivers/scsi/ppa.c	Thu Sep 17 17:53:36 1998
+++ linux/drivers/scsi/ppa.c	Sun Sep 20 18:00:15 1998
@@ -1027,6 +1027,7 @@
 		retv--;
 
 	    if (retv)
+	    {
 		if ((jiffies - tmp->jstart) > (1 * HZ)) {
 		    printk("ppa: Parallel port cable is unplugged!!\n");
 		    ppa_fail(host_no, DID_BUS_BUSY);
@@ -1035,6 +1036,7 @@
 		    ppa_disconnect(host_no);
 		    return 1;	/* Try again in a jiffy */
 		}
+	    }
 	    cmd->SCp.phase++;
 	}
 
diff -u --recursive --new-file v2.1.122/linux/drivers/scsi/st.c linux/drivers/scsi/st.c
--- v2.1.122/linux/drivers/scsi/st.c	Wed Sep  9 14:51:09 1998
+++ linux/drivers/scsi/st.c	Sun Sep 27 19:05:47 1998
@@ -890,6 +890,9 @@
     kdev_t devt = inode->i_rdev;
     int dev;
 
+    if (filp->f_count > 1)
+	return 0;
+
     dev = TAPE_NR(devt);
     STp = &(scsi_tapes[dev]);
     STm = &(STp->modes[STp->current_mode]);
diff -u --recursive --new-file v2.1.122/linux/drivers/video/vgacon.c linux/drivers/video/vgacon.c
--- v2.1.122/linux/drivers/video/vgacon.c	Tue Aug 18 22:02:05 1998
+++ linux/drivers/video/vgacon.c	Thu Sep 17 09:35:03 1998
@@ -64,8 +64,6 @@
  */
 #undef TRIDENT_GLITCH
 
-#undef VGA_CAN_DO_64KB
-
 #define dac_reg		0x3c8
 #define dac_val		0x3c9
 #define attrib_port	0x3c0
@@ -115,6 +113,7 @@
 static int	       vga_is_gfx;
 static int	       vga_512_chars;
 static int	       vga_video_font_height;
+static unsigned int    vga_rolled_over = 0;
 
 
 void no_scroll(char *str, int *ints)
@@ -190,7 +189,7 @@
 			display_desc = "*MDA";
 			request_region(0x3b0,12,"mda");
 			request_region(0x3bf, 1,"mda");
-			vga_video_font_height = 16;
+			vga_video_font_height = 14;
 		}
 	}
 	else				/* If not, it is color. */
@@ -453,9 +452,8 @@
 	 */
 	vga_video_num_columns = c->vc_cols;
 	vga_video_num_lines = c->vc_rows;
-	if (vga_is_gfx)
-		return 1;
-	scr_memcpyw_to((u16 *) c->vc_origin, (u16 *) c->vc_screenbuf, c->vc_screenbuf_size);
+	if (!vga_is_gfx)
+		scr_memcpyw_to((u16 *) c->vc_origin, (u16 *) c->vc_screenbuf, c->vc_screenbuf_size);
 	return 0;	/* Redrawing not needed */
 }
 
@@ -474,8 +472,7 @@
 static int vgacon_set_palette(struct vc_data *c, unsigned char *table)
 {
 #ifdef CAN_LOAD_PALETTE
-
-	if (vga_video_type != VIDEO_TYPE_VGAC || vga_palette_blanked)
+	if (vga_video_type != VIDEO_TYPE_VGAC || vga_palette_blanked || !CON_IS_VISIBLE(c))
 		return -EINVAL;
 	vga_set_palette(c, table);
 	return 0;
@@ -637,10 +634,11 @@
 			vga_palette_blanked = 1;
 			return 0;
 		}
-		scr_memsetw((void *)vga_vram_base, BLANK, vc_cons[0].d->vc_screenbuf_size);
+		vgacon_set_origin(c);
+		scr_memsetw((void *)vga_vram_base, BLANK, c->vc_screenbuf_size);
 		return 1;
 	case -1:			/* Entering graphic mode */
-		scr_memsetw((void *)vga_vram_base, BLANK, vc_cons[0].d->vc_screenbuf_size);
+		scr_memsetw((void *)vga_vram_base, BLANK, c->vc_screenbuf_size);
 		vga_is_gfx = 1;
 		return 1;
 	default:			/* VESA blanking */
@@ -899,14 +897,24 @@
 	if (!lines)			/* Turn scrollback off */
 		c->vc_visible_origin = c->vc_origin;
 	else {
-		int p = c->vc_visible_origin - vga_vram_base;
-		int margin = c->vc_rows/4 * c->vc_size_row;
-		p += lines * c->vc_size_row;
-		if (lines < 0 && p < margin)
+		int vram_size = vga_vram_end - vga_vram_base;
+		int margin = c->vc_size_row * 4;
+		int ul, we, p, st;
+
+		if (vga_rolled_over > (c->vc_scr_end - vga_vram_base) + margin) {
+			ul = c->vc_scr_end - vga_vram_base;
+			we = vga_rolled_over + c->vc_size_row;
+		} else {
+			ul = 0;
+			we = vram_size;
+		}
+		p = (c->vc_visible_origin - vga_vram_base - ul + we) % we + lines * c->vc_size_row;
+		st = (c->vc_origin - vga_vram_base - ul + we) % we;
+		if (p < margin)
 			p = 0;
-		c->vc_visible_origin = p + vga_vram_base;
-		if (lines > 0 && c->vc_visible_origin > c->vc_origin - margin)
-			c->vc_visible_origin = c->vc_origin;
+		if (p > st - margin)
+			p = st;
+		c->vc_visible_origin = vga_vram_base + (p + ul) % we;
 	}
 	vga_set_mem_top(c);
 	return 1;
@@ -919,6 +927,7 @@
 		return 0;
 	c->vc_origin = c->vc_visible_origin = vga_vram_base;
 	vga_set_mem_top(c);
+	vga_rolled_over = 0;
 	return 1;
 }
 
@@ -935,9 +944,8 @@
 		c->vc_x = ORIG_X;
 		c->vc_y = ORIG_Y;
 	}
-	if (vga_is_gfx)
-		return;
-	scr_memcpyw_from((u16 *) c->vc_screenbuf, (u16 *) c->vc_origin, c->vc_screenbuf_size);
+	if (!vga_is_gfx)
+		scr_memcpyw_from((u16 *) c->vc_screenbuf, (u16 *) c->vc_origin, c->vc_screenbuf_size);
 }
 
 static int vgacon_scroll(struct vc_data *c, int t, int b, int dir, int lines)
@@ -962,6 +970,7 @@
 				    (u16 *)(oldo + delta),
 				    c->vc_screenbuf_size - delta);
 			c->vc_origin = vga_vram_base;
+			vga_rolled_over = oldo - vga_vram_base;
 		} else
 			c->vc_origin += delta;
 		scr_memsetw((u16 *)(c->vc_origin + c->vc_screenbuf_size - delta), c->vc_video_erase_char, delta);
@@ -971,6 +980,7 @@
 				     (u16 *)oldo,
 				     c->vc_screenbuf_size - delta);
 			c->vc_origin = vga_vram_end - c->vc_screenbuf_size;
+			vga_rolled_over = 0;
 		} else
 			c->vc_origin -= delta;
 		c->vc_scr_end = c->vc_origin + c->vc_screenbuf_size;
diff -u --recursive --new-file v2.1.122/linux/fs/Config.in linux/fs/Config.in
--- v2.1.122/linux/fs/Config.in	Sat Sep  5 16:46:41 1998
+++ linux/fs/Config.in	Sun Sep 20 23:23:47 1998
@@ -67,11 +67,9 @@
   define_bool CONFIG_AMIGA_PARTITION y
 fi
 tristate 'UFS filesystem support' CONFIG_UFS_FS
-if [ "$CONFIG_UFS_FS" != "n" ]; then
-  bool 'BSD disklabel (FreeBSD partition tables) support' CONFIG_BSD_DISKLABEL
-  bool 'SMD disklabel (Sun partition tables) support' CONFIG_SMD_DISKLABEL
-  bool 'Solaris (x86) partition table support' CONFIG_SOLARIS_X86_PARTITION
-fi
+bool 'BSD disklabel (BSD partition tables) support' CONFIG_BSD_DISKLABEL
+bool 'SMD disklabel (Sun partition tables) support' CONFIG_SMD_DISKLABEL
+bool 'Solaris (x86) partition table support' CONFIG_SOLARIS_X86_PARTITION
 if [ "$CONFIG_UNIX98_PTYS" = "y" ]; then
   tristate '/dev/pts filesystem for Unix98 PTYs' CONFIG_DEVPTS_FS
 fi
diff -u --recursive --new-file v2.1.122/linux/fs/affs/symlink.c linux/fs/affs/symlink.c
--- v2.1.122/linux/fs/affs/symlink.c	Mon Jan 12 14:46:24 1998
+++ linux/fs/affs/symlink.c	Sat Sep 19 13:39:45 1998
@@ -20,7 +20,7 @@
 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
 
 static int		 affs_readlink(struct dentry *, char *, int);
-static struct dentry	*affs_follow_link(struct dentry *dentry, struct dentry *base);
+static struct dentry	*affs_follow_link(struct dentry *dentry, struct dentry *base, unsigned int);
 
 struct inode_operations affs_symlink_inode_operations = {
 	NULL,			/* no file-operations */
@@ -98,7 +98,7 @@
 }
 
 static struct dentry *
-affs_follow_link(struct dentry *dentry, struct dentry *base)
+affs_follow_link(struct dentry *dentry, struct dentry *base, unsigned int follow)
 {
 	struct inode *inode = dentry->d_inode;
 	struct buffer_head	*bh;
@@ -150,7 +150,7 @@
 	}
 	buffer[i] = '\0';
 	affs_brelse(bh);
-	base = lookup_dentry(buffer,base,1);
+	base = lookup_dentry(buffer,base,follow);
 	kfree(buffer);
 	return base;
 }
diff -u --recursive --new-file v2.1.122/linux/fs/autofs/symlink.c linux/fs/autofs/symlink.c
--- v2.1.122/linux/fs/autofs/symlink.c	Sun Jan  4 00:53:43 1998
+++ linux/fs/autofs/symlink.c	Sat Sep 19 13:41:02 1998
@@ -27,12 +27,13 @@
 }
 
 static struct dentry * autofs_follow_link(struct dentry *dentry,
-					struct dentry *base)
+					struct dentry *base,
+					unsigned int follow)
 {
 	struct autofs_symlink *sl;
 
 	sl = (struct autofs_symlink *)dentry->d_inode->u.generic_ip;
-	return lookup_dentry(sl->data, base, 1);
+	return lookup_dentry(sl->data, base, follow);
 }
 
 struct inode_operations autofs_symlink_inode_operations = {
diff -u --recursive --new-file v2.1.122/linux/fs/bad_inode.c linux/fs/bad_inode.c
--- v2.1.122/linux/fs/bad_inode.c	Wed Aug 26 11:37:40 1998
+++ linux/fs/bad_inode.c	Mon Sep 21 14:37:20 1998
@@ -15,7 +15,7 @@
  * so that a bad root inode can at least be unmounted. To do this
  * we must dput() the base and return the dentry with a dget().
  */
-static struct dentry * bad_follow_link(struct dentry *dent, struct dentry *base)
+static struct dentry * bad_follow_link(struct dentry *dent, struct dentry *base, unsigned int follow)
 {
 	dput(base);
 	return dget(dent);
diff -u --recursive --new-file v2.1.122/linux/fs/coda/symlink.c linux/fs/coda/symlink.c
--- v2.1.122/linux/fs/coda/symlink.c	Thu May  7 22:51:53 1998
+++ linux/fs/coda/symlink.c	Sat Sep 19 13:41:28 1998
@@ -26,7 +26,7 @@
 #include <linux/coda_proc.h>
 
 static int coda_readlink(struct dentry *de, char *buffer, int length);
-static struct dentry *coda_follow_link(struct dentry *, struct dentry *);
+static struct dentry *coda_follow_link(struct dentry *, struct dentry *, unsigned int);
 
 struct inode_operations coda_symlink_inode_operations = {
 	NULL,			/* no file-operations */
@@ -86,7 +86,8 @@
 }
 
 static struct dentry *coda_follow_link(struct dentry *de, 
-				       struct dentry *base)
+				       struct dentry *base,
+				       unsigned int follow)
 {
 	struct inode *inode = de->d_inode;
 	int error;
@@ -116,7 +117,7 @@
 	memcpy(path, mem, len);
 	path[len] = 0;
 
-	base = lookup_dentry(path, base, 1);
+	base = lookup_dentry(path, base, follow);
 	kfree(path);
 	return base;
 }
diff -u --recursive --new-file v2.1.122/linux/fs/dquot.c linux/fs/dquot.c
--- v2.1.122/linux/fs/dquot.c	Wed Aug 26 11:37:40 1998
+++ linux/fs/dquot.c	Fri Sep 25 16:24:56 1998
@@ -18,6 +18,9 @@
  *
  * Fixes:   Dmitry Gorodchanin <pgmdsg@ibi.com>, 11 Feb 96
  *
+ *		Revised list management to avoid races
+ *		-- Bill Hawes, <whawes@star.net>, 9/98
+ *
  * (C) Copyright 1994 - 1997 Marco van Wieringen 
  */
 
@@ -50,12 +53,29 @@
 
 static kmem_cache_t *dquot_cachep;
 
-static struct dquot *dquot_hash[NR_DQHASH];
-static struct free_dquot_queue {
-	struct dquot *head;
-	struct dquot **last;
-} free_dquots = { NULL, &free_dquots.head };
+/*
+ * Dquot List Management:
+ * The quota code uses three lists for dquot management: the inuse_list,
+ * free_dquots, and dquot_hash[] array. A single dquot structure may be
+ * on all three lists, depending on its current state.
+ *
+ * All dquots are placed on the inuse_list when first created, and this
+ * list is used for the sync and invalidate operations, which must look
+ * at every dquot.
+ *
+ * Unused dquots (dq_count == 0) are added to the free_dquots list when
+ * freed, and this list is searched whenever we need an available dquot.
+ * Dquots are removed from the list as soon as they are used again, and
+ * nr_free_dquots gives the number of dquots on the list.
+ *
+ * Dquots with a specific identity (device, type and id) are placed on
+ * one of the dquot_hash[] hash chains. The provides an efficient search
+ * mechanism to lcoate a specific dquot.
+ */
+
 static struct dquot *inuse_list = NULL;
+LIST_HEAD(free_dquots);
+static struct dquot *dquot_hash[NR_DQHASH];
 static int dquot_updating[NR_DQHASH];
 
 static struct dqstats dqstats;
@@ -128,37 +148,29 @@
 	return dquot;
 }
 
+/* Add a dquot to the head of the free list */
 static inline void put_dquot_head(struct dquot *dquot)
 {
-	if ((dquot->dq_next = free_dquots.head) != NULL)
-		free_dquots.head->dq_pprev = &dquot->dq_next;
-	else
-		free_dquots.last = &dquot->dq_next;
-	free_dquots.head = dquot;
-	dquot->dq_pprev = &free_dquots.head;
+	list_add(&dquot->dq_free, &free_dquots);
 	nr_free_dquots++;
 }
 
+/* Add a dquot to the tail of the free list */
 static inline void put_dquot_last(struct dquot *dquot)
 {
-	dquot->dq_next = NULL;
-	dquot->dq_pprev = free_dquots.last;
-	*free_dquots.last = dquot;
-	free_dquots.last = &dquot->dq_next;
+	list_add(&dquot->dq_free, free_dquots.prev);
 	nr_free_dquots++;
 }
 
 static inline void remove_free_dquot(struct dquot *dquot)
 {
-	if (dquot->dq_pprev) {
-		if (dquot->dq_next)
-			dquot->dq_next->dq_pprev = dquot->dq_pprev;
-		else
-			free_dquots.last = dquot->dq_pprev;
-		*dquot->dq_pprev = dquot->dq_next;
-		dquot->dq_pprev = NULL;
-		nr_free_dquots--;
-	}
+	/* sanity check */
+	if (list_empty(&dquot->dq_free)) {
+		printk("remove_free_dquot: dquot not on free list??\n");
+	}
+	list_del(&dquot->dq_free);
+	INIT_LIST_HEAD(&dquot->dq_free);
+	nr_free_dquots--;
 }
 
 static inline void put_inuse(struct dquot *dquot)
@@ -169,6 +181,7 @@
 	dquot->dq_pprev = &inuse_list;
 }
 
+#if 0	/* currently not needed */
 static inline void remove_inuse(struct dquot *dquot)
 {
 	if (dquot->dq_pprev) {
@@ -178,6 +191,7 @@
 		dquot->dq_pprev = NULL;
 	}
 }
+#endif
 
 static void __wait_on_dquot(struct dquot *dquot)
 {
@@ -187,7 +201,6 @@
 repeat:
 	current->state = TASK_UNINTERRUPTIBLE;
 	if (dquot->dq_flags & DQ_LOCKED) {
-		dquot->dq_flags |= DQ_WANT;
 		schedule();
 		goto repeat;
 	}
@@ -210,24 +223,16 @@
 static inline void unlock_dquot(struct dquot *dquot)
 {
 	dquot->dq_flags &= ~DQ_LOCKED;
-	if (dquot->dq_flags & DQ_WANT) {
-		dquot->dq_flags &= ~DQ_WANT;
-		wake_up(&dquot->dq_wait);
-	}
+	wake_up(&dquot->dq_wait);
 }
 
 static void write_dquot(struct dquot *dquot)
 {
-	short type;
-	struct file *filp;
+	short type = dquot->dq_type;
+	struct file *filp = dquot->dq_mnt->mnt_dquot.files[type];
 	mm_segment_t fs;
 	loff_t offset;
-
-	type = dquot->dq_type;
-	filp = dquot->dq_mnt->mnt_dquot.files[type];
-
-	if (!(dquot->dq_flags & DQ_MOD) || (filp == (struct file *)NULL))
-		return;
+	ssize_t ret;
 
 	lock_dquot(dquot);
 	down(&dquot->dq_mnt->mnt_dquot.semaphore);
@@ -235,8 +240,18 @@
 	fs = get_fs();
 	set_fs(KERNEL_DS);
 
-	if (filp->f_op->write(filp, (char *)&dquot->dq_dqb, sizeof(struct dqblk), &offset) == sizeof(struct dqblk))
-		dquot->dq_flags &= ~DQ_MOD;
+	/*
+	 * Note: clear the DQ_MOD flag unconditionally,
+	 * so we don't loop forever on failure.
+	 */
+	dquot->dq_flags &= ~DQ_MOD;
+	ret = 0;
+	if (filp)
+		ret = filp->f_op->write(filp, (char *)&dquot->dq_dqb, 
+					sizeof(struct dqblk), &offset);
+	if (ret != sizeof(struct dqblk))
+		printk(KERN_WARNING "VFS: dquota write failed on dev %s\n",
+			kdevname(dquot->dq_dev));
 
 	up(&dquot->dq_mnt->mnt_dquot.semaphore);
 	set_fs(fs);
@@ -274,73 +289,86 @@
 	dqstats.reads++;
 }
 
+/*
+ * Unhash and selectively clear the dquot structure,
+ * but preserve the use count, list pointers, and
+ * wait queue.
+ */
 void clear_dquot(struct dquot *dquot)
 {
-        struct wait_queue *wait;
-
-        /* So we don't disappear. */
-        dquot->dq_count++;
-
-        wait_on_dquot(dquot);
-
-        if (--dquot->dq_count > 0)
-                remove_inuse(dquot);
-        else
-                remove_free_dquot(dquot);
+	/* unhash it first */
         unhash_dquot(dquot);
-        wait = dquot->dq_wait;
-        memset(dquot, 0, sizeof(*dquot)); barrier();
-        dquot->dq_wait = wait;
-        put_dquot_head(dquot);
+        dquot->dq_mnt = NULL;
+        dquot->dq_flags = 0;
+        dquot->dq_referenced = 0;
+        memset(&dquot->dq_dqb, 0, sizeof(struct dqblk));
 }
 
 void invalidate_dquots(kdev_t dev, short type)
 {
-	struct dquot *dquot, *next = NULL;
-	int pass = 0;
+	struct dquot *dquot, *next = inuse_list;
+	int need_restart;
 
-	dquot = free_dquots.head;
-repeat:
-	while (dquot) {
+restart:
+	need_restart = 0;
+	while ((dquot = next) != NULL) {
 		next = dquot->dq_next;
-		if (dquot->dq_dev != dev || dquot->dq_type != type)
-			goto next;
-		clear_dquot(dquot);
-	next:
-		dquot = next;
-	}
+		if (dquot->dq_dev != dev)
+			continue;
+		if (dquot->dq_type != type)
+			continue;
+		if (dquot->dq_flags & DQ_LOCKED) {
+			__wait_on_dquot(dquot);
 
-	if (pass == 0) {
-		dquot = inuse_list;
-		pass = 1;
-		goto repeat;
+			/* Set the flag for another pass. */
+			need_restart = 1;
+			/*
+			 * Make sure it's still the same dquot.
+			 */
+			if (dquot->dq_dev != dev)
+				continue;
+			if (dquot->dq_type != type)
+				continue;
+		}
+		clear_dquot(dquot);
 	}
+	/*
+	 * If anything blocked, restart the operation
+	 * to ensure we don't miss any dquots.
+	 */ 
+	if (need_restart)
+		goto restart;
 }
 
 int sync_dquots(kdev_t dev, short type)
 {
-	struct dquot *dquot, *next;
-	int pass = 0;
+	struct dquot *dquot, *next = inuse_list;
+	int need_restart;
 
-	dquot = free_dquots.head;
-repeat:
-	while (dquot) {
+restart:
+	need_restart = 0;
+	while ((dquot = next) != NULL) {
 		next = dquot->dq_next;
-		if ((dev && dquot->dq_dev != dev) ||
-                    (type != -1 && dquot->dq_type != type))
-			goto next;
+		if (dev && dquot->dq_dev != dev)
+			continue;
+                if (type != -1 && dquot->dq_type != type)
+			continue;
+		if (!(dquot->dq_flags & (DQ_LOCKED | DQ_MOD)))
+			continue;
+
 		wait_on_dquot(dquot);
 		if (dquot->dq_flags & DQ_MOD)
 			write_dquot(dquot);
-	next:
-		dquot = next;
+		/* Set the flag for another pass. */
+		need_restart = 1;
 	}
+	/*
+	 * If anything blocked, restart the operation
+	 * to ensure we don't miss any dquots.
+	 */ 
+	if (need_restart)
+		goto restart;
 
-	if (pass == 0) {
-		dquot = inuse_list;
-		pass = 1;
-		goto repeat;
-	}
 	dqstats.syncs++;
 	return(0);
 }
@@ -349,40 +377,41 @@
 {
 	if (!dquot)
 		return;
+	if (!dquot->dq_count) {
+		printk("VFS: dqput: trying to free free dquot\n");
+		printk("VFS: device %s, dquot of %s %d\n",
+			kdevname(dquot->dq_dev), quotatypes[dquot->dq_type],
+			dquot->dq_id);
+		return;
+	}
 
 	/*
 	 * If the dq_mnt pointer isn't initialized this entry needs no
-	 * checking and doesn't need to be written. It just an empty
+	 * checking and doesn't need to be written. It's just an empty
 	 * dquot that is put back on to the freelist.
 	 */
 	if (dquot->dq_mnt != (struct vfsmount *)NULL) {
 		dqstats.drops++;
-		wait_on_dquot(dquot);
-
-		if (!dquot->dq_count) {
-			printk("VFS: dqput: trying to free free dquot\n");
-			printk("VFS: device %s, dquot of %s %d\n", kdevname(dquot->dq_dev),
-			       quotatypes[dquot->dq_type], dquot->dq_id);
-			return;
-		}
 we_slept:
+		wait_on_dquot(dquot);
 		if (dquot->dq_count > 1) {
 			dquot->dq_count--;
 			return;
-		} else {
-			wake_up(&dquot_wait);
-
-			if (dquot->dq_flags & DQ_MOD) {
-				write_dquot(dquot);
-				wait_on_dquot(dquot);
-				goto we_slept;
-			}
+		}
+		if (dquot->dq_flags & DQ_MOD) {
+			write_dquot(dquot);
+			goto we_slept;
 		}
 	}
 
+	/* sanity check */
+	if (!list_empty(&dquot->dq_free)) {
+		printk("dqput: dquot already on free list??\n");
+	}
 	if (--dquot->dq_count == 0) {
-		remove_inuse(dquot);
-		put_dquot_last(dquot); /* Place at end of LRU free queue */
+ 		/* Place at end of LRU free queue */
+		put_dquot_last(dquot);
+		wake_up(&dquot_wait);
 	}
 
 	return;
@@ -400,45 +429,43 @@
 
 		nr_dquots++;
 		memset((caddr_t)dquot, 0, sizeof(struct dquot));
+		/* all dquots go on the inuse_list */
+		put_inuse(dquot);
 		put_dquot_head(dquot);
 		cnt--;
 	}
 }
 
-static struct dquot *find_best_candidate_weighted(struct dquot *dquot)
+static struct dquot *find_best_candidate_weighted(void)
 {
-	int limit, myscore;
-	unsigned long bestscore;
-	struct dquot *best = NULL;
-
-	if (dquot) {
-		bestscore = 2147483647;
-		limit = nr_free_dquots >> 2;
-		do {
-			if (!((dquot->dq_flags & DQ_LOCKED) || (dquot->dq_flags & DQ_MOD))) {
-				myscore = dquot->dq_referenced;
-				if (myscore < bestscore) {
-					bestscore = myscore;
-					best = dquot;
-				}
-			}
-			dquot = dquot->dq_next;
-		} while (dquot && --limit);
+	struct list_head *tmp = &free_dquots;
+	struct dquot *dquot, *best = NULL;
+	unsigned long myscore, bestscore = ~0U;
+	int limit = (nr_free_dquots > 128) ? nr_free_dquots >> 2 : 32;
+
+	while ((tmp = tmp->next) != &free_dquots && --limit) {
+		dquot = list_entry(tmp, struct dquot, dq_free);
+		if (dquot->dq_flags & (DQ_LOCKED | DQ_MOD))
+			continue;
+		myscore = dquot->dq_referenced;
+		if (myscore < bestscore) {
+			bestscore = myscore;
+			best = dquot;
+		}
 	}
 	return best;
 }
 
-static inline struct dquot *find_best_free(struct dquot *dquot)
+static inline struct dquot *find_best_free(void)
 {
-	int limit;
+	struct list_head *tmp = &free_dquots;
+	struct dquot *dquot;
+	int limit = (nr_free_dquots > 1024) ? nr_free_dquots >> 5 : 32;
 
-	if (dquot) {
-		limit = nr_free_dquots >> 5;
-		do {
-			if (dquot->dq_referenced == 0)
-				return dquot;
-			dquot = dquot->dq_next;
-		} while (dquot && --limit);
+	while ((tmp = tmp->next) != &free_dquots && --limit) {
+		dquot = list_entry(tmp, struct dquot, dq_free);
+		if (dquot->dq_referenced == 0)
+			return dquot;
 	}
 	return NULL;
 }
@@ -446,42 +473,56 @@
 struct dquot *get_empty_dquot(void)
 {
 	struct dquot *dquot;
+	int count;
 
 repeat:
-	dquot = find_best_free(free_dquots.head);
+	dquot = find_best_free();
 	if (!dquot)
 		goto pressure;
 got_it:
-	dquot->dq_count++;
-	wait_on_dquot(dquot);
-	unhash_dquot(dquot);
-	remove_free_dquot(dquot);
+	if (dquot->dq_flags & (DQ_LOCKED | DQ_MOD)) {
+		wait_on_dquot(dquot);
+		if (dquot->dq_flags & DQ_MOD)
+			write_dquot(dquot);
+		/*
+		 * The dquot may be back in use now, so we
+		 * must recheck the free list.
+		 */
+		goto repeat;
+	}
+	/* sanity check ... */
+	if (dquot->dq_count != 0)
+		printk(KERN_ERR "VFS: free dquot count=%d\n", dquot->dq_count);
 
-	memset(dquot, 0, sizeof(*dquot));
+	remove_free_dquot(dquot);
 	dquot->dq_count = 1;
-
-	put_inuse(dquot);
+	/* unhash and selectively clear the structure */
+	clear_dquot(dquot);
 	return dquot;
+
 pressure:
 	if (nr_dquots < max_dquots) {
 		grow_dquots();
 		goto repeat;
 	}
 
-	dquot = find_best_candidate_weighted(free_dquots.head);
-	if (!dquot) {
-		printk("VFS: No free dquots, contact mvw@planets.elm.net\n");
-		sleep_on(&dquot_wait);
-		goto repeat;
-	}
-	if (dquot->dq_flags & DQ_LOCKED) {
-		wait_on_dquot(dquot);
-		goto repeat;
-	} else if (dquot->dq_flags & DQ_MOD) {
-		write_dquot(dquot);
+	dquot = find_best_candidate_weighted();
+	if (dquot)
+		goto got_it;
+	/*
+	 * Try pruning the dcache to free up some dquots ...
+	 */
+	count = select_dcache(128, 0);
+	if (count) {
+		printk(KERN_DEBUG "get_empty_dquot: pruning %d\n", count);
+		prune_dcache(count);
+		free_inode_memory(count);
 		goto repeat;
 	}
-	goto got_it;
+
+	printk("VFS: No free dquots, contact mvw@planets.elm.net\n");
+	sleep_on(&dquot_wait);
+	goto repeat;
 }
 
 struct dquot *dqget(kdev_t dev, unsigned int id, short type)
@@ -507,12 +548,12 @@
         	dquot->dq_type = type;
         	dquot->dq_dev = dev;
         	dquot->dq_mnt = vfsmnt;
-        	read_dquot(dquot);
+		/* hash it first so it can be found */
 		hash_dquot(dquot);
+        	read_dquot(dquot);
 	} else {
 		if (!dquot->dq_count++) {
 			remove_free_dquot(dquot);
-			put_inuse(dquot);
 		} else
 			dqstats.cache_hits++;
 		wait_on_dquot(dquot);
@@ -546,6 +587,7 @@
 		inode = filp->f_dentry->d_inode;
 		if (!inode)
 			continue;
+		/* N.B. race problem -- filp could become unused */
 		if (filp->f_mode & FMODE_WRITE) {
 			sb->dq_op->initialize(inode, type);
 			inode->i_flags |= S_QUOTA;
@@ -558,10 +600,16 @@
 	struct super_block *sb = get_super(dev);
 	struct file *filp;
 	struct inode *inode;
+	struct dquot *dquot;
+	int cnt;
 
 	if (!sb || !sb->dq_op)
 		return;	/* nothing to do */
 
+restart:
+	/* free any quota for unused dentries */
+	shrink_dcache_sb(sb);
+
 	for (filp = inuse_filps; filp; filp = filp->f_next) {
 		if (!filp->f_dentry)
 			continue;
@@ -570,10 +618,25 @@
 		inode = filp->f_dentry->d_inode;
 		if (!inode)
 			continue;
+		/*
+		 * Note: we restart after each blocking operation,
+		 * as the inuse_filps list may have changed.
+		 */
 		if (IS_QUOTAINIT(inode)) {
-			sb->dq_op->drop(inode);
+			dquot = inode->i_dquot[type];
 			inode->i_dquot[type] = NODQUOT;
+			/* any other quota in use? */
+			for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+				if (inode->i_dquot[cnt] != NODQUOT)
+					goto put_it;
+			}
 			inode->i_flags &= ~S_QUOTA;
+		put_it:
+			if (dquot != NODQUOT) {
+				dqput(dquot);
+				/* we may have blocked ... */
+				goto restart;
+			}
 		}
 	}
 }
@@ -638,7 +701,8 @@
 	return(initiator == 0 && dquot->dq_mnt->mnt_dquot.rsquash[dquot->dq_type] == 0);
 }
 
-static int check_idq(struct dquot *dquot, short type, u_long short inodes, uid_t initiator, struct tty_struct *tty)
+static int check_idq(struct dquot *dquot, short type, u_long short inodes, uid_t initiator, 
+			struct tty_struct *tty)
 {
 	if (inodes <= 0 || dquot->dq_flags & DQ_FAKE)
 		return(QUOTA_OK);
@@ -682,7 +746,8 @@
 	return(QUOTA_OK);
 }
 
-static int check_bdq(struct dquot *dquot, short type, u_long blocks, uid_t initiator, struct tty_struct *tty, char warn)
+static int check_bdq(struct dquot *dquot, short type, u_long blocks, uid_t initiator, 
+			struct tty_struct *tty, char warn)
 {
 	if (blocks <= 0 || dquot->dq_flags & DQ_FAKE)
 		return(QUOTA_OK);
@@ -732,15 +797,15 @@
  */ 
 static int set_dqblk(kdev_t dev, int id, short type, int flags, struct dqblk *dqblk)
 {
-	int error;
 	struct dquot *dquot;
+	int error = -EFAULT;
 	struct dqblk dq_dqblk;
 
 	if (dqblk == (struct dqblk *)NULL)
-		return(-EFAULT);
+		return error;
 
 	if (flags & QUOTA_SYSCALL) {
-		if ((error = copy_from_user((caddr_t)&dq_dqblk, (caddr_t)dqblk, sizeof(struct dqblk))) != 0)
+		if (copy_from_user(&dq_dqblk, dqblk, sizeof(struct dqblk)))
 			return(error);
 	} else
 		memcpy((caddr_t)&dq_dqblk, (caddr_t)dqblk, sizeof(struct dqblk));
@@ -793,26 +858,35 @@
 static int get_quota(kdev_t dev, int id, short type, struct dqblk *dqblk)
 {
 	struct dquot *dquot;
-	int error;
+	int error = -ESRCH;
 
-	if (dev_has_quota_enabled(dev, type)) {
-		if (dqblk == (struct dqblk *)NULL)
-			return(-EFAULT);
-
-		if ((dquot = dqget(dev, id, type)) != NODQUOT) {
-			error = copy_to_user((caddr_t)dqblk, (caddr_t)&dquot->dq_dqb, sizeof(struct dqblk));
-			dqput(dquot);
-			return(error);
-		}
-	}
-	return(-ESRCH);
+	if (!dev_has_quota_enabled(dev, type))
+		goto out;
+	dquot = dqget(dev, id, type);
+	if (dquot == NODQUOT)
+		goto out;
+
+	error = -EFAULT;
+	if (dqblk && !copy_to_user(dqblk, &dquot->dq_dqb, sizeof(struct dqblk)))
+		error = 0;
+	dqput(dquot);
+out:
+	return error;
 }
 
 static int get_stats(caddr_t addr)
 {
+	int error = -EFAULT;
+	struct dqstats stats;
+
 	dqstats.allocated_dquots = nr_dquots;
 	dqstats.free_dquots = nr_free_dquots;
-	return(copy_to_user(addr, (caddr_t)&dqstats, sizeof(struct dqstats)));
+
+	/* make a copy, in case we page-fault in user space */
+	memcpy(&stats, &dqstats, sizeof(struct dqstats));
+	if (!copy_to_user(addr, &stats, sizeof(struct dqstats)))
+		error = 0;
+	return error;
 }
 
 static int quota_root_squash(kdev_t dev, short type, int *addr)
@@ -823,11 +897,12 @@
 	if ((vfsmnt = lookup_vfsmnt(dev)) == (struct vfsmount *)NULL)
 		return(-ENODEV);
 
-	if ((error = copy_from_user((caddr_t)&new_value, (caddr_t)addr, sizeof(int))) != 0)
-		return(error);
-
-	vfsmnt->mnt_dquot.rsquash[type] = new_value;
-	return(0);
+	error = -EFAULT;
+	if (!copy_from_user(&new_value, addr, sizeof(int))) {
+		vfsmnt->mnt_dquot.rsquash[type] = new_value;
+		error = 0;
+	}
+	return error;
 }
 
 /*
@@ -856,6 +931,8 @@
 
 /*
  * Externally referenced functions through dquot_operations in inode.
+ *
+ * Note: this is a blocking operation.
  */
 void dquot_initialize(struct inode *inode, short type)
 {
@@ -894,6 +971,11 @@
 	}
 }
 
+/*
+ * Release all quota for the specified inode.
+ *
+ * Note: this is a blocking operation.
+ */
 void dquot_drop(struct inode *inode)
 {
 	struct dquot *dquot;
@@ -909,7 +991,11 @@
 	}
 }
 
-int dquot_alloc_block(const struct inode *inode, unsigned long number, uid_t initiator, char warn)
+/*
+ * Note: this is a blocking operation.
+ */
+int dquot_alloc_block(const struct inode *inode, unsigned long number, uid_t initiator, 
+			char warn)
 {
 	unsigned short cnt;
 	struct tty_struct *tty = current->tty;
@@ -930,6 +1016,9 @@
 	return(QUOTA_OK);
 }
 
+/*
+ * Note: this is a blocking operation.
+ */
 int dquot_alloc_inode(const struct inode *inode, unsigned long number, uid_t initiator)
 {
 	unsigned short cnt;
@@ -951,6 +1040,9 @@
 	return(QUOTA_OK);
 }
 
+/*
+ * Note: this is a blocking operation.
+ */
 void dquot_free_block(const struct inode *inode, unsigned long number)
 {
 	unsigned short cnt;
@@ -962,6 +1054,9 @@
 	}
 }
 
+/*
+ * Note: this is a blocking operation.
+ */
 void dquot_free_inode(const struct inode *inode, unsigned long number)
 {
 	unsigned short cnt;
@@ -975,6 +1070,8 @@
 
 /*
  * Transfer the number of inode and blocks from one diskquota to an other.
+ *
+ * Note: this is a blocking operation.
  */
 int dquot_transfer(struct inode *inode, struct iattr *iattr, char direction, uid_t initiator)
 {
@@ -1029,8 +1126,8 @@
 	}
 
 	/*
-	 * Finally perform the needed transfer from transfer_from to transfer_to.
-	 * And release any pointer to dquots not needed anymore.
+	 * Finally perform the needed transfer from transfer_from to transfer_to,
+	 * and release any pointers to dquots not needed anymore.
 	 */
 	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
 		/*
@@ -1050,9 +1147,10 @@
 		}
 
 		if (inode->i_dquot[cnt] != NODQUOT) {
-			dqput(transfer_from[cnt]);
-			dqput(inode->i_dquot[cnt]);
+			struct dquot *temp = inode->i_dquot[cnt];
 			inode->i_dquot[cnt] = transfer_to[cnt];
+			dqput(temp);
+			dqput(transfer_from[cnt]);
 		} else {
 			dqput(transfer_from[cnt]);
 			dqput(transfer_to[cnt]);
@@ -1082,8 +1180,8 @@
  * Definitions of diskquota operations.
  */
 struct dquot_operations dquot_operations = {
-	dquot_initialize,
-	dquot_drop,
+	dquot_initialize,		/* mandatory */
+	dquot_drop,			/* mandatory */
 	dquot_alloc_block,
 	dquot_alloc_inode,
 	dquot_free_block,
@@ -1121,30 +1219,47 @@
 int quota_off(kdev_t dev, short type)
 {
 	struct vfsmount *vfsmnt;
+	struct file *filp;
 	short cnt;
 
 	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
 		if (type != -1 && cnt != type)
 			continue;
 
-		if ((vfsmnt = lookup_vfsmnt(dev)) == (struct vfsmount *)NULL ||
-                    is_enabled(vfsmnt, cnt) == 0 ||
-                    vfsmnt->mnt_sb == (struct super_block *)NULL) 
+		vfsmnt = lookup_vfsmnt(dev);
+		if (!vfsmnt)
+			goto out;
+                if (!vfsmnt->mnt_sb)
+			goto out;
+		if (!is_enabled(vfsmnt, cnt))
 			continue;
+		reset_enable_flags(vfsmnt, cnt);
 
-		vfsmnt->mnt_sb->dq_op = (struct dquot_operations *)NULL;
-
+		/* Note: these are blocking operations */
 		reset_dquot_ptrs(dev, cnt);
 		invalidate_dquots(dev, cnt);
 
-		fput(vfsmnt->mnt_dquot.files[cnt]);
-
-		reset_enable_flags(vfsmnt, cnt);
+		filp = vfsmnt->mnt_dquot.files[cnt];
 		vfsmnt->mnt_dquot.files[cnt] = (struct file *)NULL;
 		vfsmnt->mnt_dquot.inode_expire[cnt] = 0;
 		vfsmnt->mnt_dquot.block_expire[cnt] = 0;
+		fput(filp);
+	}
+
+	/*
+	 * Check whether any quota is still enabled,
+	 * and if not clear the dq_op pointer.
+	 */
+	vfsmnt = lookup_vfsmnt(dev);
+	if (vfsmnt && vfsmnt->mnt_sb) {
+		int enabled = 0;
+		for (cnt = 0; cnt < MAXQUOTAS; cnt++)
+			enabled |= is_enabled(vfsmnt, cnt);
+		if (!enabled)
+			vfsmnt->mnt_sb->dq_op = NULL;
 	}
 
+out:
 	return(0);
 }
 
@@ -1316,10 +1431,9 @@
 
 	flags |= QUOTA_SYSCALL;
 
+	ret = -ESRCH;
 	if (dev_has_quota_enabled(dev, type))
 		ret = set_dqblk(dev, id, type, flags, (struct dqblk *) addr);
-	else
-		ret = -ESRCH;
 out:
 	unlock_kernel();
 	return ret;
diff -u --recursive --new-file v2.1.122/linux/fs/ext2/balloc.c linux/fs/ext2/balloc.c
--- v2.1.122/linux/fs/ext2/balloc.c	Wed Aug 26 11:37:40 1998
+++ linux/fs/ext2/balloc.c	Fri Sep 25 23:02:42 1998
@@ -686,6 +686,12 @@
 	}
 }
 
+int ext2_group_sparse(int group)
+{
+	return (test_root(group, 3) || test_root(group, 5) ||
+		test_root(group, 7));
+}
+
 void ext2_check_blocks_bitmap (struct super_block * sb)
 {
 	struct buffer_head * bh;
@@ -716,7 +722,7 @@
 
 		if (!(le32_to_cpu(sb->u.ext2_sb.s_feature_ro_compat) &
 		     EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER) ||
-		    (test_root(i, 3) || test_root(i, 5) || test_root(i, 7))) {
+		    ext2_group_sparse(i)) {
 			if (!ext2_test_bit (0, bh->b_data))
 				ext2_error (sb, "ext2_check_blocks_bitmap",
 					    "Superblock in group %d "
diff -u --recursive --new-file v2.1.122/linux/fs/ext2/super.c linux/fs/ext2/super.c
--- v2.1.122/linux/fs/ext2/super.c	Mon Apr  6 17:41:00 1998
+++ linux/fs/ext2/super.c	Fri Sep 25 23:02:42 1998
@@ -764,8 +764,8 @@
 int ext2_statfs (struct super_block * sb, struct statfs * buf, int bufsiz)
 {
 	unsigned long overhead;
-	unsigned long overhead_per_group;
 	struct statfs tmp;
+	int	ngroups, i;
 
 	if (test_opt (sb, MINIX_DF))
 		overhead = 0;
@@ -773,13 +773,35 @@
 		/*
 		 * Compute the overhead (FS structures)
 		 */
-		overhead_per_group = 1 /* super block */ +
-				     sb->u.ext2_sb.s_db_per_group /* descriptors */ +
-				     1 /* block bitmap */ +
-				     1 /* inode bitmap */ +
-				     sb->u.ext2_sb.s_itb_per_group /* inode table */;
-		overhead = le32_to_cpu(sb->u.ext2_sb.s_es->s_first_data_block) +
-			   sb->u.ext2_sb.s_groups_count * overhead_per_group;
+
+		/*
+		 * All of the blocks before first_data_block are
+		 * overhead
+		 */
+		overhead = le32_to_cpu(sb->u.ext2_sb.s_es->s_first_data_block);
+
+		/*
+		 * Add the overhead attributed to the superblock and
+		 * block group descriptors.  If this is sparse
+		 * superblocks is turned on, then not all groups have
+		 * this.
+		 */
+		if (le32_to_cpu(sb->u.ext2_sb.s_feature_ro_compat) &
+		    EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER) {
+			ngroups = 0;
+			for (i=0 ; i < sb->u.ext2_sb.s_groups_count; i++)
+				if (ext2_group_sparse(i))
+					ngroups++;
+		} else
+			ngroups = sb->u.ext2_sb.s_groups_count;
+		overhead += ngroups * (1 + sb->u.ext2_sb.s_db_per_group);
+
+		/*
+		 * Every block group has an inode bitmap, a block
+		 * bitmap, and an inode table.
+		 */
+		overhead += (sb->u.ext2_sb.s_groups_count *
+			     (2 + sb->u.ext2_sb.s_itb_per_group));
 	}
 
 	tmp.f_type = EXT2_SUPER_MAGIC;
diff -u --recursive --new-file v2.1.122/linux/fs/ext2/symlink.c linux/fs/ext2/symlink.c
--- v2.1.122/linux/fs/ext2/symlink.c	Sun Jan  4 00:53:41 1998
+++ linux/fs/ext2/symlink.c	Sat Sep 19 13:38:18 1998
@@ -25,7 +25,7 @@
 #include <linux/stat.h>
 
 static int ext2_readlink (struct dentry *, char *, int);
-static struct dentry *ext2_follow_link(struct dentry *, struct dentry *);
+static struct dentry *ext2_follow_link(struct dentry *, struct dentry *, unsigned int);
 
 /*
  * symlinks can't do much...
@@ -52,7 +52,8 @@
 };
 
 static struct dentry * ext2_follow_link(struct dentry * dentry,
-					struct dentry *base)
+					struct dentry *base,
+					unsigned int follow)
 {
 	struct inode *inode = dentry->d_inode;
 	struct buffer_head * bh = NULL;
@@ -68,7 +69,7 @@
 		link = bh->b_data;
 	}
 	UPDATE_ATIME(inode);
-	base = lookup_dentry(link, base, 1);
+	base = lookup_dentry(link, base, follow);
 	if (bh)
 		brelse(bh);
 	return base;
diff -u --recursive --new-file v2.1.122/linux/fs/inode.c linux/fs/inode.c
--- v2.1.122/linux/fs/inode.c	Wed Sep  9 14:51:09 1998
+++ linux/fs/inode.c	Sat Sep 19 15:13:19 1998
@@ -131,6 +131,7 @@
 	INIT_LIST_HEAD(&inode->i_hash);
 	INIT_LIST_HEAD(&inode->i_dentry);
 	sema_init(&inode->i_sem, 1);
+	sema_init(&inode->i_atomic_write, 1);
 }
 
 static inline void write_inode(struct inode *inode)
@@ -714,8 +715,11 @@
 printk(KERN_ERR "iput: device %s inode %ld count changed, count=%d\n",
 kdevname(inode->i_dev), inode->i_ino, inode->i_count);
 if (atomic_read(&inode->i_sem.count) != 1)
-printk(KERN_ERR "iput: Aieee, semaphore in use device %s, count=%d\n",
-kdevname(inode->i_dev), atomic_read(&inode->i_sem.count));
+printk(KERN_ERR "iput: Aieee, semaphore in use inode %s/%ld, count=%d\n",
+kdevname(inode->i_dev), inode->i_ino, atomic_read(&inode->i_sem.count));
+if (atomic_read(&inode->i_atomic_write.count) != 1)
+printk(KERN_ERR "iput: Aieee, atomic write semaphore in use inode %s/%ld, count=%d\n",
+kdevname(inode->i_dev), inode->i_ino, atomic_read(&inode->i_sem.count));
 #endif
 		}
 		if (inode->i_count > (1<<31)) {
diff -u --recursive --new-file v2.1.122/linux/fs/isofs/inode.c linux/fs/isofs/inode.c
--- v2.1.122/linux/fs/isofs/inode.c	Thu Sep 17 17:53:37 1998
+++ linux/fs/isofs/inode.c	Fri Sep 25 16:27:13 1998
@@ -445,25 +445,31 @@
   return vol_desc_start;
 }
 
+/*
+ * Initialize the superblock and read the root inode.
+ *
+ * Note: a check_disk_change() has been done immediately prior
+ * to this call, so we don't need to check again.
+ */
 struct super_block *isofs_read_super(struct super_block *s, void *data,
 				     int silent)
 {
-	struct buffer_head	      * bh = NULL, *pri_bh = NULL;
-	unsigned int			blocksize;
-	unsigned int			blocksize_bits;
 	kdev_t				dev = s->s_dev;
+	struct buffer_head	      * bh = NULL, *pri_bh = NULL;
 	struct hs_primary_descriptor  * h_pri = NULL;
 	struct iso_primary_descriptor * pri = NULL;
 	struct iso_supplementary_descriptor *sec = NULL;
 	struct iso_directory_record   * rootp;
+	int				joliet_level = 0;
 	int				high_sierra;
 	int				iso_blknum, block;
-	int				joliet_level = 0;
 	int				orig_zonesize;
+	int				table;
+	unsigned int			blocksize, blocksize_bits;
 	unsigned int			vol_desc_start;
+	unsigned long			first_data_zone;
 	struct inode		      * inode;
 	struct iso9660_options		opt;
-	int				table;
 
 	MOD_INC_USE_COUNT;
 	/* lock before any blocking operations */
@@ -592,7 +598,6 @@
 
 root_found:
 	brelse(pri_bh);
-	s->u.isofs_sb.s_joliet_level = joliet_level;
 
 	if (joliet_level && opt.rock == 'n') {
 	    /* This is the case of Joliet with the norock mount flag.
@@ -623,10 +628,7 @@
 
 	s->u.isofs_sb.s_ninodes = 0; /* No way to figure this out easily */
 
-	/* RDE: convert log zone size to bit shift */
-
 	orig_zonesize = s -> u.isofs_sb.s_log_zone_size;
-
 	/*
 	 * If the zone size is smaller than the hardware sector size,
 	 * this is a fatal error.  This would occur if the disc drive
@@ -637,6 +639,7 @@
 	if(blocksize != 0 && orig_zonesize < blocksize)
 		goto out_bad_size;
 
+	/* RDE: convert log zone size to bit shift */
 	switch (s -> u.isofs_sb.s_log_zone_size)
 	  { case  512: s -> u.isofs_sb.s_log_zone_size =  9; break;
 	    case 1024: s -> u.isofs_sb.s_log_zone_size = 10; break;
@@ -657,14 +660,15 @@
 
 	/* RDE: data zone now byte offset! */
 
-	s->u.isofs_sb.s_firstdatazone = ((isonum_733 (rootp->extent) +
-					   isonum_711 (rootp->ext_attr_length))
-					 << s -> u.isofs_sb.s_log_zone_size);
+	first_data_zone = ((isonum_733 (rootp->extent) +
+			  isonum_711 (rootp->ext_attr_length))
+			 << s -> u.isofs_sb.s_log_zone_size);
+	s->u.isofs_sb.s_firstdatazone = first_data_zone;
 #ifndef BEQUIET
 	printk(KERN_DEBUG "Max size:%ld   Log zone size:%ld\n",
 	       s->u.isofs_sb.s_max_size,
 	       1UL << s->u.isofs_sb.s_log_zone_size);
-	printk(KERN_DEBUG "First datazone:%ld   Root inode number %d\n",
+	printk(KERN_DEBUG "First datazone:%ld   Root inode number:%ld\n",
 	       s->u.isofs_sb.s_firstdatazone >> s -> u.isofs_sb.s_log_zone_size,
 	       s->u.isofs_sb.s_firstdatazone);
 	if(high_sierra)
@@ -672,6 +676,29 @@
 #endif
 
 	/*
+	 * If the Joliet level is set, we _may_ decide to use the
+	 * secondary descriptor, but can't be sure until after we
+	 * read the root inode. But before reading the root inode
+	 * we may need to change the device blocksize, and would
+	 * rather release the old buffer first. So, we cache the
+	 * first_data_zone value from the secondary descriptor.
+	 */
+	if (joliet_level) {
+		pri = (struct iso_primary_descriptor *) sec;
+		rootp = (struct iso_directory_record *)
+			pri->root_directory_record;
+		first_data_zone = ((isonum_733 (rootp->extent) +
+			  	isonum_711 (rootp->ext_attr_length))
+				 << s -> u.isofs_sb.s_log_zone_size);
+	}
+
+	/*
+	 * We're all done using the volume descriptor, and may need
+	 * to change the device blocksize, so release the buffer now.
+	 */
+	brelse(bh);
+
+	/*
 	 * Force the blocksize to 512 for 512 byte sectors.  The file
 	 * read primitives really get it wrong in a bad way if we don't
 	 * do this.
@@ -688,22 +715,15 @@
 	 * entries.  By forcing the blocksize in this way, we ensure
 	 * that we will never be required to do this.
 	 */
-	if( orig_zonesize != opt.blocksize )
-	  {
-	    opt.blocksize = orig_zonesize;
-	    blocksize_bits = 0;
-	    {
-	      int i = opt.blocksize;
-	      while (i != 1){
-		blocksize_bits++;
-		i >>=1;
-	      }
-	    }
-	    set_blocksize(dev, opt.blocksize);
+	if ( orig_zonesize != opt.blocksize ) {
+		set_blocksize(dev, orig_zonesize);
 #ifndef BEQUIET
-	    printk(KERN_DEBUG "Forcing new log zone size:%d\n", opt.blocksize);
+		printk(KERN_DEBUG 
+			"ISOFS: Forcing new log zone size:%d\n", orig_zonesize);
 #endif
-	  }
+	}
+	s->s_blocksize = orig_zonesize;
+	s->s_blocksize_bits = s -> u.isofs_sb.s_log_zone_size;
 
 	s->u.isofs_sb.s_nls_iocharset = NULL;
 
@@ -732,8 +752,12 @@
 	 * as suid, so we merely allow them to set the default permissions.
 	 */
 	s->u.isofs_sb.s_mode = opt.mode & 0777;
-	s->s_blocksize = opt.blocksize;
-	s->s_blocksize_bits = blocksize_bits;
+
+	/*
+	 * Read the root inode, which _may_ result in changing
+	 * the s_rock flag. Once we have the final s_rock value,
+	 * we then decide whether to use the Joliet descriptor.
+	 */
 	inode = iget(s, s->u.isofs_sb.s_firstdatazone);
 
 	/*
@@ -744,21 +768,17 @@
 	 * CD with Unicode names.  Until someone sees such a beast, it
 	 * will not be supported.
 	 */
-	if (opt.rock == 'y' && s->u.isofs_sb.s_rock == 1) {
+	if (s->u.isofs_sb.s_rock == 1) {
 		joliet_level = 0;
-	}
-	if (joliet_level) {
-		iput(inode);
-		pri = (struct iso_primary_descriptor *) sec;
-		rootp = (struct iso_directory_record *)
-			pri->root_directory_record;
-		s->u.isofs_sb.s_firstdatazone =
-			((isonum_733 (rootp->extent) +
-			  isonum_711 (rootp->ext_attr_length))
-			 << s -> u.isofs_sb.s_log_zone_size);
-		inode = iget(s, s->u.isofs_sb.s_firstdatazone);
+	} else if (joliet_level) {
 		s->u.isofs_sb.s_rock = 0;
-		opt.rock = 'n';
+		if (s->u.isofs_sb.s_firstdatazone != first_data_zone) {
+			s->u.isofs_sb.s_firstdatazone = first_data_zone;
+			printk(KERN_DEBUG 
+				"ISOFS: changing to secondary root\n");
+			iput(inode);
+			inode = iget(s, s->u.isofs_sb.s_firstdatazone);
+		}
 	}
 
 	if (opt.check == 'u') {
@@ -766,32 +786,46 @@
 		if (joliet_level) opt.check = 'r';
 		else opt.check = 's';
 	}
+	s->u.isofs_sb.s_joliet_level = joliet_level;
 
+	/* check the root inode */
+	if (!inode)
+		goto out_no_root;
+	if (!inode->i_op)
+		goto out_bad_root;
+	/* get the root dentry */
 	s->s_root = d_alloc_root(inode, NULL);
 	if (!(s->s_root))
 		goto out_no_root;
+
 	table = 0;
 	if (joliet_level) table += 2;
 	if (opt.check == 'r') table++;
 	s->s_root->d_op = &isofs_dentry_ops[table];
 
-	if(!check_disk_change(dev)) {
-		brelse(bh);
-		unlock_super(s);
-		return s;
-	}
-	/*
-	 * Disk changed? Free the root dentry and clean up ...
-	 */
-	dput(s->s_root);
-	goto out_freechar;
+	unlock_super(s);
+	return s;
 
 	/*
-	 * Display error message
+	 * Display error messages and free resources.
 	 */
-out_no_root:
-	printk(KERN_ERR "isofs_read_super: get root inode failed\n");
+out_bad_root:
+	printk(KERN_WARNING "isofs_read_super: root inode not initialized\n");
 	goto out_iput;
+out_no_root:
+	printk(KERN_WARNING "isofs_read_super: get root inode failed\n");
+out_iput:
+	iput(inode);
+#ifdef CONFIG_JOLIET
+	if (s->u.isofs_sb.s_nls_iocharset)
+		unload_nls(s->u.isofs_sb.s_nls_iocharset);
+#endif
+	goto out_unlock;
+out_no_read:
+	printk(KERN_WARNING "isofs_read_super: "
+		"bread failed, dev=%s, iso_blknum=%d, block=%d\n",
+		kdevname(dev), iso_blknum, block);
+	goto out_unlock;
 out_bad_zone_size:
 	printk(KERN_WARNING "Bad logical zone size %ld\n",
 		s->u.isofs_sb.s_log_zone_size);
@@ -808,23 +842,7 @@
 out_unknown_format:
 	if (!silent)
 		printk(KERN_WARNING "Unable to identify CD-ROM format.\n");
-	goto out_freebh;
-out_no_read:
-	printk(KERN_WARNING "isofs_read_super: "
-		"bread failed, dev=%s, iso_blknum=%d, block=%d\n",
-		kdevname(dev), iso_blknum, block);
-	goto out_unlock;
 
- 	/*
-	 * Cascaded error cleanup to ensure all resources are freed.
-	 */
-out_iput:
-	iput(inode);
-out_freechar:
-#ifdef CONFIG_JOLIET
-	if (s->u.isofs_sb.s_nls_iocharset)
-		unload_nls(s->u.isofs_sb.s_nls_iocharset);
-#endif
 out_freebh:
 	brelse(bh);
 out_unlock:
@@ -945,101 +963,113 @@
 
 static int isofs_read_level3_size(struct inode * inode)
 {
+	unsigned long ino = inode->i_ino;
 	unsigned long bufsize = ISOFS_BUFFER_SIZE(inode);
+	int high_sierra = inode->i_sb->u.isofs_sb.s_high_sierra;
 	struct buffer_head * bh = NULL;
-	struct iso_directory_record * raw_inode = NULL;		/* quiet gcc */
-	unsigned char *pnt = NULL;
-	void *cpnt = NULL;
-	int block = 0;			/* Quiet GCC */
-	unsigned long ino;
-	int i;
+	int block = 0;
+	int i = 0;
+	void *cpnt;
+	struct iso_directory_record * raw_inode;
 
 	inode->i_size = 0;
 	inode->u.isofs_i.i_next_section_ino = 0;
-	ino = inode->i_ino;
-	i = 0;
 	do {
-		if(i > 100) {
-			printk("isofs_read_level3_size: More than 100 file sections ?!?, aborting...\n"
-			       "isofs_read_level3_size: inode=%lu ino=%lu\n", inode->i_ino, ino);
-			return 0;
+		unsigned char *pnt;
+		unsigned int reclen;
+		int offset = (ino & (bufsize - 1));
+
+		cpnt = NULL;
+		/* Check whether to update our buffer */
+		if (block != ino >> ISOFS_BUFFER_BITS(inode)) {
+			block = ino >> ISOFS_BUFFER_BITS(inode);
+			brelse(bh);
+			bh = bread(inode->i_dev, block, bufsize);
+			if (!bh)
+				goto out_noread;
 		}
+		pnt = ((unsigned char *) bh->b_data + offset);
+		raw_inode = ((struct iso_directory_record *) pnt);
+		/*
+		 * Note: this is invariant even if the record
+		 * spans buffers and must be copied ...
+		 */
+		reclen = *pnt;
 
-		if(bh == NULL || block != ino >> ISOFS_BUFFER_BITS(inode)) {
-			if(bh) brelse(bh);
-			block = ino >> ISOFS_BUFFER_BITS(inode);
-			if (!(bh=bread(inode->i_dev,block, bufsize))) {
-				printk("unable to read i-node block");
-				return 1;
-			}
+		/* N.B. this test doesn't trigger the i++ code ... */	
+		if(reclen == 0) {
+			ino = (ino & ~(ISOFS_BLOCK_SIZE - 1)) + ISOFS_BLOCK_SIZE;
+			continue;
 		}
-		pnt = ((unsigned char *) bh->b_data
-		       + (ino & (bufsize - 1)));
-	
-		if ((ino & (bufsize - 1)) + *pnt > bufsize){
-		        int frag1, offset;
+
+		/* Check whether the raw inode spans the buffer ... */	
+		if (offset + reclen > bufsize){
+		        int frag1 = bufsize - offset;
 	
-			offset = (ino & (bufsize - 1));
-			frag1 = bufsize - offset;
-		        cpnt = kmalloc(*pnt,GFP_KERNEL);
-			if (cpnt == NULL) {
-				printk(KERN_INFO "NoMem ISO inode %lu\n",inode->i_ino);
-				brelse(bh);
-				return 1;
-			}
-			memcpy(cpnt, bh->b_data + offset, frag1);
+		        cpnt = kmalloc(reclen, GFP_KERNEL);
+			if (cpnt == NULL)
+				goto out_nomem;
+			memcpy(cpnt, pnt, frag1);
 			brelse(bh);
-			if (!(bh = bread(inode->i_dev,++block, bufsize))) {
-				kfree(cpnt);
-				printk("unable to read i-node block");
-				return 1;
-			}
-			offset += *pnt - bufsize;
+			bh = bread(inode->i_dev, ++block, bufsize);
+			if (!bh)
+				goto out_noread;
+			offset += reclen - bufsize;
 			memcpy((char *)cpnt+frag1, bh->b_data, offset);
-			pnt = ((unsigned char *) cpnt);
+			raw_inode = ((struct iso_directory_record *) cpnt);
 		}
-		
-		if(*pnt == 0) {
-			ino = (ino & ~(ISOFS_BLOCK_SIZE - 1)) + ISOFS_BLOCK_SIZE;
-			continue;
-		}
-		raw_inode = ((struct iso_directory_record *) pnt);
 
 		inode->i_size += isonum_733 (raw_inode->size);
 		if(i == 1) inode->u.isofs_i.i_next_section_ino = ino;
 
-		ino += *pnt;
-		if (cpnt) {
+		ino += reclen;
+		if (cpnt)
 			kfree (cpnt);
-			cpnt = NULL;
-		}
 		i++;
-	} while(raw_inode->flags[-inode->i_sb->u.isofs_sb.s_high_sierra] & 0x80);
+		if(i > 100)
+			goto out_toomany;
+	} while(raw_inode->flags[-high_sierra] & 0x80);
+out:
 	brelse(bh);
 	return 0;
+
+out_nomem:
+	printk(KERN_INFO "ISOFS: NoMem ISO inode %lu\n", inode->i_ino);
+	brelse(bh);
+	return 1;
+out_noread:
+	printk(KERN_INFO "ISOFS: unable to read i-node block %d\n", block);
+	if (cpnt)
+		kfree(cpnt);
+	return 1;
+out_toomany:
+	printk(KERN_INFO "isofs_read_level3_size: "
+		"More than 100 file sections ?!?, aborting...\n"
+	  	"isofs_read_level3_size: inode=%lu ino=%lu\n",
+		inode->i_ino, ino);
+	goto out;
 }
 
 void isofs_read_inode(struct inode * inode)
 {
+	struct super_block *sb = inode->i_sb;
 	unsigned long bufsize = ISOFS_BUFFER_SIZE(inode);
+	int block = inode->i_ino >> ISOFS_BUFFER_BITS(inode);
+	int high_sierra = sb->u.isofs_sb.s_high_sierra;
 	struct buffer_head * bh;
 	struct iso_directory_record * raw_inode;
-	unsigned char *pnt = NULL;
-	int high_sierra;
-	int block;
-	int volume_seq_no ;
-	int i;
+	unsigned char *pnt;
+	int volume_seq_no, i;
 
-	block = inode->i_ino >> ISOFS_BUFFER_BITS(inode);
-	if (!(bh=bread(inode->i_dev,block, bufsize))) {
-	  printk("unable to read i-node block");
-	  goto fail;
+	bh = bread(inode->i_dev, block, bufsize);
+	if (!bh) {
+		printk(KERN_WARNING "ISOFS: unable to read i-node block\n");
+		goto fail;
 	}
 
 	pnt = ((unsigned char *) bh->b_data
 	       + (inode->i_ino & (bufsize - 1)));
 	raw_inode = ((struct iso_directory_record *) pnt);
-	high_sierra = inode->i_sb->u.isofs_sb.s_high_sierra;
 
 	if (raw_inode->flags[-high_sierra] & 2) {
 		inode->i_mode = S_IRUGO | S_IXUGO | S_IFDIR;
@@ -1049,10 +1079,13 @@
 				       easier to give 1 which tells find to
 				       do it the hard way. */
 	} else {
-		inode->i_mode = inode->i_sb->u.isofs_sb.s_mode; /* Everybody gets to read the file. */
+ 		/* Everybody gets to read the file. */
+		inode->i_mode = inode->i_sb->u.isofs_sb.s_mode;
 		inode->i_nlink = 1;
 	        inode->i_mode |= S_IFREG;
-/* If there are no periods in the name, then set the execute permission bit */
+		/* If there are no periods in the name,
+		 * then set the execute permission bit
+		 */
 		for(i=0; i< raw_inode->name_len[0]; i++)
 			if(raw_inode->name[i]=='.' || raw_inode->name[i]==';')
 				break;
@@ -1133,14 +1166,16 @@
 #ifdef DEBUG
 	printk("Inode: %x extent: %x\n",inode->i_ino, inode->u.isofs_i.i_first_extent);
 #endif
-	brelse(bh);
-
-	inode->i_op = NULL;
 
 	/* get the volume sequence number */
 	volume_seq_no = isonum_723 (raw_inode->volume_sequence_number) ;
 
 	/*
+	 * All done with buffer ... no more references to buffer memory!
+	 */
+	brelse(bh);
+
+	/*
 	 * Disable checking if we see any volume number other than 0 or 1.
 	 * We could use the cruft option, but that has multiple purposes, one
 	 * of which is limiting the file size to 16Mb.  Thus we silently allow
@@ -1152,6 +1187,8 @@
 	  inode->i_sb->u.isofs_sb.s_cruft = 'y';
 	}
 
+	/* Install the inode operations vector */
+	inode->i_op = NULL;
 #ifndef IGNORE_WRONG_MULTI_VOLUME_SPECS
 	if (inode->i_sb->u.isofs_sb.s_cruft != 'y' &&
 	    (volume_seq_no != 0) && (volume_seq_no != 1)) {
@@ -1173,6 +1210,7 @@
 	    init_fifo(inode);
 	}
 	return;
+
       fail:
 	/* With a data error we return this information */
 	inode->i_mtime = inode->i_atime = inode->i_ctime = 0;
diff -u --recursive --new-file v2.1.122/linux/fs/isofs/rock.c linux/fs/isofs/rock.c
--- v2.1.122/linux/fs/isofs/rock.c	Wed Jun 24 22:54:09 1998
+++ linux/fs/isofs/rock.c	Fri Sep 25 16:27:13 1998
@@ -54,25 +54,23 @@
   {if (buffer) kfree(buffer); \
   if (cont_extent){ \
     int block, offset, offset1; \
-    struct buffer_head * bh; \
+    struct buffer_head * pbh; \
     buffer = kmalloc(cont_size,GFP_KERNEL); \
     if (!buffer) goto out; \
     block = cont_extent; \
     offset = cont_offset; \
     offset1 = 0; \
-    if(buffer) { \
-      bh = bread(DEV->i_dev, block, ISOFS_BUFFER_SIZE(DEV)); \
-      if(bh){       \
-        memcpy(buffer + offset1, bh->b_data + offset, cont_size - offset1); \
-        brelse(bh); \
-        chr = (unsigned char *) buffer; \
-        len = cont_size; \
-        cont_extent = 0; \
-        cont_size = 0; \
-        cont_offset = 0; \
-        goto LABEL; \
-      };    \
-    } \
+    pbh = bread(DEV->i_dev, block, ISOFS_BUFFER_SIZE(DEV)); \
+    if(pbh){       \
+      memcpy(buffer + offset1, pbh->b_data + offset, cont_size - offset1); \
+      brelse(pbh); \
+      chr = (unsigned char *) buffer; \
+      len = cont_size; \
+      cont_extent = 0; \
+      cont_size = 0; \
+      cont_offset = 0; \
+      goto LABEL; \
+    };    \
     printk("Unable to read rock-ridge attributes\n");    \
   }}
 
@@ -162,7 +160,6 @@
  
   if (!inode->i_sb->u.isofs_sb.s_rock) return 0;
   *retname = 0;
-  retnamlen = 0;
 
   SETUP_ROCK_RIDGE(de, chr, len);
  repeat:
@@ -364,6 +361,8 @@
 	inode->u.isofs_i.i_first_extent = isonum_733(rr->u.CL.location) <<
 		inode -> i_sb -> u.isofs_sb.s_log_zone_size;
 	reloc = iget(inode->i_sb, inode->u.isofs_i.i_first_extent);
+	if (!reloc)
+		goto out;
 	inode->i_mode = reloc->i_mode;
 	inode->i_nlink = reloc->i_nlink;
 	inode->i_uid = reloc->i_uid;
@@ -396,8 +395,8 @@
   unsigned long bufsize = ISOFS_BUFFER_SIZE(inode);
   unsigned char bufbits = ISOFS_BUFFER_BITS(inode);
   struct buffer_head * bh;
+  char * rpnt = NULL;
   unsigned char * pnt;
-  char * rpnt;
   struct iso_directory_record * raw_inode;
   CONTINUE_DECLS;
   int block;
@@ -410,13 +409,10 @@
   if (!inode->i_sb->u.isofs_sb.s_rock)
     panic("Cannot have symlink with high sierra variant of iso filesystem\n");
 
-  rpnt = 0;
-  
   block = inode->i_ino >> bufbits;
-  if (!(bh=bread(inode->i_dev,block, bufsize))) {
-    printk("unable to read i-node block");
-    return NULL;
-  };
+  bh = bread(inode->i_dev, block, bufsize);
+  if (!bh)
+	goto out_noread;
   
   pnt = ((unsigned char *) bh->b_data) + (inode->i_ino & (bufsize - 1));
   
@@ -425,10 +421,8 @@
   /*
    * If we go past the end of the buffer, there is some sort of error.
    */
-  if ((inode->i_ino & (bufsize - 1)) + *pnt > bufsize){
-	printk("symlink spans iso9660 blocks\n");
-	return NULL;
-  };
+  if ((inode->i_ino & (bufsize - 1)) + *pnt > bufsize)
+	goto out_bad_span;
   
   /* Now test for possible Rock Ridge extensions which will override some of
      these numbers in the inode structure. */
@@ -511,16 +505,23 @@
     };
   };
   MAYBE_CONTINUE(repeat,inode);
-  brelse(bh);
   
-  return rpnt;
- out:
-  if(buffer) kfree(buffer);
-  return 0;
+out_freebh:
+	brelse(bh);
+	return rpnt;
+
+	/* error exit from macro */
+out:
+	if(buffer)
+		kfree(buffer);
+	if(rpnt)
+		kfree(rpnt);
+	rpnt = NULL;
+	goto out_freebh;
+out_noread:
+	printk("unable to read i-node block");
+	goto out_freebh;
+out_bad_span:
+	printk("symlink spans iso9660 blocks\n");
+	goto out_freebh;
 }
-
-
-
-
-
-
diff -u --recursive --new-file v2.1.122/linux/fs/isofs/symlink.c linux/fs/isofs/symlink.c
--- v2.1.122/linux/fs/isofs/symlink.c	Wed Jun 24 22:54:09 1998
+++ linux/fs/isofs/symlink.c	Sat Sep 19 13:42:23 1998
@@ -19,7 +19,7 @@
 #include <asm/uaccess.h>
 
 static int isofs_readlink(struct dentry *, char *, int);
-static struct dentry * isofs_follow_link(struct dentry *, struct dentry *);
+static struct dentry * isofs_follow_link(struct dentry *, struct dentry *, unsigned int);
 
 /*
  * symlinks can't do much...
@@ -66,7 +66,8 @@
 }
 
 static struct dentry * isofs_follow_link(struct dentry * dentry,
-					struct dentry *base)
+					struct dentry *base,
+					unsigned int follow)
 {
 	char * pnt;
 
@@ -76,7 +77,7 @@
 		return ERR_PTR(-ELOOP);
 	}
 
-	base = lookup_dentry(pnt, base, 1);
+	base = lookup_dentry(pnt, base, follow);
 
 	kfree(pnt);
 	return base;
diff -u --recursive --new-file v2.1.122/linux/fs/minix/namei.c linux/fs/minix/namei.c
--- v2.1.122/linux/fs/minix/namei.c	Tue Jun 23 10:01:25 1998
+++ linux/fs/minix/namei.c	Sat Sep 19 15:23:42 1998
@@ -88,7 +88,7 @@
 {
 	unsigned long hash;
 	int i;
-	const char *name;
+	const unsigned char *name;
 
 	i = dentry->d_inode->i_sb->u.minix_sb.s_namelen;
 	if (i >= qstr->len)
diff -u --recursive --new-file v2.1.122/linux/fs/minix/symlink.c linux/fs/minix/symlink.c
--- v2.1.122/linux/fs/minix/symlink.c	Sun Jan  4 00:53:41 1998
+++ linux/fs/minix/symlink.c	Sat Sep 19 13:42:44 1998
@@ -15,7 +15,7 @@
 #include <asm/uaccess.h>
 
 static int minix_readlink(struct dentry *, char *, int);
-static struct dentry *minix_follow_link(struct dentry *, struct dentry *);
+static struct dentry *minix_follow_link(struct dentry *, struct dentry *, unsigned int);
 
 /*
  * symlinks can't do much...
@@ -41,7 +41,8 @@
 };
 
 static struct dentry * minix_follow_link(struct dentry * dentry,
-					struct dentry * base)
+					struct dentry * base,
+					unsigned int follow)
 {
 	struct inode *inode = dentry->d_inode;
 	struct buffer_head * bh;
@@ -52,7 +53,7 @@
 		return ERR_PTR(-EIO);
 	}
 	UPDATE_ATIME(inode);
-	base = lookup_dentry(bh->b_data, base, 1);
+	base = lookup_dentry(bh->b_data, base, follow);
 	brelse(bh);
 	return base;
 }
diff -u --recursive --new-file v2.1.122/linux/fs/msdos/namei.c linux/fs/msdos/namei.c
--- v2.1.122/linux/fs/msdos/namei.c	Thu Sep 17 17:53:38 1998
+++ linux/fs/msdos/namei.c	Sun Sep 27 12:35:58 1998
@@ -266,6 +266,7 @@
 
 	res = msdos_find(dir, dentry->d_name.name, dentry->d_name.len, &bh,
 			&de, &ino);
+
 	if (res == -ENOENT)
 		goto add;
 	if (res < 0)
@@ -275,7 +276,7 @@
 
 	/* try to get the inode */
 	res = -EACCES;
-	inode = iget(dir->i_sb, ino);
+	inode = iget(sb, ino);
 	if (!inode)
 		goto out;
 	if (!inode->i_sb ||
@@ -289,9 +290,9 @@
 		iput(inode);
 		goto out;
 	}
-	res = 0;
 add:
 	d_add(dentry, inode);
+	res = 0;
 out:
 	return res;
 }
@@ -446,14 +447,14 @@
 	if (dir->i_dev != inode->i_dev || dir == inode)
 		printk("msdos_rmdir: impossible condition\n");
 	/*
-	 * Prune any child dentries, then verify that
-	 * the directory is empty and not in use.
+	 * Check whether the directory is empty, then prune
+	 * any child dentries and make sure it's not in use.
 	 */
-	shrink_dcache_parent(dentry);
 	res = msdos_empty(inode);
 	if (res)
 		goto rmdir_done;
 	res = -EBUSY;
+	shrink_dcache_parent(dentry);
 	if (dentry->d_count > 1) {
 #ifdef MSDOS_DEBUG
 printk("msdos_rmdir: %s/%s busy, d_count=%d\n",
@@ -476,6 +477,7 @@
 	de->name[0] = DELETED_FLAG;
 	fat_mark_buffer_dirty(sb, bh, 1);
 	res = 0;
+
 rmdir_done:
 	fat_brelse(sb, bh);
 	return res;
@@ -499,18 +501,21 @@
 		return res;
 	is_hid = (dentry->d_name.name[0]=='.') && (msdos_name[0]!='.');
 	fat_lock_creation();
-	if (fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_ANY) >= 0) {
-		fat_unlock_creation();
-		/* N.B. does this need to be released on the other path? */
-		fat_brelse(sb, bh);
-		return -EEXIST;
- 	}
+	if (fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_ANY) >= 0)
+		goto out_exist;
+
 	res = msdos_create_entry(dir,msdos_name,1,is_hid, &inode);
 	if (res < 0)
 		goto out_unlock;
+
 	dir->i_nlink++;
 	inode->i_nlink = 2; /* no need to mark them dirty */
 	MSDOS_I(inode)->i_busy = 1; /* prevent lookups */
+	/*
+	 * Instantiate the dentry now, in case we need to cleanup.
+	 */
+	d_instantiate(dentry, inode);
+
 	if ((res = fat_add_cluster(inode)) < 0)
 		goto mkdir_error;
 	if ((res = msdos_create_entry(inode,MSDOS_DOT,1,0,&dot)) < 0)
@@ -521,9 +526,9 @@
 	dot->i_nlink = inode->i_nlink;
 	mark_inode_dirty(dot);
 	iput(dot);
+
 	if ((res = msdos_create_entry(inode,MSDOS_DOTDOT,1,0,&dot)) < 0)
 		goto mkdir_error;
-	fat_unlock_creation();
 	dot->i_size = dir->i_size;
 	MSDOS_I(dot)->i_start = MSDOS_I(dir)->i_start;
 	MSDOS_I(dot)->i_logstart = MSDOS_I(dir)->i_logstart;
@@ -531,15 +536,22 @@
 	mark_inode_dirty(dot);
 	MSDOS_I(inode)->i_busy = 0;
 	iput(dot);
-	d_instantiate(dentry, inode);
-	return 0;
+	res = 0;
 
-mkdir_error:
-	if (msdos_rmdir(dir,dentry) < 0)
-		fat_fs_panic(dir->i_sb,"rmdir in mkdir failed");
 out_unlock:
 	fat_unlock_creation();
 	return res;
+
+mkdir_error:
+	printk("msdos_mkdir: error=%d, attempting cleanup\n", res);
+	if (msdos_rmdir(dir,dentry) < 0)
+		fat_fs_panic(dir->i_sb,"rmdir in mkdir failed");
+	goto out_unlock;
+
+out_exist:
+	fat_brelse(sb, bh);
+	res = -EEXIST;
+	goto out_unlock;
 }
 
 /***** Unlink a file */
@@ -636,10 +648,14 @@
 		if ((old_de->attr & ATTR_SYS))
 			goto out_error;
 
+		if (S_ISDIR(new_inode->i_mode)) {
+			/* make sure it's empty */
+			error = msdos_empty(new_inode);
+			if (error)
+				goto out_error;
 #ifdef MSDOS_CHECK_BUSY
-		/* check for a busy dentry */
-		error = -EBUSY;
-		if (new_dentry->d_count > 1) {
+			/* check for a busy dentry */
+			error = -EBUSY;
 			shrink_dcache_parent(new_dentry);
 			if (new_dentry->d_count > 1) {
 printk("msdos_rename_same: %s/%s busy, count=%d\n",
@@ -647,20 +663,19 @@
 new_dentry->d_count);
 				goto out_error;
 			}
-		}
 #endif
-
-		if (S_ISDIR(new_inode->i_mode)) {
 			new_dir->i_nlink--;
 			mark_inode_dirty(new_dir);
 		}
 		new_inode->i_nlink = 0;
 		MSDOS_I(new_inode)->i_busy = 1;
 		mark_inode_dirty(new_inode);
-#ifdef MSDOS_CHECK_BUSY
-		/* d_delete the dentry, as we killed its inode */
-		d_delete(new_dentry);
-#endif
+		/*
+		 * Make it negative if it's not busy;
+		 * otherwise let d_move() drop it.
+		 */
+		if (new_dentry->d_count == 1)
+			d_delete(new_dentry);
 
 		new_de->name[0] = DELETED_FLAG;
 		fat_mark_buffer_dirty(sb, new_bh, 1);
@@ -763,16 +778,22 @@
 		}
 #endif
 		if (S_ISDIR(new_inode->i_mode)) {
+			/* make sure it's empty */
+			error = msdos_empty(new_inode);
+			if (error)
+				goto out_new;
 			new_dir->i_nlink--;
 			mark_inode_dirty(new_dir);
 		}
 		new_inode->i_nlink = 0;
 		MSDOS_I(new_inode)->i_busy = 1;
 		mark_inode_dirty(new_inode);
-#ifdef MSDOS_CHECK_BUSY
-		/* d_delete the dentry, as we killed its inode */
-		d_delete(new_dentry);
-#endif
+		/*
+		 * Make it negative if it's not busy;
+		 * otherwise let d_move() drop it.
+		 */
+		if (new_dentry->d_count == 1)
+			d_delete(new_dentry);
 		new_de->name[0] = DELETED_FLAG;
 		fat_mark_buffer_dirty(sb, new_bh, 1);
 		fat_brelse(sb, new_bh);
@@ -785,8 +806,13 @@
 	if (S_ISDIR(old_inode->i_mode)) {
 		error = fat_scan(old_inode, MSDOS_DOTDOT, &dotdot_bh,
 				&dotdot_de, &dotdot_ino, SCAN_ANY);
-		if (error < 0)
+		if (error < 0) {
+			printk(KERN_WARNING
+				"MSDOS: %s/%s, get dotdot failed, ret=%d\n",
+				old_dentry->d_parent->d_name.name,
+				old_dentry->d_name.name, error);
 			goto rename_done;
+		}
 		error = -EIO;
 		dotdot_inode = iget(sb, dotdot_ino);
 		if (!dotdot_inode)
@@ -804,12 +830,19 @@
 	free_inode = iget(sb, free_ino);
 	if (!free_inode)
 		goto out_iput;
+	/* make sure it's not busy! */
+	if (MSDOS_I(free_inode)->i_busy)
+		printk(KERN_ERR "msdos_rename_diff: new inode %ld busy!\n",
+			(ino_t) free_ino);
+	if (!list_empty(&free_inode->i_dentry))
+		printk("msdos_rename_diff: free inode has aliases??\n");
 	msdos_read_inode(free_inode);
 
 	/*
 	 * Make sure the old dentry isn't busy,
 	 * as we need to change inodes ...
 	 */
+	error = -EBUSY;
 	if (old_dentry->d_count > 1) {
 		shrink_dcache_parent(old_dentry);
 		if (old_dentry->d_count > 1) {
@@ -825,6 +858,7 @@
 	d_delete(old_dentry);
 
 	free_inode->i_mode   = old_inode->i_mode;
+	free_inode->i_nlink  = old_inode->i_nlink;
 	free_inode->i_size   = old_inode->i_size;
 	free_inode->i_blocks = old_inode->i_blocks;
 	free_inode->i_mtime  = old_inode->i_mtime;
@@ -836,16 +870,21 @@
 	MSDOS_I(free_inode)->i_logstart = MSDOS_I(old_inode)->i_logstart;
 	MSDOS_I(free_inode)->i_attrs = MSDOS_I(old_inode)->i_attrs;
 
+	/* release the old inode's resources */
+	MSDOS_I(old_inode)->i_start = 0;
+	MSDOS_I(old_inode)->i_logstart = 0;
+	old_inode->i_nlink = 0;
+
 	/*
 	 * Install the new inode ...
 	 */
 	d_instantiate(old_dentry, free_inode);
 
+	fat_mark_buffer_dirty(sb, free_bh, 1);
 	fat_cache_inval_inode(old_inode);
 	mark_inode_dirty(old_inode);
 	old_de->name[0] = DELETED_FLAG;
 	fat_mark_buffer_dirty(sb, old_bh, 1);
-	fat_mark_buffer_dirty(sb, free_bh, 1);
 	iput(old_inode);
 
 	/* a directory? */
@@ -854,13 +893,13 @@
 		MSDOS_I(dotdot_inode)->i_logstart = MSDOS_I(new_dir)->i_logstart;
 		dotdot_de->start = CT_LE_W(MSDOS_I(new_dir)->i_logstart);
 		dotdot_de->starthi = CT_LE_W((MSDOS_I(new_dir)->i_logstart) >> 16);
-		mark_inode_dirty(dotdot_inode);
-		fat_mark_buffer_dirty(sb, dotdot_bh, 1);
 		old_dir->i_nlink--;
 		new_dir->i_nlink++;
 		/* no need to mark them dirty */
 		dotdot_inode->i_nlink = new_dir->i_nlink;
+		mark_inode_dirty(dotdot_inode);
 		iput(dotdot_inode);
+		fat_mark_buffer_dirty(sb, dotdot_bh, 1);
 		fat_brelse(sb, dotdot_bh);
 	}
 
diff -u --recursive --new-file v2.1.122/linux/fs/namei.c linux/fs/namei.c
--- v2.1.122/linux/fs/namei.c	Thu Sep 17 17:53:38 1998
+++ linux/fs/namei.c	Tue Sep 22 17:47:27 1998
@@ -280,7 +280,19 @@
 	return result;
 }
 
-static struct dentry * do_follow_link(struct dentry *base, struct dentry *dentry)
+/*
+ * The bitmask for a lookup event:
+ *  - follow links at the end
+ *  - require a directory
+ *  - ending slashes ok even for nonexistent files
+ *  - internal "there are more path compnents" flag
+ */
+#define LOOKUP_FOLLOW		(1)
+#define LOOKUP_DIRECTORY	(2)
+#define LOOKUP_SLASHOK		(4)
+#define LOOKUP_CONTINUE		(8)
+
+static struct dentry * do_follow_link(struct dentry *base, struct dentry *dentry, unsigned int follow)
 {
 	struct inode * inode = dentry->d_inode;
 
@@ -290,7 +302,7 @@
 
 			current->link_count++;
 			/* This eats the base */
-			result = inode->i_op->follow_link(dentry, base);
+			result = inode->i_op->follow_link(dentry, base, follow);
 			current->link_count--;
 			dput(dentry);
 			return result;
@@ -320,9 +332,10 @@
  * This is the basic name resolution function, turning a pathname
  * into the final dentry.
  */
-struct dentry * lookup_dentry(const char * name, struct dentry * base, int follow_link)
+struct dentry * lookup_dentry(const char * name, struct dentry * base, unsigned int lookup_flags)
 {
 	struct dentry * dentry;
+	struct inode *inode;
 
 	if (*name == '/') {
 		if (base)
@@ -338,22 +351,16 @@
 	if (!*name)
 		goto return_base;
 
+	inode = base->d_inode;
+	lookup_flags &= LOOKUP_FOLLOW | LOOKUP_DIRECTORY | LOOKUP_SLASHOK;
+
 	/* At this point we know we have a real path component. */
 	for(;;) {
 		int err;
 		unsigned long hash;
 		struct qstr this;
-		struct inode *inode;
-		char c, follow;
-
-		dentry = ERR_PTR(-ENOENT);
-		inode = base->d_inode;
-		if (!inode)
-			break;
-
-		dentry = ERR_PTR(-ENOTDIR);
-		if (!inode->i_op || !inode->i_op->lookup)
-			break;
+		unsigned int flags;
+		unsigned int c;
 
 		err = permission(inode, MAY_EXEC);
 		dentry = ERR_PTR(err);
@@ -361,23 +368,28 @@
 			break;
 
 		this.name = name;
-		c = *name;
+		c = *(const unsigned char *)name;
 
 		hash = init_name_hash();
 		do {
+			name++;
 			hash = partial_name_hash(c, hash);
-			c = *++name;
+			c = *(const unsigned char *)name;
 		} while (c && (c != '/'));
 		this.len = name - (const char *) this.name;
 		this.hash = end_name_hash(hash);
 
 		/* remove trailing slashes? */
-		follow = follow_link;
+		flags = lookup_flags;
 		if (c) {
-			follow |= c;
+			char tmp;
+
+			flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
 			do {
-				c = *++name;
-			} while (c == '/');
+				tmp = *++name;
+			} while (tmp == '/');
+			if (tmp)
+				flags |= LOOKUP_CONTINUE;
 		}
 
 		/*
@@ -407,15 +419,47 @@
 		/* Check mountpoints.. */
 		dentry = follow_mount(dentry);
 
-		if (!follow)
+		if (!(flags & LOOKUP_FOLLOW))
 			break;
 
-		base = do_follow_link(base, dentry);
-		if (c && !IS_ERR(base))
-			continue;
+		base = do_follow_link(base, dentry, flags);
+		if (IS_ERR(base))
+			goto return_base;
 
+		inode = base->d_inode;
+		if (flags & LOOKUP_DIRECTORY) {
+			if (!inode)
+				goto no_inode;
+			dentry = ERR_PTR(-ENOTDIR); 
+			if (!inode->i_op || !inode->i_op->lookup)
+				break;
+			if (flags & LOOKUP_CONTINUE)
+				continue;
+		}
 return_base:
 		return base;
+/*
+ * The case of a nonexisting file is special.
+ *
+ * In the middle of a pathname lookup (ie when
+ * LOOKUP_CONTINUE is set), it's an obvious
+ * error and returns ENOENT.
+ *
+ * At the end of a pathname lookup it's legal,
+ * and we return a negative dentry. However, we
+ * get here only if there were trailing slashes,
+ * which is legal only if we know it's supposed
+ * to be a directory (ie "mkdir"). Thus the
+ * LOOKUP_SLASHOK flag.
+ */
+no_inode:
+		dentry = ERR_PTR(-ENOENT);
+		if (flags & LOOKUP_CONTINUE)
+			break;
+		if (flags & LOOKUP_SLASHOK)
+			goto return_base;
+		dentry = ERR_PTR(-ENOTDIR);
+		break;
 	}
 	dput(base);
 	return dentry;
@@ -431,7 +475,7 @@
  * namei exists in two versions: namei/lnamei. The only difference is
  * that namei follows links, while lnamei does not.
  */
-struct dentry * __namei(const char *pathname, int follow_link)
+struct dentry * __namei(const char *pathname, unsigned int lookup_flags)
 {
 	char *name;
 	struct dentry *dentry;
@@ -439,7 +483,7 @@
 	name = getname(pathname);
 	dentry = (struct dentry *) name;
 	if (!IS_ERR(name)) {
-		dentry = lookup_dentry(name, NULL, follow_link);
+		dentry = lookup_dentry(name, NULL, lookup_flags);
 		putname(name);
 		if (!IS_ERR(dentry)) {
 			if (!dentry->d_inode) {
@@ -637,7 +681,7 @@
 	struct dentry *dentry, *retval;
 
 	mode &= ~current->fs->umask;
-	dentry = lookup_dentry(filename, NULL, 1);
+	dentry = lookup_dentry(filename, NULL, LOOKUP_FOLLOW);
 	if (IS_ERR(dentry))
 		return dentry;
 
@@ -721,7 +765,7 @@
 	struct dentry *dir;
 	struct dentry *dentry;
 
-	dentry = lookup_dentry(pathname, NULL, 0);
+	dentry = lookup_dentry(pathname, NULL, LOOKUP_SLASHOK);
 	error = PTR_ERR(dentry);
 	if (IS_ERR(dentry))
 		goto exit;
@@ -1128,7 +1172,16 @@
 	if (IS_ERR(old_dentry))
 		goto exit;
 
-	new_dentry = lookup_dentry(newname, NULL, 0);
+	error = -ENOENT;
+	if (!old_dentry->d_inode)
+		goto exit;
+
+	{
+		unsigned int flags = 0;
+		if (S_ISDIR(old_dentry->d_inode->i_mode))
+			flags = LOOKUP_SLASHOK;
+		new_dentry = lookup_dentry(newname, NULL, flags);
+	}
 
 	error = PTR_ERR(new_dentry);
 	if (IS_ERR(new_dentry))
@@ -1138,10 +1191,6 @@
 	old_dir = get_parent(old_dentry);
 
 	double_lock(new_dir, old_dir);
-
-	error = -ENOENT;
-	if (!old_dentry->d_inode)
-		goto exit_lock;
 
 	error = permission(old_dir->d_inode,MAY_WRITE | MAY_EXEC);
 	if (error)
diff -u --recursive --new-file v2.1.122/linux/fs/nfs/dir.c linux/fs/nfs/dir.c
--- v2.1.122/linux/fs/nfs/dir.c	Wed Sep  9 14:51:09 1998
+++ linux/fs/nfs/dir.c	Sat Sep 26 11:11:17 1998
@@ -416,23 +416,16 @@
 	 */
 	error = nfs_proc_lookup(NFS_DSERVER(parent), NFS_FH(parent), 
 				dentry->d_name.name, &fhandle, &fattr);
-	if (error) {
-printk("nfs_lookup_revalidate: error=%d\n", error);
+	if (error)
 		goto out_bad;
-	}
 
 	/* Inode number matches? */
-	if (fattr.fileid != inode->i_ino) {
-printk("nfs_lookup_revalidate: %s/%s inode mismatch, old=%ld, new=%u\n",
-parent->d_name.name, dentry->d_name.name, inode->i_ino, fattr.fileid);
+	if (fattr.fileid != inode->i_ino)
 		goto out_bad;
-	}
+
 	/* Filehandle matches? */
-	if (memcmp(dentry->d_fsdata, &fhandle, sizeof(struct nfs_fh))) {
-printk("nfs_lookup_revalidate: %s/%s fh changed\n",
-parent->d_name.name, dentry->d_name.name);
+	if (memcmp(dentry->d_fsdata, &fhandle, sizeof(struct nfs_fh)))
 		goto out_bad;
-	}
 
 out_valid:
 	return 1;
diff -u --recursive --new-file v2.1.122/linux/fs/nfs/inode.c linux/fs/nfs/inode.c
--- v2.1.122/linux/fs/nfs/inode.c	Wed Aug 26 11:37:43 1998
+++ linux/fs/nfs/inode.c	Fri Sep 25 16:26:30 1998
@@ -322,18 +322,20 @@
 	goto out_shutdown;
 
 out_no_iod:
-	printk("NFS: couldn't start rpciod!\n");
+	printk(KERN_WARNING "NFS: couldn't start rpciod!\n");
 out_shutdown:
 	rpc_shutdown_client(server->client);
-	goto out_unlock;
+	goto out_free_host;
 
 out_no_client:
-	printk("NFS: cannot create RPC client.\n");
+	printk(KERN_WARNING "NFS: cannot create RPC client.\n");
 	xprt_destroy(xprt);
-	goto out_unlock;
+	goto out_free_host;
 
 out_no_xprt:
-	printk("NFS: cannot create RPC transport.\n");
+	printk(KERN_WARNING "NFS: cannot create RPC transport.\n");
+
+out_free_host:
 	kfree(server->hostname);
 out_unlock:
 	unlock_super(sb);
@@ -393,9 +395,10 @@
 	tmp = head;
 	while ((tmp = tmp->next) != head) {
 		struct dentry *dentry = list_entry(tmp, struct dentry, d_alias);
+printk("nfs_free_dentries: found %s/%s, d_count=%d, hashed=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name,
+dentry->d_count, !list_empty(&dentry->d_hash));
 		if (!dentry->d_count) {
-printk("nfs_free_dentries: freeing %s/%s, i_count=%d\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, inode->i_count);
 			dget(dentry);
 			d_drop(dentry);
 			dput(dentry);
diff -u --recursive --new-file v2.1.122/linux/fs/nfs/symlink.c linux/fs/nfs/symlink.c
--- v2.1.122/linux/fs/nfs/symlink.c	Sun Jan  4 00:53:41 1998
+++ linux/fs/nfs/symlink.c	Sat Sep 19 13:43:09 1998
@@ -19,7 +19,7 @@
 #include <asm/uaccess.h>
 
 static int nfs_readlink(struct dentry *, char *, int);
-static struct dentry *nfs_follow_link(struct dentry *, struct dentry *);
+static struct dentry *nfs_follow_link(struct dentry *, struct dentry *, unsigned int);
 
 /*
  * symlinks can't do much...
@@ -68,7 +68,7 @@
 }
 
 static struct dentry *
-nfs_follow_link(struct dentry * dentry, struct dentry *base)
+nfs_follow_link(struct dentry * dentry, struct dentry *base, unsigned int follow)
 {
 	int error;
 	unsigned int len;
@@ -94,7 +94,7 @@
 	path[len] = 0;
 	kfree(mem);
 
-	result = lookup_dentry(path, base, 1);
+	result = lookup_dentry(path, base, follow);
 	kfree(path);
 out:
 	return result;
diff -u --recursive --new-file v2.1.122/linux/fs/pipe.c linux/fs/pipe.c
--- v2.1.122/linux/fs/pipe.c	Wed Aug 26 11:37:43 1998
+++ linux/fs/pipe.c	Sat Sep 19 15:13:19 1998
@@ -92,7 +92,7 @@
 			  size_t count, loff_t *ppos)
 {
 	struct inode * inode = filp->f_dentry->d_inode;
-	ssize_t chars = 0, free = 0, written = 0;
+	ssize_t chars = 0, free = 0, written = 0, err=0;
 	char *pipebuf;
 
 	if (ppos != &filp->f_pos)
@@ -107,16 +107,26 @@
 		free = count;
 	else
 		free = 1; /* can't do it atomically, wait for any free space */
+	up(&inode->i_sem);
+	if (down_interruptible(&inode->i_atomic_write)) {
+		down(&inode->i_sem);
+		return -ERESTARTSYS;
+	}
 	while (count>0) {
 		while ((PIPE_FREE(*inode) < free) || PIPE_LOCK(*inode)) {
 			if (!PIPE_READERS(*inode)) { /* no readers */
 				send_sig(SIGPIPE,current,0);
-				return written? :-EPIPE;
+				err = -EPIPE;
+				goto errout;
+			}
+			if (signal_pending(current)) {
+				err = -ERESTARTSYS;
+				goto errout;
+			}
+			if (filp->f_flags & O_NONBLOCK) {
+				err = -EAGAIN;
+				goto errout;
 			}
-			if (signal_pending(current))
-				return written? :-ERESTARTSYS;
-			if (filp->f_flags & O_NONBLOCK)
-				return written? :-EAGAIN;
 			interruptible_sleep_on(&PIPE_WAIT(*inode));
 		}
 		PIPE_LOCK(*inode)++;
@@ -139,7 +149,10 @@
 	}
 	inode->i_ctime = inode->i_mtime = CURRENT_TIME;
 	mark_inode_dirty(inode);
-	return written;
+errout:
+	up(&inode->i_atomic_write);
+	down(&inode->i_sem);
+	return written ? written : err;
 }
 
 static long long pipe_lseek(struct file * file, long long offset, int orig)
diff -u --recursive --new-file v2.1.122/linux/fs/proc/link.c linux/fs/proc/link.c
--- v2.1.122/linux/fs/proc/link.c	Wed Aug 26 11:37:43 1998
+++ linux/fs/proc/link.c	Sat Sep 19 15:11:37 1998
@@ -17,7 +17,7 @@
 #include <linux/stat.h>
 
 static int proc_readlink(struct dentry *, char *, int);
-static struct dentry * proc_follow_link(struct dentry *, struct dentry *);
+static struct dentry * proc_follow_link(struct dentry *, struct dentry *, unsigned int);
 
 /*
  * links can't do much...
@@ -57,7 +57,8 @@
 };
 
 static struct dentry * proc_follow_link(struct dentry *dentry,
-					struct dentry *base)
+					struct dentry *base,
+					unsigned int follow)
 {
 	struct inode *inode = dentry->d_inode;
 	struct task_struct *p;
@@ -172,7 +173,7 @@
 {
 	int error;
 
-	dentry = proc_follow_link(dentry, NULL);
+	dentry = proc_follow_link(dentry, NULL, 1);
 	error = PTR_ERR(dentry);
 	if (!IS_ERR(dentry)) {
 		error = -ENOENT;
diff -u --recursive --new-file v2.1.122/linux/fs/proc/mem.c linux/fs/proc/mem.c
--- v2.1.122/linux/fs/proc/mem.c	Wed Aug 26 11:37:43 1998
+++ linux/fs/proc/mem.c	Wed Sep 23 15:24:37 1998
@@ -215,7 +215,7 @@
 	pgd_t *src_dir, *dest_dir;
 	pmd_t *src_middle, *dest_middle;
 	pte_t *src_table, *dest_table;
-	unsigned long stmp, dtmp;
+	unsigned long stmp, dtmp, mapnr;
 	struct vm_area_struct *src_vma = NULL;
 	struct inode *inode = file->f_dentry->d_inode;
 	
@@ -296,7 +296,9 @@
 
 		set_pte(src_table, pte_mkdirty(*src_table));
 		set_pte(dest_table, *src_table);
-		atomic_inc(&mem_map[MAP_NR(pte_page(*src_table))].count);
+		mapnr = MAP_NR(pte_page(*src_table));
+		if (mapnr < max_mapnr)
+			atomic_inc(&mem_map[MAP_NR(pte_page(*src_table))].count);
 
 		stmp += PAGE_SIZE;
 		dtmp += PAGE_SIZE;
diff -u --recursive --new-file v2.1.122/linux/fs/proc/proc_devtree.c linux/fs/proc/proc_devtree.c
--- v2.1.122/linux/fs/proc/proc_devtree.c	Thu Apr 23 20:21:37 1998
+++ linux/fs/proc/proc_devtree.c	Sat Sep 19 13:43:36 1998
@@ -42,7 +42,7 @@
  */
 
 static int devtree_readlink(struct dentry *, char *, int);
-static struct dentry *devtree_follow_link(struct dentry *, struct dentry *);
+static struct dentry *devtree_follow_link(struct dentry *, struct dentry *, unsigned int);
 
 struct inode_operations devtree_symlink_inode_operations = {
 	NULL,			/* no file-operations */
@@ -66,7 +66,8 @@
 };
 
 static struct dentry *devtree_follow_link(struct dentry *dentry,
-					  struct dentry *base)
+					  struct dentry *base,
+					  unsigned int follow)
 {
 	struct inode *inode = dentry->d_inode;
 	struct proc_dir_entry * de;
@@ -74,7 +75,7 @@
 
 	de = (struct proc_dir_entry *) inode->u.generic_ip;
 	link = (char *) de->data;
-	return lookup_dentry(link, base, 1);
+	return lookup_dentry(link, base, follow);
 }
 
 static int devtree_readlink(struct dentry *dentry, char *buffer, int buflen)
diff -u --recursive --new-file v2.1.122/linux/fs/proc/root.c linux/fs/proc/root.c
--- v2.1.122/linux/fs/proc/root.c	Wed Aug 26 11:37:43 1998
+++ linux/fs/proc/root.c	Sat Sep 19 13:44:48 1998
@@ -55,9 +55,6 @@
 	NULL			/* can't fsync */
 };
 
-int proc_readlink(struct dentry * dentry, char * buffer, int buflen);
-struct dentry * proc_follow_link(struct dentry *dentry, struct dentry *base);
-
 /*
  * proc directories can do almost nothing..
  */
@@ -388,12 +385,13 @@
 }
 
 static struct dentry * proc_self_follow_link(struct dentry *dentry,
-						struct dentry *base)
+						struct dentry *base,
+						unsigned int follow)
 {
 	char tmp[30];
 
 	sprintf(tmp, "%d", current->pid);
-	return lookup_dentry(tmp, base, 1);
+	return lookup_dentry(tmp, base, follow);
 }	
 
 int proc_readlink(struct dentry * dentry, char * buffer, int buflen)
@@ -420,7 +418,7 @@
 	return len;
 }
 
-struct dentry * proc_follow_link(struct dentry * dentry, struct dentry *base)
+struct dentry * proc_follow_link(struct dentry * dentry, struct dentry *base, unsigned int follow)
 {
 	struct inode *inode = dentry->d_inode;
 	struct proc_dir_entry * de;
@@ -435,7 +433,7 @@
 	if (de->readlink_proc)
 		len = de->readlink_proc(de, page);
 
-	d = lookup_dentry(page, base, 1);
+	d = lookup_dentry(page, base, follow);
 	free_page((unsigned long) page);
 	return d;
 }
diff -u --recursive --new-file v2.1.122/linux/fs/romfs/inode.c linux/fs/romfs/inode.c
--- v2.1.122/linux/fs/romfs/inode.c	Thu Aug 27 19:56:30 1998
+++ linux/fs/romfs/inode.c	Sat Sep 19 13:45:18 1998
@@ -451,7 +451,8 @@
 }
 
 static struct dentry *romfs_follow_link(struct dentry *dentry,
-					struct dentry *base)
+					struct dentry *base,
+					unsigned int follow)
 {
 	struct inode *inode = dentry->d_inode;
 	char *link;
@@ -470,7 +471,7 @@
 	} else
 		link[len] = 0;
 
-	dentry = lookup_dentry(link, base, 1);
+	dentry = lookup_dentry(link, base, follow);
 	kfree(link);
 
 	if (0) {
diff -u --recursive --new-file v2.1.122/linux/fs/super.c linux/fs/super.c
--- v2.1.122/linux/fs/super.c	Thu Sep 17 17:53:38 1998
+++ linux/fs/super.c	Sat Sep 26 13:14:03 1998
@@ -868,17 +868,19 @@
 	struct vfsmount *vfsmnt;
 	int error;
 
-	down(&mount_sem);
 	error = -EACCES;
 	if (!(flags & MS_RDONLY) && dev && is_read_only(dev))
 		goto out;
-		/*flags |= MS_RDONLY;*/
 
+	/*
+	 * Do the lookup first to force automounting.
+	 */
 	dir_d = namei(dir_name);
 	error = PTR_ERR(dir_d);
 	if (IS_ERR(dir_d))
 		goto out;
 
+	down(&mount_sem);
 	error = -ENOTDIR;
 	if (!S_ISDIR(dir_d->d_inode->i_mode))
 		goto dput_and_out;
@@ -906,18 +908,16 @@
 
 	error = -ENOMEM;
 	vfsmnt = add_vfsmnt(sb, dev_name, dir_name);
-	if (!vfsmnt)
-		goto dput_and_out;
-	d_mount(dir_d, sb->s_root);
-	error = 0;	/* we don't dput(dir_d) - see umount */
-
-out:
-	up(&mount_sem);
-	return error;	
+	if (vfsmnt) {
+		d_mount(dget(dir_d), sb->s_root);
+		error = 0;
+	}
 
 dput_and_out:
 	dput(dir_d);
-	goto out;
+	up(&mount_sem);
+out:
+	return error;	
 }
 
 
diff -u --recursive --new-file v2.1.122/linux/fs/sysv/symlink.c linux/fs/sysv/symlink.c
--- v2.1.122/linux/fs/sysv/symlink.c	Sun Jan  4 00:53:41 1998
+++ linux/fs/sysv/symlink.c	Sat Sep 19 13:45:42 1998
@@ -21,7 +21,7 @@
 #include <asm/uaccess.h>
 
 static int sysv_readlink(struct dentry *, char *, int);
-static struct dentry *sysv_follow_link(struct dentry *, struct dentry *);
+static struct dentry *sysv_follow_link(struct dentry *, struct dentry *, unsigned int);
 
 /*
  * symlinks can't do much...
@@ -47,7 +47,8 @@
 };
 
 static struct dentry *sysv_follow_link(struct dentry * dentry,
-					struct dentry * base)
+					struct dentry * base,
+					unsigned int follow)
 {
 	struct inode *inode = dentry->d_inode;
 	struct buffer_head * bh;
@@ -58,7 +59,7 @@
 		return ERR_PTR(-EIO);
 	}
 	UPDATE_ATIME(inode);
-	base = lookup_dentry(bh->b_data, base, 1);
+	base = lookup_dentry(bh->b_data, base, follow);
 	brelse(bh);
 	return base;
 }
diff -u --recursive --new-file v2.1.122/linux/fs/ufs/symlink.c linux/fs/ufs/symlink.c
--- v2.1.122/linux/fs/ufs/symlink.c	Sat Sep  5 16:46:41 1998
+++ linux/fs/ufs/symlink.c	Sat Sep 19 13:46:01 1998
@@ -43,7 +43,7 @@
 
 
 static struct dentry * ufs_follow_link(struct dentry * dentry,
-	struct dentry * base)
+	struct dentry * base, unsigned int follow)
 {
 	struct inode * inode;
 	struct buffer_head * bh;
@@ -67,7 +67,7 @@
 		link = (char *) inode->u.ufs_i.i_u1.i_symlink;
 	}
 	UPDATE_ATIME(inode);
-	base = lookup_dentry(link, base, 1);
+	base = lookup_dentry(link, base, follow);
 	if (bh)
 		brelse(bh);
 	UFSD(("EXIT\n"))
diff -u --recursive --new-file v2.1.122/linux/fs/umsdos/dir.c linux/fs/umsdos/dir.c
--- v2.1.122/linux/fs/umsdos/dir.c	Thu Sep 17 17:53:38 1998
+++ linux/fs/umsdos/dir.c	Fri Sep 25 16:30:07 1998
@@ -20,6 +20,7 @@
 #include <asm/uaccess.h>
 
 #define UMSDOS_SPECIAL_DIRFPOS	3
+extern struct dentry *saved_root;
 extern struct inode *pseudo_root;
 
 /* #define UMSDOS_DEBUG_VERBOSE 1 */
@@ -53,42 +54,6 @@
 	NULL,
 };
 
-/*
- * This needs to have the parent dentry passed to it.
- * N.B. Try to get rid of this soon!
- */
-int compat_msdos_create (struct inode *dir, const char *name, int len, 
-			int mode, struct inode **inode)
-{
-	int ret;
-	struct dentry *dentry, *d_dir;
-
-	check_inode (dir);
-	ret = -ENOMEM;
-	d_dir = geti_dentry (dir);
-	if (!d_dir) {
-printk(KERN_ERR "compat_msdos_create: flaky i_dentry didn't work\n");
-		goto out;
-	}
-	dget(d_dir);
-	dentry = creat_dentry (name, len, NULL, d_dir);
-	dput(d_dir);
-	if (!dentry)
-		goto out;
-
-	check_dentry_path (dentry, "compat_msdos_create START");
-	ret = msdos_create (dir, dentry, mode);
-	check_dentry_path (dentry, "compat_msdos_create END");
-	if (ret)
-		goto out;
-	if (inode != NULL)
-		*inode = dentry->d_inode;
-
-	check_inode (dir);
-out:
-	return ret;
-}
-
 
 /*
  * So  grep *  doesn't complain in the presence of directories.
@@ -203,9 +168,11 @@
 	ret = PTR_ERR(demd);
 	if (IS_ERR(demd))
 		goto out_end;
-	ret = 0;
+	ret = -EIO;
 	if (!demd->d_inode) {
-printk("no EMD file??\n");
+		printk(KERN_WARNING 
+			"umsdos_readir_x: EMD file %s/%s not found\n",
+			demd->d_parent->d_name.name, demd->d_name.name);
 		goto out_dput;
 	}
 
@@ -404,18 +371,17 @@
  * does this automatically.
  */
 
-void umsdos_lookup_patch (struct inode *dir, struct inode *inode,
-			 struct umsdos_dirent *entry, off_t emd_pos)
+void umsdos_lookup_patch_new(struct dentry *dentry, struct umsdos_info *info)
 {
-	if (inode->i_sb != dir->i_sb)
-		goto out;
-	if (umsdos_isinit (inode))
-		goto out;
+	struct inode *inode = dentry->d_inode;
+	struct umsdos_dirent *entry = &info->entry;
 
-	if (S_ISDIR (inode->i_mode))
-		umsdos_lockcreate (inode);
-	if (umsdos_isinit (inode))
-		goto out_unlock;
+	/*
+	 * This part of the initialization depends only on i_patched.
+	 */
+	if (inode->u.umsdos_i.i_patched)
+		goto out;
+	inode->u.umsdos_i.i_patched = 1;
 
 	if (S_ISREG (entry->mode))
 		entry->mtime = inode->i_mtime;
@@ -444,150 +410,18 @@
 				"UMSDOS:  lookup_patch entry->nlink < 1 ???\n");
 		}
 	}
-	umsdos_patch_inode (inode, dir, emd_pos);
+	/*
+	 * The mode may have changed, so patch the inode again.
+	 */
+	umsdos_patch_dentry_inode(dentry, info->f_pos);
+	umsdos_set_dirinfo_new(dentry, info->f_pos);
 
-out_unlock:
-	if (S_ISDIR (inode->i_mode))
-		umsdos_unlockcreate (inode);
-	if (inode->u.umsdos_i.i_emd_owner == 0)
-		printk (KERN_WARNING "UMSDOS:  emd_owner still 0?\n");
 out:
 	return;
 }
 
 
 /*
- * The preferred interface to the above routine ...
- */
-void umsdos_lookup_patch_new(struct dentry *dentry, struct umsdos_dirent *entry,
-				off_t emd_pos)
-{
-	umsdos_lookup_patch(dentry->d_parent->d_inode, dentry->d_inode, entry,
-				emd_pos);
-}
-
-
-struct UMSDOS_DIRENT_K {
-	off_t f_pos;		/* will hold the offset of the entry in EMD */
-	ino_t ino;
-};
-
-
-/*
- * Just to record the offset of one entry.
- */
-
-static int umsdos_filldir_k (	    void *buf,
-				    const char *name,
-				    int len,
-				    off_t offset,
-				    ino_t ino)
-{
-	struct UMSDOS_DIRENT_K *d = (struct UMSDOS_DIRENT_K *) buf;
-
-	d->f_pos = offset;
-	d->ino = ino;
-	return 0;
-}
-
-struct UMSDOS_DIR_SEARCH {
-	struct umsdos_dirent *entry;
-	int found;
-	ino_t search_ino;
-};
-
-static int umsdos_dir_search (	     void *buf,
-				     const char *name,
-				     int len,
-				     off_t offset,
-				     ino_t ino)
-{
-	int ret = 0;
-	struct UMSDOS_DIR_SEARCH *d = (struct UMSDOS_DIR_SEARCH *) buf;
-
-	if (d->search_ino == ino) {
-		d->found = 1;
-		memcpy (d->entry->name, name, len);
-		d->entry->name[len] = '\0';
-		d->entry->name_len = len;
-		ret = 1;	/* So fat_readdir will terminate */
-	}
-	return ret;
-}
-
-
-
-/*
- * Locate the directory entry for a dentry in its parent 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_dentry_to_entry(struct dentry *dentry, struct umsdos_dirent *entry)
-{
-	struct dentry *parent = dentry->d_parent;
-	struct inode *inode = dentry->d_inode;
-	int ret = -ENOENT, err;
-	struct file filp;
-	struct UMSDOS_DIR_SEARCH bufsrch;
-	struct UMSDOS_DIRENT_K bufk;
-
-	if (pseudo_root && 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;
-		goto out;
-	}
-
-	/* initialize the file */
-	fill_new_filp (&filp, parent);
-
-	if (!umsdos_have_emd(parent)) {
-		/* This is a DOS directory. */
-		filp.f_pos = 0;
-		bufsrch.entry = entry;
-		bufsrch.search_ino = inode->i_ino;
-		fat_readdir (&filp, &bufsrch, umsdos_dir_search);
-		if (bufsrch.found) {
-			ret = 0;
-			inode->u.umsdos_i.i_emd_owner = 0;
-if (!S_ISDIR(inode->i_mode))
-printk("UMSDOS: %s/%s not a directory!\n",
-dentry->d_parent->d_name.name, dentry->d_name.name);
-			/* N.B. why call this? not always a dir ... */
-			umsdos_setup_dir(dentry);
-		}
-		goto out;
-	}
-
-	/* skip . and .. see umsdos_readdir_x() */
-	filp.f_pos = UMSDOS_SPECIAL_DIRFPOS;
-	while (1) {
-		err = umsdos_readdir_x (parent->d_inode, &filp, &bufk, 1, 
-				entry, 0, umsdos_filldir_k);
-		if (err < 0) { 
-			printk ("umsdos_dentry_to_entry: ino=%ld, err=%d\n",
-				inode->i_ino, err);
-			break;
-		}
-		if (bufk.ino == inode->i_ino) {
-			ret = 0;
-			umsdos_lookup_patch_new(dentry, entry, bufk.f_pos);
-			break;
-		}
-	}
-out:
-	return ret;
-}
-
-
-/*
  * Return != 0 if an entry is the pseudo DOS entry in the pseudo root.
  */
 
@@ -627,8 +461,6 @@
 
 int umsdos_lookup_x (struct inode *dir, struct dentry *dentry, int nopseudo)
 {				
-	const char *name = dentry->d_name.name;
-	int len = dentry->d_name.len;
 	struct dentry *dret = NULL;
 	struct inode *inode;
 	int ret = -ENOENT;
@@ -640,28 +472,13 @@
 #endif
 
 	umsdos_startlookup (dir);
-	/* this shouldn't happen ... */
-	if (len == 1 && name[0] == '.') {
-		printk("umsdos_lookup_x: UMSDOS broken, please report!\n");
-		goto out;
-	}
-
-	/* this shouldn't happen ... */
-	if (len == 2 && name[0] == '.' && name[1] == '.') {
-		printk("umsdos_lookup_x: UMSDOS broken, please report!\n");
-		goto out;
-	}
-
 	if (umsdos_is_pseudodos (dir, dentry)) {
 		/* #Specification: pseudo root / lookup(DOS)
 		 * A lookup of DOS in the pseudo root will always succeed
 		 * and return the inode of the real root.
 		 */
-		inode = iget(dir->i_sb, UMSDOS_ROOT_INO);
-		if (inode)
-			goto out_add;
-		ret = -ENOMEM;
-		goto out;
+		inode = saved_root->d_inode;
+		goto out_add;
 	}
 
 	ret = umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info);
@@ -693,7 +510,7 @@
 	inode = dret->d_inode;
 	if (!inode)
 		goto out_remove;
-	umsdos_lookup_patch_new(dret, &info.entry, info.f_pos);
+	umsdos_lookup_patch_new(dret, &info);
 #ifdef UMSDOS_DEBUG_VERBOSE
 printk("umsdos_lookup_x: found %s/%s, ino=%ld\n", 
 dret->d_parent->d_name.name, dret->d_name.name, dret->d_inode->i_ino);
@@ -726,6 +543,7 @@
 		 * mode.
 		 */
 printk(KERN_WARNING "umsdos_lookup_x: untested, inode == Pseudo_root\n");
+		ret = -ENOENT;
 		goto out_dput;
 	}
 
diff -u --recursive --new-file v2.1.122/linux/fs/umsdos/emd.c linux/fs/umsdos/emd.c
--- v2.1.122/linux/fs/umsdos/emd.c	Thu Sep 17 17:53:38 1998
+++ linux/fs/umsdos/emd.c	Fri Sep 25 16:30:07 1998
@@ -189,7 +189,9 @@
 
 
 /*
- * Create the EMD dentry for a directory.
+ * Lookup the EMD dentry for a directory.
+ *
+ * Note: the caller must hold a lock on the parent directory.
  */
 struct dentry *umsdos_get_emd_dentry(struct dentry *parent)
 {
@@ -202,6 +204,8 @@
 
 /*
  * Check whether a directory has an EMD file.
+ *
+ * Note: the caller must hold a lock on the parent directory.
  */
 int umsdos_have_emd(struct dentry *dir)
 {
@@ -219,11 +223,12 @@
 /*
  * Create the EMD file for a directory if it doesn't
  * already exist. Returns 0 or an error code.
+ *
+ * Note: the caller must hold a lock on the parent directory.
  */
 int umsdos_make_emd(struct dentry *parent)
 {
 	struct dentry *demd = umsdos_get_emd_dentry(parent);
-	struct inode *inode;
 	int err = PTR_ERR(demd);
 
 	if (IS_ERR(demd)) {
@@ -234,8 +239,7 @@
 
 	/* already created? */
 	err = 0;
-	inode = demd->d_inode;
-	if (inode)
+	if (demd->d_inode)
 		goto out_set;
 
 Printk(("umsdos_make_emd: creating EMD %s/%s\n",
@@ -244,15 +248,12 @@
 	err = msdos_create(parent->d_inode, demd, S_IFREG | 0777);
 	if (err) {
 		printk (KERN_WARNING
-			"UMSDOS: create %s/%s failed, err=%d\n",
+			"umsdos_make_emd: create %s/%s failed, err=%d\n",
 			parent->d_name.name, demd->d_name.name, err);
 		goto out_dput;
 	}
-	inode = demd->d_inode;
 out_set:
-	parent->d_inode->u.umsdos_i.i_emd_dir = inode->i_ino;
-	/* Disable UMSDOS_notify_change() for EMD file */
-	inode->u.umsdos_i.i_emd_owner = 0xffffffff;
+	parent->d_inode->u.umsdos_i.i_emd_dir = demd->d_inode->i_ino;
 
 out_dput:
 	dput(demd);
@@ -262,92 +263,6 @@
 
 
 /*
- * Locate the EMD file in a directory.
- * 
- * Return NULL if error, dir->u.umsdos_i.emd_inode if OK. 
- * Caller must iput() returned inode when finished with it!
- * Note: deprecated; get rid of this soon!
- */
-
-struct inode *umsdos_emd_dir_lookup (struct inode *dir, int creat)
-{
-	struct inode *ret = NULL;
-	struct dentry *d_dir=NULL, *dlook=NULL;
-	int rv;
-
-	Printk ((KERN_DEBUG "Entering umsdos_emd_dir_lookup\n"));
-	if (!dir) {
-		printk (KERN_CRIT "umsdos_emd_dir_lookup: FATAL, dir=NULL!\n");
-		goto out;
-	}
-	check_inode (dir);
-	
-	if (dir->u.umsdos_i.i_emd_dir != 0) {
-		ret = iget (dir->i_sb, dir->u.umsdos_i.i_emd_dir);
-		Printk (("umsdos_emd_dir_lookup: deja trouve %ld %p\n",
-			dir->u.umsdos_i.i_emd_dir, ret));
-		goto out;
-	}
-
-	PRINTK ((KERN_DEBUG "umsdos /mn/: Looking for %.*s -",
-		UMSDOS_EMD_NAMELEN, UMSDOS_EMD_FILE));
-
-	d_dir = geti_dentry (dir);
-	if (!d_dir) {
-printk("UMSDOS: flaky i_dentry hack failed\n");
-		goto out;
-	}
-	dlook = creat_dentry (UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, NULL, d_dir);
-	if (!dlook)
-		goto out;
-	rv = msdos_lookup (dir, dlook);
-		
-	PRINTK ((KERN_DEBUG "-returned %d\n", rv));
-	Printk ((KERN_INFO "emd_dir_lookup "));
-		
-	ret = dlook->d_inode;
-	if (ret) {
-		Printk (("Found --linux "));
-		dir->u.umsdos_i.i_emd_dir = ret->i_ino;
-		ret->i_count++;	/* we'll need the inode */
-		check_inode (ret);
-	} else if (creat) {
-		int code;
-			
-		Printk ((" * ERROR * /mn/: creat not yet implemented? not fixed? "));
-		Printk (("avant create "));
-
-		check_inode (ret);
-		code = compat_msdos_create (dir, UMSDOS_EMD_FILE, 
-						UMSDOS_EMD_NAMELEN, 
-						S_IFREG | 0777, &ret);
-		check_inode (ret);
-		Printk (("Creat EMD code %d ret %p ", code, ret));
-		if (ret != NULL) {
-			Printk ((" ino=%lu", ret->i_ino));
-			dir->u.umsdos_i.i_emd_dir = ret->i_ino;
-		} else {
-			printk (KERN_WARNING "UMSDOS: Can't create EMD file\n");
-		}
-	}
-	dput(dlook);
-		
-	if (ret != NULL) {
-		/* Disable UMSDOS_notify_change() for EMD file */
-		ret->u.umsdos_i.i_emd_owner = 0xffffffff;
-	}
-
-out:
-#if UMS_DEBUG
-	Printk ((KERN_DEBUG "umsdos_emd_dir_lookup returning %p /mn/\n", ret));
-	if (ret != NULL)
-		Printk ((KERN_DEBUG " returning ino=%lu\n", ret->i_ino));
-#endif
-	return ret;
-}
-
-
-/*
  * Read an entry from the EMD file.
  * Support variable length record.
  * Return -EIO if error, 0 if OK.
@@ -387,6 +302,8 @@
 /*
  * Write an entry in the EMD file.
  * Return 0 if OK, -EIO if some error.
+ *
+ * Note: the caller must hold a lock on the parent directory.
  */
 static int umsdos_writeentry (struct dentry *parent, struct umsdos_info *info,
 				int free_entry)
@@ -405,8 +322,9 @@
 	/* make sure there's an EMD file */
 	ret = -EIO;
 	if (!emd_dentry->d_inode) {
-printk("umsdos_writeentry: no EMD file in %s/%s\n",
-parent->d_parent->d_name.name, parent->d_name.name);
+		printk(KERN_WARNING
+			"umsdos_writeentry: no EMD file in %s/%s\n",
+			parent->d_parent->d_name.name, parent->d_name.name);
 		goto out_dput;
 	}
 
@@ -468,6 +386,8 @@
  * Unread bytes are simply moved to the beginning.
  * 
  * Return -ENOENT if EOF, 0 if OK, a negative error code if any problem.
+ *
+ * Note: the caller must hold a lock on the parent directory.
  */
 
 static int umsdos_fillbuf (struct find_buffer *buf)
@@ -518,6 +438,7 @@
  * All this to say that umsdos_writeentry must be called after this
  * function since it relies on the f_pos field of info.
  *
+ * Note: the caller must hold a lock on the parent directory.
  */
 /* #Specification: EMD file structure
  * The EMD file uses a fairly simple layout.  It is made of records
diff -u --recursive --new-file v2.1.122/linux/fs/umsdos/inode.c linux/fs/umsdos/inode.c
--- v2.1.122/linux/fs/umsdos/inode.c	Thu Sep 17 17:53:38 1998
+++ linux/fs/umsdos/inode.c	Fri Sep 25 16:30:07 1998
@@ -23,35 +23,11 @@
 extern struct inode_operations umsdos_rdir_inode_operations;
 
 
+struct dentry *saved_root = NULL;	/* Original root if changed */
 struct inode *pseudo_root = NULL;	/* Useful to simulate the pseudo DOS */
 					/* directory. See UMSDOS_readdir_x() */
 
-
-/*
- * returns inode->i_dentry
- * Note: Deprecated; won't work reliably
- */
-
-struct dentry *geti_dentry (struct inode *inode)
-{
-	struct dentry *ret;
-
-	if (!inode) {
-		printk (KERN_ERR "geti_dentry: ERROR: inode is NULL!\n");
-		return NULL;
-	}
-	if (list_empty(&inode->i_dentry)) {
-		printk (KERN_WARNING 
-			"geti_dentry: WARNING: no dentry for inode %ld\n",
-			inode->i_ino);
-		return NULL;
-	}
-	ret = list_entry (inode->i_dentry.next, struct dentry, d_alias);
-
-	PRINTK ((KERN_DEBUG "geti_dentry : inode %lu, dentry is %s/%s\n", 
-		inode->i_ino, ret->d_parent->d_name.name, ret->d_name.name));
-	return ret;
-}
+static struct dentry *check_pseudo_root(struct super_block *);
 
 
 /*
@@ -70,61 +46,6 @@
 }
 
 
-/*
- * makes dentry. for name name with length len.
- * if inode is not NULL, puts it also.
- * Note: Deprecated; use umsdos_lookup_dentry
- */
-
-struct dentry *creat_dentry (const char *name, const int len, 
-				struct inode *inode, struct dentry *parent)
-{
-/* FIXME /mn/: parent is not passed many times... if it is not, dentry should be destroyed before someone else gets to use it */
-
-	struct dentry *ret;
-	struct qstr qname;
-
-	if (inode)
-		Printk ((KERN_DEBUG "creat_dentry: creating dentry with inode=%lu for %.*s\n", inode->i_ino, len, name));
-	else
-		Printk ((KERN_DEBUG "creat_dentry: creating empty dentry for %.*s\n", len, name));
-
-	qname.name = name;
-	qname.len = len;
-	qname.hash = full_name_hash (name, len);
-
-	ret = d_alloc (parent, &qname);		/* create new dentry */
-
-	if (parent) {
-#if 0
-		Printk ((KERN_DEBUG "creat_dentry: cloning parent d_op !\n"));
-		ret->d_op = parent->d_op;
-#else
-		ret->d_op = NULL;
-#endif		
-	} else {
-		ret->d_parent = ret;
-		Printk ((KERN_WARNING "creat_dentry: WARNING: NO parent! faking root! beware !\n"));
-	}
-
-
-	if (inode) {
-		/* try to fill it in if available. If available in 
-		 * parent->d_sb, d_alloc will add it automatically
-		 */
-		if (!ret->d_sb)	ret->d_sb = inode->i_sb;
-		d_add (ret, inode);
-	}
-
-	if (!ret->d_sb) {
-		printk (KERN_ERR "creat_dentry: ERROR: NO d_sb !\n");
-	} else if (!ret->d_sb->s_dev) {
-		printk (KERN_WARNING "creat_dentry: WARNING: NO s_dev. Ugh. !\n");
-	}
-	
-	return ret;
-}
-
 
 void UMSDOS_put_inode (struct inode *inode)
 {
@@ -146,18 +67,23 @@
 void UMSDOS_put_super (struct super_block *sb)
 {
 	Printk ((KERN_DEBUG "UMSDOS_put_super: entering\n"));
+	if (saved_root) {
+		shrink_dcache_parent(saved_root);
+printk("UMSDOS_put_super: freeing saved root, d_count=%d\n",
+saved_root->d_count);
+		dput(saved_root);
+		saved_root = NULL;
+		pseudo_root = NULL;
+	}
 	msdos_put_super (sb);
 	MOD_DEC_USE_COUNT;
 }
 
 
 /*
- * Complete the setup of an directory dentry.
- * First, it completes the function pointers, then
- * it locates the EMD file. If the EMD is there, then plug the
+ * Complete the setup of a directory dentry based on its
+ * EMD/non-EMD status.  If it has an EMD, then plug the
  * umsdos function table. If not, use the msdos one.
- *
- * {i,d}_counts are untouched by this function.
  */
 void umsdos_setup_dir(struct dentry *dir)
 {
@@ -176,40 +102,6 @@
 	}
 }
 
-/*
- * 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.
- *
- * {i,d}_counts are untouched by this function.
- * Note: Deprecated; use above function if possible.
- */
-void umsdos_setup_dir_inode (struct inode *inode)
-{
-	struct inode *emd_dir;
-
-	inode->u.umsdos_i.i_emd_dir = 0;
-
-	Printk ((KERN_DEBUG 
-		"umsdos_setup_dir_inode: Entering for inode=%lu\n",
-		 inode->i_ino));
-	check_inode (inode);
-	emd_dir = umsdos_emd_dir_lookup (inode, 0);
-	Printk ((KERN_DEBUG "umsdos_setup_dir_inode: "
-		"umsdos_emd_dir_lookup for inode=%lu returned %p\n",
-		inode->i_ino, emd_dir));
-	check_inode (inode);
-	check_inode (emd_dir);
-
-	inode->i_op = &umsdos_rdir_inode_operations;
-	if (emd_dir) {
-		Printk ((KERN_DEBUG "umsdos_setup_dir_inode: using EMD.\n"));
-		inode->i_op = &umsdos_dir_inode_operations;
-		iput (emd_dir);
-	}
-}
-
 
 /*
  * Add some info into an inode so it can find its owner quickly
@@ -224,34 +116,11 @@
 
 	/* now check the EMD file */
 	demd = umsdos_get_emd_dentry(dentry->d_parent);
-	if (IS_ERR(demd)) {
-		goto out;
-	}
-	if (demd->d_inode) {
-		inode->u.umsdos_i.i_emd_owner = demd->d_inode->i_ino;
+	if (!IS_ERR(demd)) {
+		if (demd->d_inode)
+			inode->u.umsdos_i.i_emd_owner = demd->d_inode->i_ino;
+		dput(demd);
 	}
-	dput (demd);
-out:
-	return;
-}
-
-
-/*
- * Add some info into an inode so it can find its owner quickly
- * Note: Deprecated; use above function if possible.
- */
-void umsdos_set_dirinfo (struct inode *inode, struct inode *dir, off_t f_pos)
-{
-	struct inode *emd_owner = umsdos_emd_dir_lookup (dir, 1);
-
-	if (!emd_owner)
-		goto out;
-	Printk (("umsdos_set_dirinfo: emd_owner is %lu for dir %lu\n", 
-		emd_owner->i_ino, dir->i_ino));
-	inode->u.umsdos_i.i_emd_owner = emd_owner->i_ino;
-	inode->u.umsdos_i.pos = f_pos;
-	iput (emd_owner);
-out:
 	return;
 }
 
@@ -262,28 +131,12 @@
  */
 int umsdos_isinit (struct inode *inode)
 {
-	return inode->u.umsdos_i.i_emd_owner != 0;
+	return 0; /* inode->u.umsdos_i.i_emd_owner != 0; */
 }
 
 
 /*
  * Connect the proper tables in the inode and add some info.
- * i_counts is not changed.
- *
- * 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 too 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 accommodate 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.
  */
 /* #Specification: inode / umsdos info
  * The first time an inode is seen (inode->i_count == 1),
@@ -291,88 +144,45 @@
  * is tagged to this inode. It allows operations such as
  * notify_change to be handled.
  */
-void umsdos_patch_inode (struct inode *inode, struct inode *dir, off_t f_pos)
+void umsdos_patch_dentry_inode(struct dentry *dentry, off_t f_pos)
 {
-	Printk ((KERN_DEBUG "Entering umsdos_patch_inode for inode=%lu\n",
-		inode->i_ino));
+	struct inode *inode = dentry->d_inode;
+
+Printk (("umsdos_patch_dentry_inode: inode=%lu\n", inode->i_ino));
 
-	if (umsdos_isinit (inode))
-		goto already_init;
+	/*
+	 * Classify the inode based on EMD/non-EMD status.
+	 */
+Printk (("umsdos_patch_inode: call x_set_dirinfo(%p,%p,%lu)\n",
+inode, dir, f_pos));
+	umsdos_set_dirinfo_new(dentry, f_pos);
 
 	inode->u.umsdos_i.i_emd_dir = 0;
 	if (S_ISREG (inode->i_mode)) {
 		if (MSDOS_SB (inode->i_sb)->cvf_format) {
 			if (MSDOS_SB (inode->i_sb)->cvf_format->flags & CVF_USE_READPAGE) {
-Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: setting i_op = umsdos_file_inode_operations_readpage\n"));
 				inode->i_op = &umsdos_file_inode_operations_readpage;
 			} else {
-Printk ((KERN_DEBUG "umsdos_patch_inode: umsdos_file_inode_ops_no_bmap\n"));
 				inode->i_op = &umsdos_file_inode_operations_no_bmap;
 			}
 		} else {
 			if (inode->i_op->bmap != NULL) {
-Printk ((KERN_DEBUG "umsdos_patch_inode: umsdos_file_inode_ops\n"));
 				inode->i_op = &umsdos_file_inode_operations;
 			} else {
-Printk ((KERN_DEBUG "umsdos_patch_inode: umsdos_file_inode_ops_no_bmap\n"));
 				inode->i_op = &umsdos_file_inode_operations_no_bmap;
 			}
 		}
 	} else if (S_ISDIR (inode->i_mode)) {
-		if (dir != NULL) {
-			umsdos_setup_dir_inode (inode);
-		}
+		umsdos_setup_dir(dentry);
 	} else if (S_ISLNK (inode->i_mode)) {
-		Printk ((KERN_DEBUG 
-			"umsdos_patch_inode: umsdos_symlink_inode_ops\n"));
 		inode->i_op = &umsdos_symlink_inode_operations;
 	} else if (S_ISCHR (inode->i_mode)) {
-		Printk ((KERN_DEBUG "umsdos_patch_inode: chrdev_inode_ops\n"));
 		inode->i_op = &chrdev_inode_operations;
 	} else if (S_ISBLK (inode->i_mode)) {
-		Printk ((KERN_DEBUG "umsdos_patch_inode: blkdev_inode_ops\n"));
 		inode->i_op = &blkdev_inode_operations;
 	} else if (S_ISFIFO (inode->i_mode)) {
-		Printk ((KERN_DEBUG "umsdos_patch_inode /mn/: uhm, init_fifo\n"));
 		init_fifo (inode);
 	}
-	if (dir != NULL) {
-		/*
-		 * This is done last because it also control the
-		 * status of umsdos_isinit()
-		 */
-		Printk ((KERN_DEBUG 
-			"umsdos_patch_inode: call x_set_dirinfo(%p,%p,%lu)\n",
-			 inode, dir, f_pos));
-		umsdos_set_dirinfo (inode, dir, f_pos);
-	}
-	return;
-
-already_init:
-	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);
-		if (!emd_owner)
-			goto out;
-		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);
-		}
-		iput (emd_owner);
-	out:
-		return;
-	}
-}
-
-/*
- * Patch the inode in a dentry.
- */
-void umsdos_patch_dentry_inode(struct dentry *dentry, off_t f_pos)
-{
-	umsdos_patch_inode(dentry->d_inode, dentry->d_parent->d_inode, f_pos);
 }
 
 
@@ -382,40 +192,43 @@
 /* #Specification: Inode / post initialisation
  * To completely 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
+ * not available the first time the inode is accessed, so 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.
+ * New inodes are obtained by the lookup and create routines, and
+ * each of these must ensure that the inode gets patched.
  */
 void UMSDOS_read_inode (struct inode *inode)
 {
 	PRINTK ((KERN_DEBUG "UMSDOS_read_inode %p ino = %lu ",
 		inode, inode->i_ino));
 	msdos_read_inode (inode);
-	PRINTK (("ino after msdos_read_inode= %lu i_count=%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
-		|| waitqueue_active (&inode->u.umsdos_i.u.dir_info.p))) {
-		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));
-	}
 
-	/* N.B. Defer this until we have a dentry ... */
-	umsdos_patch_inode (inode, NULL, 0);
+	/* inode needs patching */
+	inode->u.umsdos_i.i_patched = 0;
 }
 
 
+int umsdos_notify_change_locked(struct dentry *, struct iattr *);
+/*
+ * lock the parent dir before starting ...
+ */
 int UMSDOS_notify_change (struct dentry *dentry, struct iattr *attr)
 {
+	struct inode *dir = dentry->d_parent->d_inode;
+	int ret;
+
+	down(&dir->i_sem);
+	ret = umsdos_notify_change_locked(dentry, attr);
+	up(&dir->i_sem);
+	return ret;
+}
+
+/*
+ * Must be called with the parent lock held.
+ */
+int umsdos_notify_change_locked(struct dentry *dentry, struct iattr *attr)
+{
 	struct inode *inode = dentry->d_inode;
 	struct dentry *demd;
 	int ret;
@@ -433,16 +246,15 @@
 	}
 
 	if (inode->i_nlink == 0)
-		goto out_nolink;
-
+		goto out;
 	if (inode->i_ino == UMSDOS_ROOT_INO)
-		goto out_nolink;
+		goto out;
 
 	/* get the EMD file dentry */
 	demd = umsdos_get_emd_dentry(dentry->d_parent);
 	ret = PTR_ERR(demd);
 	if (IS_ERR(demd))
-		goto out_nolink;
+		goto out;
 	ret = -EPERM;
 	if (!demd->d_inode) {
 		printk(KERN_WARNING
@@ -509,10 +321,9 @@
 
 out_dput:
 	dput(demd);
-out_nolink:
+out:
 	if (ret == 0)
 		inode_setattr (inode, attr);
-out:
 	return ret;
 }
 
@@ -562,7 +373,7 @@
 				      int silent)
 {
 	struct super_block *res;
-	struct inode *pseudo = NULL;
+	struct dentry *new_root;
 
 	MOD_INC_USE_COUNT;
 	MSDOS_SB(sb)->options.isvfat = 0;
@@ -583,14 +394,19 @@
 
 	/* install our dentry operations ... */
 	sb->s_root->d_op = &umsdos_dentry_operations;
-	pseudo = sb->s_root->d_inode;
-	umsdos_setup_dir(sb->s_root);
+	umsdos_patch_dentry_inode(sb->s_root, 0);
 
-#if 0
-	if (pseudo) {
-		pseudo_root_stuff();
+	/* Check whether to change to the /linux root */
+	new_root = check_pseudo_root(sb);
+	if (new_root) {
+		pseudo_root = new_root->d_inode;
+		/* sanity check */
+		if (new_root->d_op != &umsdos_dentry_operations)
+			printk("umsdos_read_super: pseudo-root wrong ops!\n");
+		saved_root = sb->s_root;
+		sb->s_root = new_root;
+		printk(KERN_INFO "UMSDOS: changed to alternate root\n");
 	}
-#endif
 
 	/* if d_count is not 1, mount will fail with -EBUSY! */
 	if (sb->s_root->d_count > 1) {
@@ -606,78 +422,50 @@
 }
 
 /*
- * FIXME URGENT:
- * disable pseudo root-for the moment of testing. 
- * re-enable this before release !
+ * Check for an alternate root if we're the root device.
  */
-#if 0
-void pseudo_root_stuff(void)
+static struct dentry *check_pseudo_root(struct super_block *sb)
 {
-	struct dentry *root, *etc, *etc_rc, *sbin, *init = NULL;
-
-	root = creat_dentry (UMSDOS_PSDROOT_NAME, 
-				strlen (UMSDOS_PSDROOT_NAME),
-			 	NULL, res->s_root);
-	sbin = creat_dentry ("sbin", 4, NULL, root);
-
-	Printk ((KERN_DEBUG "Mounting root\n"));
-	if (msdos_lookup (pseudo, root) == 0
-	    && (root->d_inode != NULL)
-	    && S_ISDIR (root->d_inode->i_mode)) {
-
-		int pseudo_ok = 0;
-
-Printk ((KERN_DEBUG "/%s is there\n", UMSDOS_PSDROOT_NAME));
-		etc = creat_dentry ("etc", 3, NULL, root);
+	struct dentry *root, *init;
 
+	/*
+	 * Check whether we're mounted as the root device.
+	 * If so, this should be the only superblock.
+	 */
+	if (sb->s_list.next->next != &sb->s_list)
+		goto out_noroot;
+printk("check_pseudo_root: mounted as root\n");
+
+	root = lookup_dentry(UMSDOS_PSDROOT_NAME, dget(sb->s_root), 0); 
+	if (IS_ERR(root))
+		goto out_noroot;
+	if (!root->d_inode)
+		goto out_dput;
+printk("check_pseudo_root: found %s/%s\n",
+root->d_parent->d_name.name, root->d_name.name);
 
-		if (msdos_lookup (pseudo, etc) == 0
-		    && S_ISDIR (etc->d_inode->i_mode)) {
-
-Printk ((KERN_DEBUG "/%s/etc is there\n", UMSDOS_PSDROOT_NAME));
-
-			init = creat_dentry ("init", 4, NULL, etc);
-			etc_rc = creat_dentry ("rc", 2, NULL, etc);
-
-			if ((msdos_lookup (pseudo, init) == 0
-			     && S_ISREG (init->d_inode->i_mode))
-			    || (msdos_lookup (pseudo, etc_rc) == 0
-			 && S_ISREG (etc_rc->d_inode->i_mode))) {
-				pseudo_ok = 1;
-			}
+	/* look for /sbin/init */
+	init = lookup_dentry("sbin/init", dget(root), 0);
+	if (!IS_ERR(init)) {
+		if (init->d_inode)
+			goto root_ok;
+		dput(init);
+	}
+	/* check for other files? */
+	goto out_dput;
+
+root_ok:
+printk("check_pseudo_root: found %s/%s, enabling pseudo-root\n",
+init->d_parent->d_name.name, init->d_name.name);
+	dput(init);
+	return root;
 
-				/* FIXME !!!!!! */
-			/* iput(init); */
-			/* iput(rc); */
-		}
-		if (!pseudo_ok
-		/* && msdos_lookup (pseudo, "sbin", 4, sbin)==0 */
-		    && msdos_lookup (pseudo, sbin) == 0
-		    && S_ISDIR (sbin->d_inode->i_mode)) {
-
-Printk ((KERN_DEBUG "/%s/sbin is there\n", UMSDOS_PSDROOT_NAME));
-			if (msdos_lookup (pseudo, init) == 0
-			    && S_ISREG (init->d_inode->i_mode)) {
-				pseudo_ok = 1;
-			}
-			/* FIXME !!! 
-			 * iput (init); */
-		}
-		if (pseudo_ok) {
-			umsdos_setup_dir_inode (pseudo);
-Printk ((KERN_INFO "Activating pseudo root /%s\n", UMSDOS_PSDROOT_NAME));
-			pseudo_root = pseudo;
-			inc_count (pseudo);
-			pseudo = NULL;
-		}
-		/* FIXME 
-		 * 
-		 * iput (sbin);
-		 * iput (etc);
-		 */
-	}
+	/* Alternate root not found ... */
+out_dput:
+	dput(root);
+out_noroot:
+	return NULL;
 }
-#endif	
 
 
 static struct file_system_type umsdos_fs_type =
diff -u --recursive --new-file v2.1.122/linux/fs/umsdos/ioctl.c linux/fs/umsdos/ioctl.c
--- v2.1.122/linux/fs/umsdos/ioctl.c	Thu Sep 17 17:53:38 1998
+++ linux/fs/umsdos/ioctl.c	Fri Sep 25 16:30:07 1998
@@ -262,7 +262,7 @@
 		 * is in the dos_dirent.name field and the destination
 		 * is in umsdos_dirent.name field.
 		 * 
-		 * This ioctl allows umssync to rename a mangle file
+		 * This ioctl allows umssync to rename a mangled file
 		 * name before syncing it back in the EMD.
 		 */
 		old_dentry = umsdos_lookup_dentry (dentry, 
@@ -282,7 +282,6 @@
 			ret = msdos_rename (dir, old_dentry, dir, new_dentry);
 			dput(new_dentry);
 		}
-		d_drop(old_dentry);
 		dput(old_dentry);
 		goto out;
 	}
@@ -306,6 +305,11 @@
 				data.umsdos_dirent.name_len, &info);
 		ret = umsdos_delentry (dentry, &info,
 				S_ISDIR (data.umsdos_dirent.mode));
+		if (ret) {
+			printk(KERN_WARNING
+				"umsdos_ioctl: delentry %s/%s failed, ret=%d\n",
+				dentry->d_name.name, info.entry.name, ret);
+		}
 		goto out;
 	}
 	else if (cmd == UMSDOS_UNLINK_DOS) {
@@ -324,9 +328,11 @@
 		if (IS_ERR(temp))
 			goto out;
 		ret = -ENOENT;
-		if (temp->d_inode)
-			ret = msdos_unlink (dir, temp);
-		d_drop(temp);
+		if (temp->d_inode) {
+			ret = -EISDIR;
+			if (!S_ISDIR(temp->d_inode->i_mode))
+				ret = msdos_unlink (dir, temp);
+		}
 		dput (temp);
 		goto out;
 	}
@@ -346,9 +352,11 @@
 		if (IS_ERR(temp))
 			goto out;
 		ret = -ENOENT;
-		if (temp->d_inode)
-			ret = msdos_rmdir (dir, temp);
-		d_drop(temp);
+		if (temp->d_inode) {
+			ret = -ENOTDIR;
+			if (S_ISDIR(temp->d_inode->i_mode))
+				ret = msdos_rmdir (dir, temp);
+		}
 		dput (temp);
 		goto out;
 
@@ -385,7 +393,6 @@
 						sizeof (data.stat)))
 				ret = 0;
 		}
-		d_drop(dret);
 		dput(dret);
 		goto out;
 	}
diff -u --recursive --new-file v2.1.122/linux/fs/umsdos/namei.c linux/fs/umsdos/namei.c
--- v2.1.122/linux/fs/umsdos/namei.c	Thu Sep 17 17:53:38 1998
+++ linux/fs/umsdos/namei.c	Fri Sep 25 16:30:07 1998
@@ -177,17 +177,15 @@
 static int is_sticky(struct inode *dir, int uid)
 {
 	return !((dir->i_mode & S_ISVTX) == 0 || 
-		capable (CAP_FOWNER) ||
 		current->fsuid == uid ||
-		current->fsuid == dir->i_uid);
+		current->fsuid == dir->i_uid ||
+		capable (CAP_FOWNER));
 }
 
 
 static int umsdos_nevercreat (struct inode *dir, struct dentry *dentry,
 				int errcod)
 {
-	const char *name = dentry->d_name.name;
-	int len = dentry->d_name.len;
 	int ret = 0;
 
 	if (umsdos_is_pseudodos (dir, dentry)) {
@@ -198,20 +196,6 @@
 		 * 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;
@@ -269,10 +253,9 @@
 	info.entry.gid = (dir->i_mode & S_ISGID) ? dir->i_gid : current->fsgid;
 	info.entry.ctime = info.entry.atime = info.entry.mtime = CURRENT_TIME;
 	info.entry.nlink = 1;
-	umsdos_lockcreate (dir);
 	ret = umsdos_newentry (dentry->d_parent, &info);
 	if (ret)
-		goto out_unlock;
+		goto out;
 
 	/* do a real lookup to get the short name dentry */
 	fake = umsdos_lookup_dentry(dentry->d_parent, info.fake.fname, 
@@ -281,43 +264,49 @@
 	if (IS_ERR(fake))
 		goto out_remove;
 
+ 	/* keep the short name anonymous ... */
+	if (dentry != fake)
+		d_drop(fake);
+
 	/* should not exist yet ... */
 	ret = -EEXIST;
 	if (fake->d_inode)
-		goto out_remove;
+		goto out_remove_dput;
 
 	ret = msdos_create (dir, fake, S_IFREG | 0777);
 	if (ret)
-		goto out_remove;
+		goto out_remove_dput;
 
+	inode = fake->d_inode;
 	/*
 	 * Note! The long and short name might be the same,
 	 * so check first before doing the instantiate ...
 	 */
 	if (dentry != fake) {
-		inode = fake->d_inode;
 		inode->i_count++;
 		d_instantiate (dentry, inode);
 	}
-	umsdos_lookup_patch_new(dentry, &info.entry, info.f_pos);
-	goto out_dput;
+	dput(fake);
+	if (inode->i_count > 1) {
+		printk(KERN_WARNING
+			"umsdos_create_any: %s/%s, ino=%ld, icount=%d??\n",
+			dentry->d_parent->d_name.name, dentry->d_name.name,
+			inode->i_ino, inode->i_count);
+	}
+	umsdos_lookup_patch_new(dentry, &info);
 
+out:
+	return ret;
+
+	/* Creation failed ... remove the EMD entry */
+out_remove_dput:
+	dput(fake);
 out_remove:
 	if (ret == -EEXIST)
 		printk(KERN_WARNING "UMSDOS: out of sync, deleting %s/%s\n",
 			dentry->d_parent->d_name.name, info.fake.fname);
 	umsdos_delentry (dentry->d_parent, &info, S_ISDIR (info.entry.mode));
-
-out_dput:
-	/* N.B. any value in keeping short name dentries? */
-	if (dentry != fake)
-		d_drop(fake);
-	dput(fake);
-
-out_unlock:
-	umsdos_unlockcreate (dir);
-out:
-	return ret;
+	goto out;
 }
 
 /*
@@ -340,7 +329,6 @@
 static void umsdos_ren_init (struct umsdos_info *new_info,
 			     struct umsdos_info *old_info)
 {
-	/* != 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;
@@ -373,7 +361,8 @@
 			    struct inode *new_dir, struct dentry *new_dentry,
 			    int flags)
 {
-	struct dentry *old, *new, *old_emd, *new_target = NULL;
+	struct inode *old_inode = old_dentry->d_inode;
+	struct dentry *old, *new, *old_emd;
 	int err, ret, rehash = 0;
 	struct umsdos_info old_info;
 	struct umsdos_info new_info;
@@ -407,8 +396,12 @@
 
 	/* check sticky bit on old_dir */
 	ret = -EPERM;
-	if (is_sticky(old_dir, old_info.entry.uid))
+	if (is_sticky(old_dir, old_info.entry.uid)) {
+printk("umsdos_rename_f: %s/%s old sticky bit, fsuid=%d, uid=%d, dir=%d\n",
+old_dentry->d_parent->d_name.name, old_info.entry.name,
+current->fsuid, old_info.entry.uid, old_dir->i_uid);
 		goto out_unlock;
+	}
 
 	/*
 	 * Check whether the new_name already exists, and
@@ -470,6 +463,34 @@
 	/* short and long name dentries match? */
 	if (old == old_dentry)
 		dput(old);
+	else {
+		/* make sure it's the same inode! */
+		ret = -ENOENT;
+		if (old->d_inode != old_inode)
+			goto out_dput;
+		/*
+		 * A cross-directory move with different short and long
+		 * names has nasty complications: msdos-fs will need to
+		 * change inodes, so we must check whether the original
+		 * dentry is busy, and if the rename succeeds the short
+		 * dentry will come back with a different inode.
+		 *
+		 * To handle this, we drop the dentry and free the inode,
+		 * and then pick up the new inode after the rename.
+		 */
+		if (old_dir != new_dir) {
+			ret = -EBUSY;
+			if (old_dentry->d_count > 1) {
+printk("umsdos_rename_f: old dentry %s/%s busy, d_count=%d\n",
+old_dentry->d_parent->d_name.name, old_dentry->d_name.name,old_dentry->d_count);
+				goto out_dput;
+			}
+			d_drop(old_dentry);
+			d_delete(old_dentry);
+printk("umsdos_rename_f: cross-dir move, %s/%s dropped\n",
+old_dentry->d_parent->d_name.name, old_dentry->d_name.name);
+		}
+	}
 
 	new = umsdos_lookup_dentry(new_dentry->d_parent, new_info.fake.fname, 
 					new_info.fake.len, 1);
@@ -481,27 +502,21 @@
 			ret);
 		goto out_dput;
 	}
-	/*
-	 * Note! If the new short- and long-name dentries are
-	 * aliases, the target name will be destroyed by the
-	 * msdos-level rename. If in addition the old dentries
-	 * _aren't_ aliased, we'll need the original new name
-	 * for the final d_move, and so must make a copy here.
-	 *
-	 * Welcome to the mysteries of the dcache ...
-	 */
-	if (new == new_dentry) {
+#ifdef UMSDOS_PARANOIA
+if (new->d_inode != new_dentry->d_inode)
+printk("umsdos_rename_f: new %s/%s, inode %p!=%p??\n",
+new->d_parent->d_name.name, new->d_name.name, new->d_inode,new_dentry->d_inode);
+#endif
+	/* short and long name dentries match? */
+	if (new == new_dentry)
 		dput(new);
-		if (old != old_dentry) {
-			/* make a copy of the target dentry */
-			ret = -ENOMEM;
-			new_target = d_alloc(new_dentry->d_parent,
-						&new_dentry->d_name);
-			if (!new_target)
-				goto out_dput;
-		}
-	}
 
+#ifdef UMSDOS_DEBUG_VERBOSE
+printk("umsdos_rename_f: msdos_rename %s/%s(%ld) to %s/%s(%ld)\n",
+old->d_parent->d_name.name, old->d_name.name, old->d_inode->i_ino,
+new->d_parent->d_name.name, new->d_name.name, 
+new->d_inode ? new->d_inode->i_ino : 0);
+#endif
 	/* Do the msdos-level rename */
 	ret = msdos_rename (old_dir, old, new_dir, new);
 Printk(("umsdos_rename_f: now %s/%s, ret=%d\n", 
@@ -534,23 +549,31 @@
 			err);
 	}
 
-	/* dput() the dentry if we haven't already */
-out_dput:
-	if (old_dentry != old)
-		dput(old);
-	if (ret)
-		goto out_unlock;
 	/*
 	 * Check whether to update the dcache ... if both 
 	 * old and new dentries match, it's already correct.
-	 */
-	if (new_dentry != new) {
+	 * If the targets were aliases, the old short-name
+	 * dentry has the original target name.
+ 	 */
+	if (old_dentry != old) {
+		if (!old_dentry->d_inode) {
+			struct inode *inode = old->d_inode;
+			inode->i_count++;
+			d_instantiate(old_dentry, inode);
+printk("umsdos_rename_f: %s/%s gets new ino=%ld\n",
+old_dentry->d_parent->d_name.name, old_dentry->d_name.name, inode->i_ino);
+		}
+		if (new_dentry == new)
+			new_dentry = old;
+		goto move_it;
+	} else if (new_dentry != new) {
+	move_it:
+		/* this will rehash the dentry ... */
 		d_move(old_dentry, new_dentry);
-	} else if (old_dentry != old) {
-		/* new dentry was destroyed ... */
-		d_drop(new_dentry);
-		d_add(new_target, NULL);
-		d_move(old_dentry, new_target);
+	}
+	/* Check whether the old inode changed ... */
+	if (old_dentry->d_inode != old_inode) {
+		umsdos_lookup_patch_new(old_dentry, &new_info);
 	}
 
 	/*
@@ -559,8 +582,12 @@
 	 */
 	umsdos_set_dirinfo_new(old_dentry, new_info.f_pos);
 
+	/* dput() the dentry if we haven't already */
+out_dput:
+	if (old_dentry != old)
+		dput(old);
+
 out_unlock:
-	dput(new_target);
 	dput(old_emd);
 	umsdos_unlockcreate (old_dir);
 	umsdos_unlockcreate (new_dir);
@@ -585,6 +612,8 @@
  * Let's go for simplicity...
  */
 
+extern struct inode_operations umsdos_symlink_inode_operations;
+
 static int umsdos_symlink_x (struct inode *dir, struct dentry *dentry,
 			const char *symname, int mode, char flags)
 {
@@ -596,29 +625,28 @@
 
 	ret = umsdos_create_any (dir, dentry, mode, 0, flags);
 	if (ret) {
-printk("umsdos_symlink: create failed, ret=%d\n", ret);
+		printk(KERN_WARNING
+			"umsdos_symlink: create failed, ret=%d\n", ret);
 		goto out;
 	}
 
 	fill_new_filp (&filp, dentry);
 	len = strlen (symname);
 	ret = umsdos_file_write_kmem_real (&filp, symname, len);
-	if (ret >= 0) {
-		if (ret != len) {
-			ret = -EIO;
-			printk (KERN_WARNING
-				"UMSDOS: Can't write symbolic link data\n");
-		} else {
-			ret = 0;
-		}
-	}
-	if (ret != 0) {
-printk("umsdos_symlink: write failed, unlinking\n");
-		UMSDOS_unlink (dir, dentry);
-	}
-
+	if (ret < 0)
+		goto out_unlink;
+	if (ret != len)
+		goto out_error;
+	ret = 0;
 out:
 	return ret;
+
+out_error:
+	ret = -EIO;
+out_unlink:
+	printk(KERN_WARNING "umsdos_symlink: write failed, unlinking\n");
+	UMSDOS_unlink (dir, dentry);
+	goto out;
 }
 
 /*
@@ -665,7 +693,15 @@
 	if (!buffer)
 		goto out;
 
-	umsdos_lockcreate2 (dir, olddir);
+	/*
+	 * Lock the link parent if it's not the same directory.
+	 */
+	ret = -EDEADLOCK;
+	if (olddir != dir) {
+		if (atomic_read(&olddir->i_sem.count) < 1)
+			goto out_free;
+		down(&olddir->i_sem);
+	}
 
 	/*
 	 * Parse the name and get the visible directory entry.
@@ -802,10 +838,7 @@
 	ret = umsdos_symlink_x (dir, dentry, path, S_IFREG | 0777,UMSDOS_HLINK);
 
 out_unlock:
-	umsdos_unlockcreate (olddir);
-	umsdos_unlockcreate (dir);
-	free_page(buffer);
-out:
+	/* remain locked for the call to notify_change ... */
 	if (ret == 0) {
 		struct iattr newattrs;
 
@@ -819,8 +852,14 @@
 olddentry->d_parent->d_name.name, olddentry->d_name.name,
 oldinode->i_ino, oldinode->i_nlink));
 		newattrs.ia_valid = 0;
-		ret = UMSDOS_notify_change (olddentry, &newattrs);
+		ret = umsdos_notify_change_locked(olddentry, &newattrs);
 	}
+	if (olddir != dir)
+		up(&olddir->i_sem);
+
+out_free:
+	free_page(buffer);
+out:
 	Printk (("umsdos_link %d\n", ret));
 	return ret;
 }
@@ -854,7 +893,6 @@
 	if (ret)
 		goto out;
 
-	umsdos_lockcreate (dir);
 	info.entry.mode = mode | S_IFDIR;
 	info.entry.rdev = 0;
 	info.entry.uid = current->fsuid;
@@ -864,26 +902,36 @@
 	info.entry.nlink = 1;
 	ret = umsdos_newentry (dentry->d_parent, &info);
 	if (ret)
-		goto out_unlock;
+		goto out;
 
 	/* lookup the short name dentry */
 	temp = umsdos_lookup_dentry(dentry->d_parent, info.fake.fname, 
 					info.fake.len, 1);
 	ret = PTR_ERR(temp);
 	if (IS_ERR(temp))
-		goto out_unlock;
+		goto out_remove;
+
+	/* Keep the short name dentry anonymous */ 
+	if (temp != dentry)
+		d_drop(temp);
 
 	/* Make sure the short name doesn't exist */
 	ret = -EEXIST;
 	if (temp->d_inode) {
 printk("umsdos_mkdir: short name %s/%s exists\n",
 dentry->d_parent->d_name.name, info.fake.fname);
-		goto out_remove;
+		goto out_remove_dput;
 	}
 
 	ret = msdos_mkdir (dir, temp, mode);
 	if (ret)
-		goto out_remove;
+		goto out_remove_dput;
+
+	/*
+	 * Lock the inode to protect the EMD creation ...
+	 */
+	inode = temp->d_inode;
+	down(&inode->i_sem);
 
 	/*
 	 * Note! The long and short name might be the same,
@@ -892,11 +940,11 @@
 	if (dentry != temp) {
 if (dentry->d_inode)
 printk("umsdos_mkdir: dentry not negative!\n");
-		inode = temp->d_inode;
 		inode->i_count++;
 		d_instantiate(dentry, inode);
 	}
-	umsdos_lookup_patch_new(dentry, &info.entry, info.f_pos);
+	/* N.B. this should have an option to create the EMD ... */
+	umsdos_lookup_patch_new(dentry, &info);
 
 	/* 
 	 * Create the EMD file, and set up the dir so it is
@@ -907,22 +955,20 @@
 	err = umsdos_make_emd(dentry);
 	umsdos_setup_dir(dentry);
 
-out_dput:
-	/* kill off the short name dentry */ 
-	if (temp != dentry)
-		d_drop(temp);
+	up(&inode->i_sem);
 	dput(temp);
 
-out_unlock:
-	umsdos_unlockcreate (dir);
 out:
-	Printk (("umsdos_mkdir %d\n", ret));
+	Printk(("umsdos_mkdir: %s/%s, ret=%d\n",
+		dentry->d_parent->d_name.name, dentry->d_name.name, ret));
 	return ret;
 
 	/* an error occurred ... remove EMD entry. */
+out_remove_dput:
+	dput(temp);
 out_remove:
 	umsdos_delentry (dentry->d_parent, &info, 1);
-	goto out_dput;
+	goto out;
 }
 
 /*
@@ -961,53 +1007,52 @@
 	if (ret)
 		goto out;
 
-	umsdos_lockcreate (dir);
 	ret = -EBUSY;
-	if (dentry->d_count > 1) {
-		shrink_dcache_parent(dentry);
-		if (dentry->d_count > 1) {
-printk("umsdos_rmdir: %s/%s busy\n",
-dentry->d_parent->d_name.name, dentry->d_name.name);
-			goto out_unlock;
-		}
-	}
+	shrink_dcache_parent(dentry);
+	if (dentry->d_count > 1)
+		goto out;
 
 	/* check the sticky bit */
 	ret = -EPERM;
 	if (is_sticky(dir, dentry->d_inode->i_uid)) {
 printk("umsdos_rmdir: %s/%s is sticky\n",
 dentry->d_parent->d_name.name, dentry->d_name.name);
-		goto out_unlock;
+		goto out;
 	}
 
+	/*
+	 * Lock the directory, then check whether it's empty.
+	 */
+	down(&dentry->d_inode->i_sem);
+
 	/* check whether the EMD is empty */
-	empty = umsdos_isempty (dentry);
 	ret = -ENOTEMPTY;
-	if (empty == 0)
-		goto out_unlock;
+	empty = umsdos_isempty (dentry);
 
 	/* Have to remove the EMD file? */
 	if (empty == 1) {
 		struct dentry *demd;
 
 Printk (("UMSDOS_rmdir: unlinking empty EMD err=%d", err));
-
-		ret = -ENOTEMPTY;
-		/* see if there's an EMD file ... */
 		demd = umsdos_get_emd_dentry(dentry);
-		if (IS_ERR(demd))
-			goto out_unlock;
-
-		err = msdos_unlink (dentry->d_inode, demd);
+		if (!IS_ERR(demd)) {
+			err = -ENOENT;
+			if (demd->d_inode)
+				err = msdos_unlink (dentry->d_inode, demd);
 #ifdef UMSDOS_PARANOIA
 if (err)
 printk("umsdos_rmdir: EMD %s/%s unlink failed, err=%d\n",
 demd->d_parent->d_name.name, demd->d_name.name, err);
 #endif
-		dput(demd);
-		if (err)
-			goto out_unlock;
-	}
+			dput(demd);
+			if (!err)
+				ret = 0;
+		}
+	} else if (empty == 2)
+		ret = 0;
+	up(&dentry->d_inode->i_sem);
+	if (ret)
+		goto out;
 
 	umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info);
 	/* Call findentry to complete the mangling */
@@ -1016,14 +1061,23 @@
 					info.fake.len, 1);
 	ret = PTR_ERR(temp);
 	if (IS_ERR(temp))
-		goto out_unlock;
+		goto out;
 	/*
-	 * If the short name matches the dentry, dput() it now.
+	 * If the short name is an alias, dput() it now;
+	 * otherwise d_drop() it to keep it anonymous.
 	 */
-	if (temp == dentry) {
+	if (temp == dentry)
 		dput(temp);
-Printk(("umsdos_rmdir: %s/%s, short matches long\n",
-dentry->d_parent->d_name.name, dentry->d_name.name));
+	else
+		d_drop(temp);
+
+	/* Check again for a busy dentry */
+	ret = -EBUSY;
+	shrink_dcache_parent(dentry);
+	if (dentry->d_count > 1) {
+printk("umsdos_rmdir: %s/%s busy\n",
+dentry->d_parent->d_name.name, dentry->d_name.name);
+		goto out_dput;
 	}
 
 	/*
@@ -1043,15 +1097,11 @@
 	/* dput() temp if we didn't do it above */
 out_dput:
 	if (temp != dentry) {
-		d_drop(temp);
 		dput(temp);
 		if (!ret)
 			d_delete (dentry);
 	}
 
-out_unlock:
-	umsdos_unlockcreate (dir);
-
 out:
 	Printk (("umsdos_rmdir %d\n", ret));
 	return ret;
@@ -1196,6 +1246,10 @@
 
 	/*
 	 * If this was the last linked reference, delete it now.
+	 *
+	 * N.B. Deadlock problem? We should be holding the lock
+	 * for the hardlink's parent, but another process might
+	 * be holding that lock waiting for us to finish ...
 	 */
 	if (inode->i_nlink <= 1) {
 		ret = UMSDOS_unlink (link->d_parent->d_inode, link);
@@ -1208,7 +1262,7 @@
 		struct iattr newattrs;
 		inode->i_nlink--;
 		newattrs.ia_valid = 0;
-		ret = UMSDOS_notify_change (link, &newattrs);
+		ret = umsdos_notify_change_locked(link, &newattrs);
 	}
 
 out_cleanup:
@@ -1227,38 +1281,49 @@
 int UMSDOS_rename (struct inode *old_dir, struct dentry *old_dentry,
 		   struct inode *new_dir, struct dentry *new_dentry)
 {
+	struct dentry *new_target;
 	int ret;
 
+#ifdef UMSDOS_DEBUG_VERBOSE
+printk("umsdos_rename: enter, %s/%s(%ld) to %s/%s(%ld)\n",
+old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
+old_dentry->d_inode->i_ino,
+new_dentry->d_parent->d_name.name, new_dentry->d_name.name, 
+new_dentry->d_inode ? new_dentry->d_inode->i_ino : 0);
+#endif
 	ret = umsdos_nevercreat (new_dir, new_dentry, -EEXIST);
 	if (ret)
 		goto out;
 
-	ret = umsdos_rename_f(old_dir, old_dentry, new_dir, new_dentry, 0);
-	if (ret != -EEXIST)
-		goto out;
-
 	/*
-	 * Something seems to be giving negative lookups when
-	 * the file really exists ... track this down!
+	 * If the target already exists, delete it first.
 	 */
-	ret = -EIO;
-	if (!new_dentry->d_inode) {
-printk("UMSDOS_rename: %s/%s negative, error EEXIST??\n",
-new_dentry->d_parent->d_name.name, new_dentry->d_name.name);
-		d_drop(new_dentry);
-		goto out;
+	if (new_dentry->d_inode) {
+		if (S_ISDIR(new_dentry->d_inode->i_mode))
+			ret = UMSDOS_rmdir (new_dir, new_dentry);
+		else
+			ret = UMSDOS_unlink (new_dir, new_dentry);
+		if (ret)
+			goto out;
 	}
 
-	/* This is not terribly efficient but should work. */
-	if (S_ISDIR(new_dentry->d_inode->i_mode))
-		ret = UMSDOS_rmdir (new_dir, new_dentry);
-	else
-		ret = UMSDOS_unlink (new_dir, new_dentry);
-	if (ret)
-		goto out;
-
-	/* this time the rename should work ... */
-	ret = umsdos_rename_f(old_dir, old_dentry, new_dir, new_dentry, 0);
+	/*
+	 * If we didn't get a negative dentry, make a copy and hash it.
+	 */
+	new_target = new_dentry;
+	if (new_dentry->d_inode) {
+printk("umsdos_rename: %s/%s not negative, hash=%d\n",
+new_dentry->d_parent->d_name.name, new_dentry->d_name.name,
+!list_empty(&new_dentry->d_hash));
+		ret = -ENOMEM;
+		new_target = d_alloc(new_dentry->d_parent, &new_dentry->d_name);
+		if (!new_target)
+			goto out;
+		d_add(new_target, NULL);
+	} 
+	ret = umsdos_rename_f(old_dir, old_dentry, new_dir, new_target, 0);
+	if (new_target != new_dentry)
+		dput(new_target);
 
 out:
 	return ret;
diff -u --recursive --new-file v2.1.122/linux/fs/umsdos/rdir.c linux/fs/umsdos/rdir.c
--- v2.1.122/linux/fs/umsdos/rdir.c	Thu Sep 17 17:53:38 1998
+++ linux/fs/umsdos/rdir.c	Fri Sep 25 16:30:07 1998
@@ -19,6 +19,7 @@
 #include <asm/uaccess.h>
 
 
+extern struct dentry *saved_root;
 extern struct inode *pseudo_root;
 extern struct dentry_operations umsdos_dentry_operations;
 
@@ -64,9 +65,7 @@
 
 	bufk.filldir = filldir;
 	bufk.dirbuf = dirbuf;
-	bufk.real_root = pseudo_root &&
-			 dir->i_ino == UMSDOS_ROOT_INO && 
-			 dir->i_sb == pseudo_root->i_sb;
+	bufk.real_root = pseudo_root && (dir == saved_root->d_inode);
 	return fat_readdir (filp, &bufk, rdir_filldir);
 }
 
@@ -82,30 +81,41 @@
  */
 int umsdos_rlookup_x ( struct inode *dir, struct dentry *dentry, int nopseudo)
 {
-	/* so locating "linux" will work */
-	const char *name = dentry->d_name.name;
-	int len = dentry->d_name.len;
-	struct inode *inode;
 	int ret;
 
+	/* N.B. this won't work ... lookups of `..' are done by VFS */
+#ifdef BROKEN_TO_BITS
 	if (pseudo_root && len == 2 && name[0] == '.' && name[1] == '.' &&
-	    dir->i_ino == UMSDOS_ROOT_INO && dir->i_sb == pseudo_root->i_sb) {
+	    dir == saved_root->d_inode) {
 printk (KERN_WARNING "umsdos_rlookup_x: we are at pseudo-root thingy?\n");
 		pseudo_root->i_count++;
 		d_add(dentry, pseudo_root);
 		ret = 0;
 		goto out;
 	}
+#endif
 
 	ret = msdos_lookup (dir, dentry);
 	if (ret) {
-		printk(KERN_WARNING "umsdos_rlookup_x: lookup failed, ret=%d\n",
-			ret);
+		printk(KERN_WARNING
+			"umsdos_rlookup_x: %s/%s failed, ret=%d\n",
+			dentry->d_parent->d_name.name, dentry->d_name.name,ret);
 		goto out;
 	}
-	inode = dentry->d_inode;
-	if (inode) {
-		if (inode == pseudo_root && !nopseudo) {
+	if (dentry->d_inode) {
+		/* We must install the proper function table
+		 * depending on whether this is an MS-DOS or 
+		 * a UMSDOS directory
+		 */
+Printk ((KERN_DEBUG "umsdos_rlookup_x: setting up setup_dir_inode %lu...\n",
+inode->i_ino));
+		umsdos_patch_dentry_inode(dentry, 0);
+
+		/* N.B. Won't work -- /linux dentry will already have
+		 * an inode, so we'll never get called here.
+		 */
+#ifdef BROKEN_TO_BITS
+		if (dentry->d_inode == pseudo_root && !nopseudo) {
 			/* #Specification: pseudo root / DOS/linux
 			 * Even in the real root directory (c:\), the directory
 			 * /linux won't show
@@ -114,20 +124,11 @@
 			/* make the dentry negative */
 			d_delete(dentry);
 		}
-		else if (S_ISDIR (inode->i_mode)) {
-			/* We must place the proper function table
-			 * depending on whether this is an MS-DOS or 
-			 * a UMSDOS directory
-			 */
-Printk ((KERN_DEBUG "umsdos_rlookup_x: setting up setup_dir_inode %lu...\n",
-inode->i_ino));
-			umsdos_setup_dir(dentry);
-		}
+#endif
 	}
 out:
 	/* always install our dentry ops ... */
 	dentry->d_op = &umsdos_dentry_operations;
-	PRINTK ((KERN_DEBUG "umsdos_rlookup_x: returning %d\n", ret));
 	return ret;
 }
 
@@ -168,18 +169,18 @@
 	if (umsdos_is_pseudodos (dir, dentry))
 		goto out;
 
-	umsdos_lockcreate (dir);
 	ret = -EBUSY;
 	if (dentry->d_count > 1) {
 		shrink_dcache_parent(dentry);
 		if (dentry->d_count > 1)
-			goto out_unlock;
+			goto out;
 	}
 
 	ret = msdos_rmdir (dir, dentry);
 	if (ret != -ENOTEMPTY)
-		goto out_unlock;
+		goto out;
 
+	down(&dentry->d_inode->i_sem);
 	empty = umsdos_isempty (dentry);
 	if (empty == 1) {
 		struct dentry *demd;
@@ -192,14 +193,14 @@
 				ret = msdos_unlink (dentry->d_inode, demd);
 			dput(demd);
 		}
-		if (ret)
-			goto out_unlock;
 	}
+	up(&dentry->d_inode->i_sem);
+	if (ret)
+		goto out;
+
 	/* now retry the original ... */
 	ret = msdos_rmdir (dir, dentry);
 
-out_unlock:
-	umsdos_unlockcreate (dir);
 out:
 	return ret;
 }
diff -u --recursive --new-file v2.1.122/linux/fs/umsdos/symlink.c linux/fs/umsdos/symlink.c
--- v2.1.122/linux/fs/umsdos/symlink.c	Thu Sep 17 17:53:38 1998
+++ linux/fs/umsdos/symlink.c	Sat Sep 19 13:46:28 1998
@@ -64,7 +64,8 @@
 
 /* this one mostly stolen from romfs :) */
 static struct dentry *UMSDOS_followlink (struct dentry *dentry, 
-					struct dentry *base)
+					struct dentry *base,
+					unsigned int follow)
 {
 	struct inode *inode = dentry->d_inode;
 	char *symname;
@@ -91,7 +92,7 @@
 	}
 
 	symname[len] = 0;
-	dentry = lookup_dentry (symname, base, 1);
+	dentry = lookup_dentry (symname, base, follow);
 	kfree (symname);
 
 	if (0) {
diff -u --recursive --new-file v2.1.122/linux/include/linux/console.h linux/include/linux/console.h
--- v2.1.122/linux/include/linux/console.h	Sun Jul 26 11:57:19 1998
+++ linux/include/linux/console.h	Thu Sep 17 09:35:03 1998
@@ -53,15 +53,11 @@
 extern struct consw prom_con;	/* SPARC PROM console */
 
 void take_over_console(struct consw *sw, int first, int last, int deflt);
-
-/* flag bits */
-#define CON_INITED  (1)
+void give_up_console(struct consw *sw);
 
 /* scroll */
 #define SM_UP       (1)
 #define SM_DOWN     (2)
-#define SM_LEFT     (3)
-#define SM_RIGHT    (4)
 
 /* cursor */
 #define CM_DRAW     (1)
diff -u --recursive --new-file v2.1.122/linux/include/linux/console_struct.h linux/include/linux/console_struct.h
--- v2.1.122/linux/include/linux/console_struct.h	Thu Aug  6 14:06:34 1998
+++ linux/include/linux/console_struct.h	Thu Sep 17 09:35:04 1998
@@ -19,7 +19,6 @@
 	struct consw	*vc_sw;
 	unsigned short	*vc_screenbuf;		/* In-memory character/attribute buffer */
 	unsigned int	vc_screenbuf_size;
-	unsigned short	vc_video_erase_char;	/* Background erase character */
 	unsigned char	vc_attr;		/* Current attributes */
 	unsigned char	vc_def_color;		/* Default colors */
 	unsigned char	vc_color;		/* Foreground & background */
@@ -28,6 +27,8 @@
 	unsigned char	vc_halfcolor;		/* Color for half intensity mode */
 	unsigned short	vc_complement_mask;	/* [#] Xor mask for mouse pointer */
 	unsigned short	vc_hi_font_mask;	/* [#] Attribute set for upper 256 chars of font or 0 if not supported */
+	unsigned short	vc_video_erase_char;	/* Background erase character */
+	unsigned short	vc_s_complement_mask;	/* Saved mouse pointer mask */
 	unsigned int	vc_x, vc_y;		/* Cursor position */
 	unsigned int	vc_top, vc_bottom;	/* Scrolling region */
 	unsigned int	vc_state;		/* Escape sequence parser state */
@@ -104,3 +105,5 @@
 #define CUR_SWMASK	0xfff0
 
 #define CUR_DEFAULT CUR_UNDERLINE
+
+#define CON_IS_VISIBLE(conp) (*conp->vc_display_fg == conp)
diff -u --recursive --new-file v2.1.122/linux/include/linux/dcache.h linux/include/linux/dcache.h
--- v2.1.122/linux/include/linux/dcache.h	Tue Aug 18 22:02:07 1998
+++ linux/include/linux/dcache.h	Sat Sep 19 14:27:29 1998
@@ -27,7 +27,7 @@
 #define init_name_hash()		0
 
 /* partial hash update function. Assume roughly 4 bits per character */
-static __inline__ unsigned long partial_name_hash(unsigned char c, unsigned long prevhash)
+static __inline__ unsigned long partial_name_hash(unsigned long c, unsigned long prevhash)
 {
 	prevhash = (prevhash << 4) | (prevhash >> (8*sizeof(unsigned long)-4));
 	return prevhash ^ c;
diff -u --recursive --new-file v2.1.122/linux/include/linux/ext2_fs.h linux/include/linux/ext2_fs.h
--- v2.1.122/linux/include/linux/ext2_fs.h	Fri Jul 31 17:05:52 1998
+++ linux/include/linux/ext2_fs.h	Fri Sep 25 23:02:43 1998
@@ -520,6 +520,7 @@
 extern int ext2_permission (struct inode *, int);
 
 /* balloc.c */
+extern int ext2_group_sparse(int group);
 extern int ext2_new_block (const struct inode *, unsigned long,
 			   __u32 *, __u32 *, int *);
 extern void ext2_free_blocks (const struct inode *, unsigned long,
diff -u --recursive --new-file v2.1.122/linux/include/linux/fs.h linux/include/linux/fs.h
--- v2.1.122/linux/include/linux/fs.h	Sat Sep  5 16:46:41 1998
+++ linux/include/linux/fs.h	Sun Sep 27 19:51:33 1998
@@ -348,6 +348,7 @@
 	unsigned long		i_version;
 	unsigned long		i_nrpages;
 	struct semaphore	i_sem;
+	struct semaphore	i_atomic_write;
 	struct inode_operations	*i_op;
 	struct super_block	*i_sb;
 	struct wait_queue	*i_wait;
@@ -622,7 +623,7 @@
 	int (*rename) (struct inode *, struct dentry *,
 			struct inode *, struct dentry *);
 	int (*readlink) (struct dentry *, char *,int);
-	struct dentry * (*follow_link) (struct dentry *, struct dentry *);
+	struct dentry * (*follow_link) (struct dentry *, struct dentry *, unsigned int);
 	int (*readpage) (struct file *, struct page *);
 	int (*writepage) (struct file *, struct page *);
 	int (*bmap) (struct inode *,int);
@@ -783,8 +784,8 @@
 #define PTR_ERR(ptr)	((long)(ptr))
 #define IS_ERR(ptr)	((unsigned long)(ptr) > (unsigned long)(-1000))
 
-extern struct dentry * lookup_dentry(const char *, struct dentry *, int);
-extern struct dentry * __namei(const char *, int);
+extern struct dentry * lookup_dentry(const char *, struct dentry *, unsigned int);
+extern struct dentry * __namei(const char *, unsigned int);
 
 #define namei(pathname)		__namei(pathname, 1)
 #define lnamei(pathname)	__namei(pathname, 0)
diff -u --recursive --new-file v2.1.122/linux/include/linux/nbd.h linux/include/linux/nbd.h
--- v2.1.122/linux/include/linux/nbd.h	Sat Sep  5 16:46:41 1998
+++ linux/include/linux/nbd.h	Thu Sep 17 09:16:28 1998
@@ -35,7 +35,6 @@
 }
 
 #define MAX_NBD 128
-#endif
 
 struct nbd_device {
 	int refcnt;	
@@ -51,6 +50,7 @@
 	struct request *tail;
 	struct semaphore queue_lock;
 };
+#endif
 
 /* This now IS in some kind of include file...	*/
 
diff -u --recursive --new-file v2.1.122/linux/include/linux/quota.h linux/include/linux/quota.h
--- v2.1.122/linux/include/linux/quota.h	Fri May  8 23:14:56 1998
+++ linux/include/linux/quota.h	Fri Sep 25 16:24:56 1998
@@ -165,19 +165,23 @@
 #define DQ_FAKE       0x40	/* no limits only usage */
 
 struct dquot {
+	struct dquot *dq_next;		/* Pointer to next dquot */
+	struct dquot **dq_pprev;
+	struct list_head dq_free;	/* free list element */
+	struct dquot *dq_hash_next;	/* Pointer to next in dquot_hash */
+	struct dquot **dq_hash_pprev;	/* Pointer to previous in dquot_hash */
+	struct wait_queue *dq_wait;	/* Pointer to waitqueue */
+	int dq_count;			/* Reference count */
+
+	/* fields after this point are cleared when invalidating */
+	struct vfsmount *dq_mnt;	/* VFS_mount_point this applies to */
 	unsigned int dq_id;		/* ID this applies to (uid, gid) */
-	short dq_type;			/* Type of quota */
 	kdev_t dq_dev;			/* Device this applies to */
+	short dq_type;			/* Type of quota */
 	short dq_flags;			/* See DQ_* */
-	short dq_count;			/* Reference count */
-	unsigned long dq_referenced;	/* Number of times this dquot was referenced during its lifetime */
-	struct vfsmount *dq_mnt;	/* VFS_mount_point this applies to */
+	unsigned long dq_referenced;	/* Number of times this dquot was 
+					   referenced during its lifetime */
 	struct dqblk dq_dqb;		/* Diskquota usage */
-	struct wait_queue *dq_wait;	/* Pointer to waitqueue */
-	struct dquot *dq_next;		/* Pointer to next dquot */
-	struct dquot *dq_hash_next;	/* Pointer to next in dquot_hash */
-	struct dquot **dq_hash_pprev;	/* Pointer to previous in dquot_hash */
-	struct dquot **dq_pprev;
 };
 
 #define NODQUOT (struct dquot *)NULL
diff -u --recursive --new-file v2.1.122/linux/include/linux/selection.h linux/include/linux/selection.h
--- v2.1.122/linux/include/linux/selection.h	Fri Jul 31 17:07:03 1998
+++ linux/include/linux/selection.h	Sun Sep 27 19:48:32 1998
@@ -21,7 +21,6 @@
 #define video_num_columns	(vc_cons[currcons].d->vc_cols)
 #define video_num_lines		(vc_cons[currcons].d->vc_rows)
 #define video_size_row		(vc_cons[currcons].d->vc_size_row)
-#define video_screen_size	(vc_cons[currcons].d->vc_screenbuf_size)
 #define can_do_color		(vc_cons[currcons].d->vc_can_do_color)
 
 extern int console_blanked;
@@ -31,7 +30,6 @@
 extern int default_grn[];
 extern int default_blu[];
 
-extern void do_unblank_screen(void);
 extern unsigned short *screen_pos(int currcons, int w_offset, int viewed);
 extern u16 screen_glyph(int currcons, int offset);
 extern void complement_pos(int currcons, int offset);
diff -u --recursive --new-file v2.1.122/linux/include/linux/socket.h linux/include/linux/socket.h
--- v2.1.122/linux/include/linux/socket.h	Sat Sep  5 16:46:41 1998
+++ linux/include/linux/socket.h	Mon Sep 21 15:38:39 1998
@@ -262,6 +262,7 @@
 
 extern int verify_iovec(struct msghdr *m, struct iovec *iov, char *address, int mode);
 extern int memcpy_toiovec(struct iovec *v, unsigned char *kdata, int len);
+extern void memcpy_tokerneliovec(struct iovec *iov, unsigned char *kdata, int len);
 extern int move_addr_to_user(void *kaddr, int klen, void *uaddr, int *ulen);
 extern int move_addr_to_kernel(void *uaddr, int ulen, void *kaddr);
 extern int put_cmsg(struct msghdr*, int level, int type, int len, void *data);
diff -u --recursive --new-file v2.1.122/linux/include/linux/sunrpc/auth.h linux/include/linux/sunrpc/auth.h
--- v2.1.122/linux/include/linux/sunrpc/auth.h	Fri Jul 31 17:09:54 1998
+++ linux/include/linux/sunrpc/auth.h	Sat Sep 26 10:37:31 1998
@@ -14,6 +14,8 @@
 #include <linux/config.h>
 #include <linux/sunrpc/sched.h>
 
+/* size of the nodename buffer */
+#define UNX_MAXNODENAME	32
 
 /*
  * Client user credentials
diff -u --recursive --new-file v2.1.122/linux/include/linux/sunrpc/clnt.h linux/include/linux/sunrpc/clnt.h
--- v2.1.122/linux/include/linux/sunrpc/clnt.h	Thu Aug 20 17:05:19 1998
+++ linux/include/linux/sunrpc/clnt.h	Sun Sep 27 19:49:34 1998
@@ -52,6 +52,9 @@
 
 	struct rpc_portmap	cl_pmap;	/* port mapping */
 	struct rpc_wait_queue	cl_bindwait;	/* waiting on getport() */
+
+	int			cl_nodelen;	/* nodename length */
+	char 			cl_nodename[UNX_MAXNODENAME];
 };
 #define cl_timeout		cl_xprt->timeout
 #define cl_prog			cl_pmap.pm_prog
diff -u --recursive --new-file v2.1.122/linux/include/linux/swap.h linux/include/linux/swap.h
--- v2.1.122/linux/include/linux/swap.h	Sat Sep  5 16:46:41 1998
+++ linux/include/linux/swap.h	Sun Sep 27 19:08:09 1998
@@ -89,6 +89,7 @@
 extern int swap_check_entry(unsigned long);
 extern struct page * read_swap_cache_async(unsigned long, int);
 #define read_swap_cache(entry) read_swap_cache_async(entry, 1);
+extern int FASTCALL(swap_count(unsigned long));
 /*
  * Make these inline later once they are working properly.
  */
@@ -146,14 +147,20 @@
  */
 static inline int is_page_shared(struct page *page)
 {
-	int count = atomic_read(&page->count);
+	unsigned int count;
 	if (PageReserved(page))
 		return 1;
-	if (page->inode == &swapper_inode)
-		count--;
+	count = atomic_read(&page->count);
+	if (PageSwapCache(page))
+	{
+		/* PARANOID */
+		if (page->inode != &swapper_inode)
+			panic("swap cache page has wrong inode\n");
+		count += swap_count(page->offset) - 2;
+	}
 	if (PageFreeAfter(page))
 		count--;
-	return (count > 1);
+	return  count > 1;
 }
 
 #endif /* __KERNEL__*/
diff -u --recursive --new-file v2.1.122/linux/include/linux/tcp.h linux/include/linux/tcp.h
--- v2.1.122/linux/include/linux/tcp.h	Fri Jul 31 17:07:27 1998
+++ linux/include/linux/tcp.h	Sun Sep 27 19:48:32 1998
@@ -71,7 +71,7 @@
 };
 
 #define TCP_STATE_MASK	0xF
-#define TCP_ACTION_FIN	1 << 7
+#define TCP_ACTION_FIN	(1 << 7)
 
 enum {
   TCPF_ESTABLISHED = (1 << 1),
diff -u --recursive --new-file v2.1.122/linux/include/linux/tty.h linux/include/linux/tty.h
--- v2.1.122/linux/include/linux/tty.h	Thu Aug  6 14:06:34 1998
+++ linux/include/linux/tty.h	Sun Sep 27 19:51:33 1998
@@ -9,7 +9,7 @@
  * These constants are also useful for user-level apps (e.g., VC
  * resizing).
  */
-#define MIN_NR_CONSOLES	1	/* must be at least 1 */
+#define MIN_NR_CONSOLES 1       /* must be at least 1 */
 #define MAX_NR_CONSOLES	63	/* serial lines start at 64 */
 #define MAX_NR_USER_CONSOLES 63	/* must be root to allocate above this */
 		/* Note: the ioctl VT_GETSTATE does not work for
@@ -87,7 +87,10 @@
 	unsigned char  blue_pos;		/* 0x2b */
 	unsigned char  rsvd_size;		/* 0x2c */
 	unsigned char  rsvd_pos;		/* 0x2d */
-						/* 0x2e -- 0x3f reserved for future expansion */
+	unsigned short vesapm_seg;		/* 0x2e */
+	unsigned short vesapm_off;		/* 0x30 */
+	unsigned short pages;			/* 0x32 */
+						/* 0x34 -- 0x3f reserved for future expansion */
 };
 
 extern struct screen_info screen_info;
@@ -389,10 +392,6 @@
 /* pcxx.c */
 
 extern int pcxe_open(struct tty_struct *tty, struct file *filp);
-
-/* console.c */
-
-extern void update_screen(int new_console);
 
 /* printk.c */
 
diff -u --recursive --new-file v2.1.122/linux/include/linux/umsdos_fs.p linux/include/linux/umsdos_fs.p
--- v2.1.122/linux/include/linux/umsdos_fs.p	Thu Sep 17 17:53:38 1998
+++ linux/include/linux/umsdos_fs.p	Fri Sep 25 16:30:07 1998
@@ -2,31 +2,14 @@
 void check_page_tables (void);
 
 /* dir.c 22/06/95 00.22.12 */
-int compat_msdos_create(struct inode *dir,
-			const char *name,
-			int len,
-			int mode,
-			struct inode **inode);
 int  dummy_dir_read ( struct file *filp,
 	 char *buf,
 	 size_t size,
 	 loff_t *count);
 char * umsdos_d_path(struct dentry *, char *, int);
-void umsdos_lookup_patch_new(struct dentry *, struct umsdos_dirent *, off_t);
-void umsdos_lookup_patch (struct inode *dir,
-	 struct inode *inode,
-	 struct umsdos_dirent *entry,
-	 off_t emd_pos);
-int umsdos_dentry_to_entry (struct dentry *, struct umsdos_dirent *);
-int umsdos_inode2entry (struct inode *dir,
-	 struct inode *inode,
-	 struct umsdos_dirent *entry);
-int umsdos_locate_path (struct inode *inode, char *path);
+void umsdos_lookup_patch_new(struct dentry *, struct umsdos_info *);
 int umsdos_is_pseudodos (struct inode *dir, struct dentry *dentry);
-int umsdos_lookup_x (
-			    struct inode *dir,
-			    struct dentry *dentry,
-			    int nopseudo);
+int umsdos_lookup_x ( struct inode *dir, struct dentry *dentry, int nopseudo);
 int UMSDOS_lookup(struct inode *, struct dentry *);
 struct dentry *umsdos_lookup_dentry(struct dentry *, char *, int, int);
 
@@ -52,7 +35,6 @@
 struct dentry *umsdos_get_emd_dentry(struct dentry *);
 int umsdos_have_emd(struct dentry *);
 int umsdos_make_emd(struct dentry *);
-struct inode *umsdos_emd_dir_lookup (struct inode *dir, int creat);
 int umsdos_emd_dir_readentry (struct file *, struct umsdos_dirent *);
 int umsdos_newentry (struct dentry *, struct umsdos_info *);
 int umsdos_newhidden (struct dentry *, struct umsdos_info *);
@@ -63,32 +45,20 @@
 /* file.c 25/01/95 02.25.38 */
 
 /* inode.c 12/06/95 09.49.40 */
-inline struct dentry *geti_dentry (struct inode *inode);
-void checkd_inode (struct inode *inode);
-void check_inode (struct inode *inode);
-void check_dentry (struct dentry *dentry);
-void check_dentry_path (struct dentry *dentry, const char *desc);
 void fill_new_filp (struct file *filp, struct dentry *dentry);
-struct dentry *creat_dentry (const char *name,
-			     const int len,
-			     struct inode *inode,
-			     struct dentry *parent);
 void UMSDOS_read_inode (struct inode *);
 void UMSDOS_write_inode (struct inode *);
 int UMSDOS_notify_change (struct dentry *, struct iattr *attr);
+int umsdos_notify_change_locked(struct dentry *, struct iattr *attr);
 void UMSDOS_put_inode (struct inode *);
 int UMSDOS_statfs (struct super_block *, struct statfs *, int);
 struct super_block *UMSDOS_read_super (struct super_block *, void *, int);
 void UMSDOS_put_super (struct super_block *);
 
-int umsdos_real_lookup(struct inode *, struct dentry *);	 
 void umsdos_setup_dir(struct dentry *);
-void umsdos_setup_dir_inode (struct inode *inode);
 void umsdos_set_dirinfo_new(struct dentry *, off_t);
-void umsdos_set_dirinfo (struct inode *, struct inode *, off_t);
 int umsdos_isinit (struct inode *inode);
 void umsdos_patch_dentry_inode (struct dentry *, off_t);
-void umsdos_patch_inode (struct inode *, struct inode *, off_t);
 int umsdos_get_dirowner (struct inode *inode, struct inode **result);
 
 /* ioctl.c 22/06/95 00.22.08 */
@@ -96,6 +66,7 @@
 	 struct file *filp,
 	 unsigned int cmd,
 	 unsigned long data);
+
 /* mangle.c 25/01/95 02.25.38 */
 void umsdos_manglename (struct umsdos_info *info);
 int umsdos_evalrecsize (int len);
@@ -135,9 +106,13 @@
 		   struct dentry *new_dentry);
 
 /* rdir.c 22/03/95 03.31.42 */
-int umsdos_rlookup_x (struct inode *dir,
-	 struct dentry *dentry,
-	 int nopseudo);
-int UMSDOS_rlookup (struct inode *dir,
-		    struct dentry *dentry);
+int umsdos_rlookup_x (struct inode *dir, struct dentry *dentry, int nopseudo);
+int UMSDOS_rlookup (struct inode *dir, struct dentry *dentry);
+
 /* symlink.c 23/01/95 03.38.30 */
+
+/* check.c */
+void checkd_inode (struct inode *inode);
+void check_inode (struct inode *inode);
+void check_dentry (struct dentry *dentry);
+void check_dentry_path (struct dentry *dentry, const char *desc);
diff -u --recursive --new-file v2.1.122/linux/include/linux/umsdos_fs_i.h linux/include/linux/umsdos_fs_i.h
--- v2.1.122/linux/include/linux/umsdos_fs_i.h	Fri Jul 31 17:05:52 1998
+++ linux/include/linux/umsdos_fs_i.h	Fri Sep 25 16:30:07 1998
@@ -62,12 +62,14 @@
 		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 */
+	} u;
+	int i_patched;			/* Inode has been patched */
+	int i_is_hlink;			/* Resolved hardlink inode? */
+	unsigned long i_emd_owner;	/* Is this the EMD file inode? */
 	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 */
+	/* The rest is used only if this inode describes a directory */
+	struct dentry *i_emd_dentry;	/* EMD dentry for this directory */
+	unsigned long i_emd_dir;	/* Inode of the EMD file */
 };
 
 #endif
diff -u --recursive --new-file v2.1.122/linux/include/linux/vt_buffer.h linux/include/linux/vt_buffer.h
--- v2.1.122/linux/include/linux/vt_buffer.h	Fri Jul 31 17:07:03 1998
+++ linux/include/linux/vt_buffer.h	Sun Sep 27 19:48:32 1998
@@ -16,9 +16,6 @@
 #include <linux/config.h>
 
 #ifdef CONFIG_VGA_CONSOLE
-#if !defined(CONFIG_FB) && !defined(CONFIG_FB_MODULE)
-#define VT_BUF_VRAM_ONLY
-#endif
 #include <asm/vga.h>
 #endif
 
diff -u --recursive --new-file v2.1.122/linux/include/linux/vt_kern.h linux/include/linux/vt_kern.h
--- v2.1.122/linux/include/linux/vt_kern.h	Fri Jul 31 17:07:03 1998
+++ linux/include/linux/vt_kern.h	Sun Sep 27 19:48:32 1998
@@ -35,27 +35,29 @@
 /* console.c */
 
 struct console_font_op;
+struct consw;
 
-int vc_allocate(unsigned int console, int init);
+int vc_allocate(unsigned int console);
 int vc_cons_allocated(unsigned int console);
 int vc_resize(unsigned int lines, unsigned int cols,
 	      unsigned int first, unsigned int last);
 #define vc_resize_all(l, c) vc_resize(l, c, 0, MAX_NR_CONSOLES-1)
 #define vc_resize_con(l, c, x) vc_resize(l, c, x, x)
 void vc_disallocate(unsigned int console);
-void poke_blanked_console(void);
-void set_vesa_blanking(unsigned long arg);
-void vesa_blank(void);
-void vesa_powerdown(void);
 void reset_palette(int currcons);
-void set_palette(void);
-void do_blank_screen(int nopowersave);
+void set_palette(int currcons);
+void do_blank_screen(int gfx_mode);
+void unblank_screen(void);
+void poke_blanked_console(void);
 int con_font_op(int currcons, struct console_font_op *op);
 int con_set_cmap(unsigned char *cmap);
 int con_get_cmap(unsigned char *cmap);
 void scrollback(int);
 void scrollfront(int);
 void update_region(int currcons, unsigned long start, int count);
+void redraw_screen(int new_console, int is_switch);
+#define update_screen(x) redraw_screen(x, 0)
+#define switch_screen(x) redraw_screen(x, 1)
 
 struct tty_struct;
 int tioclinux(struct tty_struct *tty, unsigned long arg);
@@ -75,6 +77,7 @@
 int con_set_default_unimap(int currcons);
 void con_free_unimap(int currcons);
 void con_protect_unimap(int currcons, int rdonly);
+int con_copy_unimap(int dstcons, int srccons);
 
 /* vt.c */
 
diff -u --recursive --new-file v2.1.122/linux/init/main.c linux/init/main.c
--- v2.1.122/linux/init/main.c	Thu Sep 17 17:53:39 1998
+++ linux/init/main.c	Mon Sep 21 13:35:32 1998
@@ -32,6 +32,7 @@
 #include <linux/major.h>
 #include <linux/blk.h>
 #include <linux/init.h>
+#include <linux/smp_lock.h>
 
 #include <asm/system.h>
 #include <asm/io.h>
@@ -77,7 +78,7 @@
 extern void sock_init(void);
 extern void uidcache_init(void);
 extern void mca_init(void);
-extern long sbus_init(long, long);
+extern long sbus_init(void);
 extern long powermac_init(unsigned long, unsigned long);
 extern void sysctl_init(void);
 extern void filescache_init(void);
@@ -1115,15 +1116,47 @@
 	check_bugs();
 	printk("POSIX conformance testing by UNIFIX\n");
 
+	/* 
+	 *	We count on the initial thread going ok 
+	 *	Like idlers init is an unlocked kernel thread, which will
+	 *	make syscalls (and thus be locked).
+	 */
 	smp_init();
+	kernel_thread(init, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
+ 	cpu_idle(NULL);
+}
 
-	/*
-	 * Ok, the machine is now initialized. None of the devices
-	 * have been touched yet, but the CPU subsystem is up and
-	 * running, and memory management works.
-	 *
-	 * Now we can finally start doing some real work..
-	 */
+#ifdef CONFIG_BLK_DEV_INITRD
+static int do_linuxrc(void * shell)
+{
+	static char *argv[] = { "linuxrc", NULL, };
+
+	close(0);close(1);close(2);
+	setsid();
+	(void) open("/dev/console",O_RDWR,0);
+	(void) dup(0);
+	(void) dup(0);
+	return execve(shell, argv, envp_init);
+}
+
+static void __init no_initrd(char *s,int *ints)
+{
+	mount_initrd = 0;
+}
+#endif
+
+/*
+ * Ok, the machine is now initialized. None of the devices
+ * have been touched yet, but the CPU subsystem is up and
+ * running, and memory and process management works.
+ *
+ * Now we can finally start doing some real work..
+ */
+static void __init do_basic_setup(void)
+{
+#ifdef CONFIG_BLK_DEV_INITRD
+	int real_root_mountflags;
+#endif
 
 #if defined(CONFIG_MTRR)	/* Do this after SMP initialization */
 /*
@@ -1161,49 +1194,6 @@
 	ecard_init();
 #endif
 
-	/* 
-	 *	We count on the initial thread going ok 
-	 *	Like idlers init is an unlocked kernel thread, which will
-	 *	make syscalls (and thus be locked).
-	 */
-	kernel_thread(init, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
-/*
- * task[0] is meant to be used as an "idle" task: it may not sleep, but
- * it might do some general things like count free pages or it could be
- * used to implement a reasonable LRU algorithm for the paging routines:
- * anything that can be useful, but shouldn't take time from the real
- * processes.
- *
- * Right now task[0] just does an infinite idle loop.
- */
- 	cpu_idle(NULL);
-}
-
-#ifdef CONFIG_BLK_DEV_INITRD
-static int do_linuxrc(void * shell)
-{
-	static char *argv[] = { "linuxrc", NULL, };
-
-	close(0);close(1);close(2);
-	setsid();
-	(void) open("/dev/console",O_RDWR,0);
-	(void) dup(0);
-	(void) dup(0);
-	return execve(shell, argv, envp_init);
-}
-
-static void __init no_initrd(char *s,int *ints)
-{
-	mount_initrd = 0;
-}
-#endif
-
-static void __init do_basic_setup(void)
-{
-#ifdef CONFIG_BLK_DEV_INITRD
-	int real_root_mountflags;
-#endif
-
 	/* Networking initialization needs a process context */ 
 	sock_init();
 
@@ -1222,6 +1212,7 @@
 #endif
 
 #ifdef CONFIG_BLK_DEV_INITRD
+
 	real_root_dev = ROOT_DEV;
 	real_root_mountflags = root_mountflags;
 	if (initrd_start && mount_initrd) root_mountflags &= ~MS_RDONLY;
@@ -1278,6 +1269,7 @@
 
 static int init(void * unused)
 {
+	lock_kernel();
 	do_basic_setup();
 
 	/*
@@ -1286,6 +1278,7 @@
 	 * initmem segments and start the user-mode stuff..
 	 */
 	free_initmem();
+	unlock_kernel();
 
 	if (open("/dev/console", O_RDWR, 0) < 0)
 		printk("Warning: unable to open an initial console.\n");
diff -u --recursive --new-file v2.1.122/linux/kernel/exit.c linux/kernel/exit.c
--- v2.1.122/linux/kernel/exit.c	Thu Sep 17 17:53:39 1998
+++ linux/kernel/exit.c	Sat Sep 19 10:43:51 1998
@@ -415,15 +415,6 @@
 	struct wait_queue wait = { current, NULL };
 	struct task_struct *p;
 
-	if (stat_addr) {
-		if(verify_area(VERIFY_WRITE, stat_addr, sizeof(*stat_addr)))
-			return -EFAULT;
-	}
-	if (ru) {
-		if(verify_area(VERIFY_WRITE, ru, sizeof(*ru)))
-			return -EFAULT;
-	}
-
 	if (options & ~(WNOHANG|WUNTRACED|__WCLONE))
 		return -EINVAL;
 
@@ -453,21 +444,23 @@
 				if (!(options & WUNTRACED) && !(p->flags & PF_PTRACED))
 					continue;
 				read_unlock(&tasklist_lock);
-				if (ru != NULL)
-					getrusage(p, RUSAGE_BOTH, ru);
-				if (stat_addr)
-					__put_user((p->exit_code << 8) | 0x7f, stat_addr);
-				p->exit_code = 0;
-				retval = p->pid;
+				retval = ru ? getrusage(p, RUSAGE_BOTH, ru) : 0; 
+				if (!retval && stat_addr) 
+					retval = put_user((p->exit_code << 8) | 0x7f, stat_addr);
+				if (!retval) {
+					p->exit_code = 0;
+					retval = p->pid;
+				}
 				goto end_wait4;
 			case TASK_ZOMBIE:
 				current->times.tms_cutime += p->times.tms_utime + p->times.tms_cutime;
 				current->times.tms_cstime += p->times.tms_stime + p->times.tms_cstime;
 				read_unlock(&tasklist_lock);
-				if (ru != NULL)
-					getrusage(p, RUSAGE_BOTH, ru);
-				if (stat_addr)
-					__put_user(p->exit_code, stat_addr);
+				retval = ru ? getrusage(p, RUSAGE_BOTH, ru) : 0;
+				if (!retval && stat_addr)
+					retval = put_user(p->exit_code, stat_addr);
+				if (retval)
+					goto end_wait4; 
 				retval = p->pid;
 				if (p->p_opptr != p->p_pptr) {
 					write_lock_irq(&tasklist_lock);
diff -u --recursive --new-file v2.1.122/linux/mm/page_alloc.c linux/mm/page_alloc.c
--- v2.1.122/linux/mm/page_alloc.c	Wed Sep  9 14:51:13 1998
+++ linux/mm/page_alloc.c	Sun Sep 27 19:08:09 1998
@@ -163,9 +163,11 @@
 		free_pages_ok(page->map_nr, 0);
 		return;
 	}
+#if 0
 	if (PageSwapCache(page) && atomic_read(&page->count) == 1)
 		printk(KERN_WARNING "VM: Releasing swap cache page at %p",
 			__builtin_return_address(0));
+#endif
 }
 
 void free_pages(unsigned long addr, unsigned long order)
@@ -182,10 +184,12 @@
 			free_pages_ok(map_nr, order);
 			return;
 		}
+#if 0
 		if (PageSwapCache(map) && atomic_read(&map->count) == 1)
 			printk(KERN_WARNING 
 				"VM: Releasing swap cache pages at %p",
 				__builtin_return_address(0));
+#endif
 	}
 }
 
diff -u --recursive --new-file v2.1.122/linux/mm/swap_state.c linux/mm/swap_state.c
--- v2.1.122/linux/mm/swap_state.c	Wed Sep  9 14:51:13 1998
+++ linux/mm/swap_state.c	Sun Sep 27 19:38:57 1998
@@ -143,6 +143,50 @@
 	goto out;
 }
 
+int swap_count(unsigned long entry)
+{
+	struct swap_info_struct * p;
+	unsigned long offset, type;
+	int retval = 0;
+
+	if (!entry)
+		goto bad_entry;
+	type = SWP_TYPE(entry);
+	if (type & SHM_SWP_TYPE)
+		goto out;
+	if (type >= nr_swapfiles)
+		goto bad_file;
+	p = type + swap_info;
+	offset = SWP_OFFSET(entry);
+	if (offset >= p->max)
+		goto bad_offset;
+	if (!p->swap_map[offset])
+		goto bad_unused;
+	retval = p->swap_map[offset];
+#ifdef DEBUG_SWAP
+	printk("DebugVM: swap_count(entry %08lx, count %d)\n",
+	       entry, retval);
+#endif
+out:
+	return retval;
+
+bad_entry:
+	printk(KERN_ERR "swap_count: null entry!\n");
+	goto out;
+bad_file:
+	printk(KERN_ERR
+	       "swap_count: entry %08lx, nonexistent swap file!\n", entry);
+	goto out;
+bad_offset:
+	printk(KERN_ERR
+	       "swap_count: entry %08lx, offset exceeds max!\n", entry);
+	goto out;
+bad_unused:
+	printk(KERN_ERR
+	       "swap_count at %8p: entry %08lx, unused page!\n", 
+	       __builtin_return_address(0), entry);
+	goto out;
+}
 
 static inline void remove_from_swap_cache(struct page *page)
 {
@@ -155,6 +199,7 @@
 		printk ("VM: Removing swap cache page with wrong inode hash "
 			"on page %08lx\n", page_address(page));
 	}
+#if 0
 	/*
 	 * This is a legal case, but warn about it.
 	 */
@@ -163,6 +208,7 @@
 			"VM: Removing page cache on unshared page %08lx\n", 
 			page_address(page));
 	}
+#endif
 
 #ifdef DEBUG_SWAP
 	printk("DebugVM: remove_from_swap_cache(%08lx count %d)\n",
diff -u --recursive --new-file v2.1.122/linux/net/core/iovec.c linux/net/core/iovec.c
--- v2.1.122/linux/net/core/iovec.c	Sat Sep  5 16:46:42 1998
+++ linux/net/core/iovec.c	Mon Sep 21 14:19:00 1998
@@ -95,6 +95,30 @@
 }
 
 /*
+ *	In kernel copy to iovec. Returns -EFAULT on error.
+ *
+ *	Note: this modifies the original iovec.
+ */
+ 
+void memcpy_tokerneliovec(struct iovec *iov, unsigned char *kdata, int len)
+{
+	while(len>0)
+	{
+		if(iov->iov_len)
+		{
+			int copy = min(iov->iov_len, len);
+			memcpy(iov->iov_base, kdata, copy);
+			kdata+=copy;
+			len-=copy;
+			iov->iov_len-=copy;
+			iov->iov_base+=copy;
+		}
+		iov++;
+	}
+}
+
+
+/*
  *	Copy iovec to kernel. Returns -EFAULT on error.
  *
  *	Note: this modifies the original iovec.
diff -u --recursive --new-file v2.1.122/linux/net/ipv4/tcp.c linux/net/ipv4/tcp.c
--- v2.1.122/linux/net/ipv4/tcp.c	Thu Sep 17 17:53:39 1998
+++ linux/net/ipv4/tcp.c	Fri Sep 25 10:57:06 1998
@@ -598,9 +598,9 @@
 		     sk->urginline || !tp->urg_data))
 			mask |= POLLIN | POLLRDNORM;
 
-		/* Always wake the user up when an error occurred */
-		if (sock_wspace(sk) >= tcp_min_write_space(sk, tp) || sk->err)
+		if (sock_wspace(sk) >= tcp_min_write_space(sk, tp))
 			mask |= POLLOUT | POLLWRNORM;
+
 		if (tp->urg_data & URG_VALID)
 			mask |= POLLPRI;
 	}
@@ -1458,7 +1458,8 @@
 	 *  reader process may not have drained the data yet!
 	 */
 	while((skb=__skb_dequeue(&sk->receive_queue))!=NULL) {
-		data_was_unread++;
+		u32 len = TCP_SKB_CB(skb)->end_seq - TCP_SKB_CB(skb)->seq - skb->h.th->fin;
+		data_was_unread += len;
 		kfree_skb(skb);
 	}
 
diff -u --recursive --new-file v2.1.122/linux/net/netsyms.c linux/net/netsyms.c
--- v2.1.122/linux/net/netsyms.c	Thu Sep 17 17:53:40 1998
+++ linux/net/netsyms.c	Mon Sep 21 15:38:39 1998
@@ -93,6 +93,7 @@
 
 /* Socket layer support routines */
 EXPORT_SYMBOL(memcpy_fromiovec);
+EXPORT_SYMBOL(memcpy_tokerneliovec);
 EXPORT_SYMBOL(sock_create);
 EXPORT_SYMBOL(sock_alloc);
 EXPORT_SYMBOL(sock_release);
diff -u --recursive --new-file v2.1.122/linux/net/sunrpc/auth_unix.c linux/net/sunrpc/auth_unix.c
--- v2.1.122/linux/net/sunrpc/auth_unix.c	Tue Aug 18 22:02:08 1998
+++ linux/net/sunrpc/auth_unix.c	Sat Sep 26 10:37:31 1998
@@ -10,7 +10,6 @@
 #include <linux/malloc.h>
 #include <linux/socket.h>
 #include <linux/in.h>
-#include <linux/utsname.h>
 #include <linux/sunrpc/clnt.h>
 #include <linux/sunrpc/auth.h>
 
@@ -28,13 +27,7 @@
 
 #define UNX_CRED_EXPIRE		(60 * HZ)
 
-#ifndef DONT_FILLIN_HOSTNAME
-/* # define UNX_MAXNODENAME	(sizeof(system_utsname.nodename)-1) */
-# define UNX_MAXNODENAME	32
-# define UNX_WRITESLACK		(21 + (UNX_MAXNODENAME >> 2))
-#else
-# define UNX_WRITESLACK		20
-#endif
+#define UNX_WRITESLACK		(21 + (UNX_MAXNODENAME >> 2))
 
 #ifdef RPC_DEBUG
 # define RPCDBG_FACILITY	RPCDBG_AUTH
@@ -170,6 +163,7 @@
 static u32 *
 unx_marshal(struct rpc_task *task, u32 *p, int ruid)
 {
+	struct rpc_clnt	*clnt = task->tk_client;
 	struct unx_cred	*cred = (struct unx_cred *) task->tk_cred;
 	u32		*base, *hold;
 	int		i, n;
@@ -177,20 +171,15 @@
 	*p++ = htonl(RPC_AUTH_UNIX);
 	base = p++;
 	*p++ = htonl(jiffies/HZ);
-#ifndef DONT_FILLIN_HOSTNAME
+
 	/*
-	 *	Problem: The UTS name could change under us. We can't lock
-	 *	here to handle this. On the other hand we can't really
-	 *	go building a bad RPC!
+	 * Copy the UTS nodename captured when the client was created.
 	 */
-	if ((n = strlen((char *) system_utsname.nodename)) > UNX_MAXNODENAME)
-		n = UNX_MAXNODENAME;
+	n = clnt->cl_nodelen;
 	*p++ = htonl(n);
-	memcpy(p, system_utsname.nodename, n);
+	memcpy(p, clnt->cl_nodename, n);
 	p += (n + 3) >> 2;
-#else
-	*p++ = 0;
-#endif
+
 	if (ruid) {
 		*p++ = htonl((u32) cred->uc_uid);
 		*p++ = htonl((u32) cred->uc_gid);
diff -u --recursive --new-file v2.1.122/linux/net/sunrpc/clnt.c linux/net/sunrpc/clnt.c
--- v2.1.122/linux/net/sunrpc/clnt.c	Wed Sep  9 14:51:13 1998
+++ linux/net/sunrpc/clnt.c	Sat Sep 26 10:37:31 1998
@@ -28,6 +28,7 @@
 #include <linux/mm.h>
 #include <linux/malloc.h>
 #include <linux/in.h>
+#include <linux/utsname.h>
 
 #include <linux/sunrpc/clnt.h>
 
@@ -101,6 +102,12 @@
 
 	if (!rpcauth_create(flavor, clnt))
 		goto out_no_auth;
+
+	/* save the nodename */
+	clnt->cl_nodelen = strlen(system_utsname.nodename);
+	if (clnt->cl_nodelen > UNX_MAXNODENAME)
+		clnt->cl_nodelen = UNX_MAXNODENAME;
+	memcpy(clnt->cl_nodename, system_utsname.nodename, clnt->cl_nodelen);
 out:
 	return clnt;
 
diff -u --recursive --new-file v2.1.122/linux/net/sunrpc/xprt.c linux/net/sunrpc/xprt.c
--- v2.1.122/linux/net/sunrpc/xprt.c	Wed Aug 26 11:37:45 1998
+++ linux/net/sunrpc/xprt.c	Mon Sep 21 15:49:13 1998
@@ -573,7 +573,6 @@
 	struct rpc_rqst *rovr;
 	struct sk_buff	*skb;
 	struct iovec	iov[MAX_IOVEC];
-	mm_segment_t	oldfs;
 	int		err, repsize, copied;
 
 	dprintk("RPC:      udp_data_ready...\n");
@@ -603,9 +602,8 @@
 
 	/* Okay, we have it. Copy datagram... */
 	memcpy(iov, rovr->rq_rvec, rovr->rq_rnr * sizeof(iov[0]));
-	oldfs = get_fs(); set_fs(get_ds());
-	skb_copy_datagram_iovec(skb, 8, iov, copied);
-	set_fs(oldfs);
+	/* This needs to stay tied with the usermode skb_copy_dagram... */
+	memcpy_tokerneliovec(iov, skb->data+8, copied);
 
 	xprt_complete_rqst(xprt, rovr, copied);
 
