diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/Makefile linux-2.0.29/Makefile
--- linux.vanilla/Makefile      Sat Feb  8 18:54:36 1997
+++ linux-2.0.29/Makefile       Tue Apr 15 19:15:50 1997
@@ -29,7 +29,7 @@
HOSTCC         =gcc -I$(HPATH)
HOSTCFLAGS     =-O2 -fomit-frame-pointer

-CROSS_COMPILE  =
+CROSS_COMPILE  = m68k-linux-

AS     =$(CROSS_COMPILE)as
LD     =$(CROSS_COMPILE)ld
@@ -140,6 +140,10 @@

ifdef CONFIG_PCI
DRIVERS := $(DRIVERS) drivers/pci/pci.a
+endif
+
+ifdef CONFIG_MAC
+DRIVERS := $(DRIVERS) drivers/nubus/nubus.a
endif

ifdef CONFIG_SBUS
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/arch/m68k/Makefile linux-2.0.29/arch/m68k/Makefile
--- linux.vanilla/arch/m68k/Makefile    Sat Oct 19 12:33:13 1996
+++ linux-2.0.29/arch/m68k/Makefile     Fri Mar 21 12:12:58 1997
@@ -28,7 +28,7 @@
LD += -m m68klinux
ifneq ($(COMPILE_ARCH),$(ARCH))
       # prefix for cross-compiling binaries
-       CROSS_COMPILE = m68k-linuxaout-
+       CROSS_COMPILE = m68k-linux-
endif
endif

@@ -45,7 +45,11 @@
ifdef CONFIG_KERNEL_ELF
LINKFLAGS = -Ttext 0x1000
else
+ifdef CONFIG_MAC
+LINKFLAGS = -omagic -e __start
+else
LINKFLAGS = -qmagic -Ttext 0xFE0
+endif
endif

CFLAGS := $(CFLAGS) -pipe
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/arch/m68k/config.in linux-2.0.29/arch/m68k/config.in
--- linux.vanilla/arch/m68k/config.in   Thu Feb 27 16:45:46 1997
+++ linux-2.0.29/arch/m68k/config.in    Thu Apr 10 14:01:11 1997
@@ -144,6 +144,9 @@
dep_tristate 'Atari native SCSI support' CONFIG_ATARI_SCSI $CONFIG_SCSI
bool 'Long delays for Toshiba CD-ROMs' CONFIG_ATARI_SCSI_TOSHIBA_DELAY
fi
+if [ "$CONFIG_MAC" = "y" ]; then
+       bool 'MAC NCR5380 SCSI' CONFIG_MAC_SCSI
+fi
#dep_tristate 'SCSI debugging host adapter' CONFIG_SCSI_DEBUG $CONFIG_SCSI
endmenu

@@ -211,6 +214,9 @@
  tristate 'Amiga builtin serial support' CONFIG_AMIGA_BUILTIN_SERIAL
  bool 'GVP IO-Extender support' CONFIG_GVPIOEXT
  tristate 'Multiface Card III serial support' CONFIG_MULTIFACE_III_TTY
+fi
+if [ "$CONFIG_MAC" = "y" ]; then
+   bool 'MAC SCC serial support' CONFIG_MAC_SCC
fi
bool 'Support for user serial device modules' CONFIG_USERIAL
bool 'Watchdog Timer Support'  CONFIG_WATCHDOG
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/arch/m68k/console/fbcon.c linux-2.0.29/arch/m68k/console/fbcon.c
--- linux.vanilla/arch/m68k/console/fbcon.c     Sat Oct  5 14:23:56 1996
+++ linux-2.0.29/arch/m68k/console/fbcon.c      Mon Mar 24 14:29:50 1997
@@ -24,6 +24,7 @@
 *          with work by Guenther Kelleter
 *                       Martin Schaller
 *                       Andreas Schwab
+ *                      Alan Cox
 *
 *
 * This file is subject to the terms and conditions of the GNU General Public
@@ -68,6 +69,8 @@

#include <asm/system.h>

+#include <asm/setup.h> /*AC DEBUG */
+
#include "../../../drivers/char/vt_kern.h"   /* vt_cons and vc_resize_con() */


@@ -89,6 +92,7 @@
#undef CONFIG_FBCON_2PLANE
#undef CONFIG_FBCON_4PLANE
#undef CONFIG_FBCON_8PLANE
+#undef CONFIG_FBCON_4PACKED
#undef CONFIG_FBCON_8PACKED
#undef CONFIG_FBCON_16PACKED
#undef CONFIG_FBCON_24PACKED
@@ -100,6 +104,20 @@

#define CONFIG_FBCON_MONO

+/* Mac support */
+
+#ifdef CONFIG_MAC
+#ifndef CONFIG_FBCON_4PACKED
+#define CONFIG_FBCON_4PACKED
+#endif
+#ifndef CONFIG_FBCON_8PACKED
+#define CONFIG_FBCON_8PACKED
+#endif
+#ifndef CONFIG_FBCON_16PACKED
+#define CONFIG_FBCON_16PACKED
+#endif
+#endif
+
/* Amiga support */

#ifdef CONFIG_AMIGA
@@ -159,8 +177,8 @@
#endif


-struct fb_info *fb_info;
-struct display *disp;
+static struct fb_info *fb_info;
+static struct display *disp;


/* ++Geert: Sorry, no hardware cursor support at the moment;
@@ -223,7 +241,7 @@
    *    Interface used by the world
    */

-static u_long fbcon_startup(u_long kmem_start, char **display_desc);
+/*static */u_long fbcon_startup(u_long kmem_start, char **display_desc);
static void fbcon_init(struct vc_data *conp);
static int fbcon_deinit(struct vc_data *conp);
static int fbcon_changevar(int con);
@@ -378,6 +396,23 @@


   /*
+    *    4bpp packed pixels (eg the MAC toby board)
+    */
+
+#ifdef CONFIG_FBCON_4PACKED
+static void bmove_4_packed(struct display *p, int sy, int sx, int dy, int dx,
+                       int height, int width);
+static void clear_4_packed(struct vc_data *conp, struct display *p, int sy, int sx,
+                       int height, int width);
+static void putc_4_packed(struct vc_data *conp, struct display *p, int c, int y,
+                      int x);
+static void putcs_4_packed(struct vc_data *conp, struct display *p, const char *s,
+                       int count, int y, int x);
+static void rev_char_4_packed(struct display *p, int x, int y);
+#endif /* CONFIG_FBCON_4PACKED */
+
+
+   /*
    *    8 bpp Packed Pixels
    */

@@ -454,70 +489,76 @@


#ifdef CONFIG_FBCON_MONO
-struct display_switch dispsw_mono = {
+static struct display_switch dispsw_mono = {
   bmove_mono, clear_mono, putc_mono, putcs_mono, rev_char_mono
};
#endif /* CONFIG_FBCON_MONO */

#ifdef CONFIG_FBCON_ILBM
-struct display_switch dispsw_ilbm = {
+static struct display_switch dispsw_ilbm = {
   bmove_ilbm, clear_ilbm, putc_ilbm, putcs_ilbm, rev_char_ilbm
};
#endif /* CONFIG_FBCON_ILBM */

#ifdef CONFIG_FBCON_PLANES
-struct display_switch dispsw_plan = {
+static struct display_switch dispsw_plan = {
   bmove_plan, clear_plan, putc_plan, putcs_plan, rev_char_plan
};
#endif /* CONFIG_FBCON_PLANES */

#ifdef CONFIG_FBCON_2PLANE
-struct display_switch dispsw_2_plane = {
+static struct display_switch dispsw_2_plane = {
   bmove_2_plane, clear_2_plane, putc_2_plane, putcs_2_plane, rev_char_2_plane
};
#endif /* CONFIG_FBCON_2PLANE */

#ifdef CONFIG_FBCON_4PLANE
-struct display_switch dispsw_4_plane = {
+static struct display_switch dispsw_4_plane = {
   bmove_4_plane, clear_4_plane, putc_4_plane, putcs_4_plane, rev_char_4_plane
};
#endif /* CONFIG_FBCON_4PLANE */

#ifdef CONFIG_FBCON_8PLANE
-struct display_switch dispsw_8_plane = {
+static struct display_switch dispsw_8_plane = {
   bmove_8_plane, clear_8_plane, putc_8_plane, putcs_8_plane, rev_char_8_plane
};
#endif /* CONFIG_FBCON_8PLANE */

+#ifdef CONFIG_FBCON_4PACKED
+static struct display_switch dispsw_4_packed = {
+   bmove_4_packed, clear_4_packed, putc_4_packed, putcs_4_packed, rev_char_4_packed
+};
+#endif /* CONFIG_FBCON_4PACKED */
+
#ifdef CONFIG_FBCON_8PACKED
-struct display_switch dispsw_8_packed = {
+static struct display_switch dispsw_8_packed = {
   bmove_8_packed, clear_8_packed, putc_8_packed, putcs_8_packed, rev_char_8_packed
};
#endif /* CONFIG_FBCON_8PACKED */

#ifdef CONFIG_FBCON_16PACKED
-struct display_switch dispsw_16_packed = {
+static struct display_switch dispsw_16_packed = {
   bmove_16_packed, clear_16_packed, putc_16_packed, putcs_16_packed,
   rev_char_16_packed
};
#endif /* CONFIG_FBCON_16PACKED */

#ifdef CONFIG_FBCON_CYBER
-struct display_switch dispsw_cyber = {
+static struct display_switch dispsw_cyber = {
   bmove_cyber, clear_cyber, putc_cyber, putcs_cyber, rev_char_cyber
};
#endif /* CONFIG_FBCON_CYBER */


-static u_long fbcon_startup(u_long kmem_start, char **display_desc)
+/* static */u_long fbcon_startup(u_long kmem_start, char **display_desc)
{
   int irqres = 0;
-
+
   fb_info = mach_fb_init(&kmem_start);
   disp = fb_info->disp;
   *display_desc = fb_info->modename;
   fb_info->changevar = &fbcon_changevar;
-
+
#ifdef CONFIG_AMIGA
   if (MACH_IS_AMIGA) {
      cursor_blink_rate = AMIGA_CURSOR_BLINK_RATE;
@@ -664,6 +705,11 @@
         p->dispsw = &dispsw_cyber;
      else
#endif /* CONFIG_FBCON_CYBER */
+#ifdef CONFIG_FBCON_4PACKED
+      if (p->var.bits_per_pixel == 4)
+         p->dispsw = &dispsw_4_packed;
+      else
+#endif /* CONFIG_FBCON_8PACKED */
#ifdef CONFIG_FBCON_8PACKED
      if (p->var.bits_per_pixel == 8)
         p->dispsw = &dispsw_8_packed;
@@ -1475,6 +1521,10 @@
   int unit = conp->vc_num;
   struct display *p = &disp[unit];

+   /* Avoid flickering if there's no real change. */
+   if (p->cursor_x == conp->vc_x && p->cursor_y == conp->vc_y &&
+       (mode == CM_ERASE) == !cursor_on)
+      return 0;
   if (CURSOR_UNDRAWN ())
      p->dispsw->rev_char(p, p->cursor_x, real_y(p, p->cursor_y));
   p->cursor_x = conp->vc_x;
@@ -1861,7 +1911,7 @@
 *      - Color Interleaved Planes � la Amiga
 *      - Color Normal Planes
 *      - Color Interleaved Planes � la Atari (2, 4 and 8 planes)
- *      - Color Packed Pixels (8 and 16 bpp)
+ *      - Color Packed Pixels (4, 8 and 16 bpp)
 *      - Cybervision Color Packed Pixels (accelerated)
 */

@@ -3292,6 +3342,170 @@

/* ====================================================================== */

+#ifdef CONFIG_FBCON_4PACKED
+
+   /*
+    *    4 bpp Packed Pixels:
+    *          IFF the font is even pixel aligned (that is to say each
+    *          character start is a byte start in the pixel pairs). That
+    *          avoids us having to mask bytes and means we won't be here
+    *          all week. On a MacII that matters _lots_
+    */
+
+static u_short nibbletab_4_packed[]={
+0x0000,0x000f,0x00f0,0x00ff,
+0x0f00,0x0f0f,0x0ff0,0x0fff,
+0xf000,0xf00f,0xf0f0,0xf0ff,
+0xff00,0xff0f,0xfff0,0xffff};
+
+static void bmove_4_packed(struct display *p, int sy, int sx, int dy, int dx,
+                           int height, int width)
+{
+       int bytes = p->next_line, linesize = bytes * p->fontheight, rows;
+       u_char *src,*dst;
+
+       if (sx == 0 && dx == 0 && width * 4 == bytes) {
+               mymemmove(p->screen_base + dy * linesize,
+                         p->screen_base + sy * linesize,
+                         height * linesize);
+       }
+       else {
+               if (dy < sy || (dy == sy && dx < sx)) {
+                       src = p->screen_base + sy * linesize + sx * 4;
+                       dst = p->screen_base + dy * linesize + dx * 4;
+                       for (rows = height * p->fontheight ; rows-- ;) {
+                               mymemmove(dst, src, width * 4);
+                               src += bytes;
+                               dst += bytes;
+                       }
+               }
+               else {
+                       src = p->screen_base + (sy+height) * linesize + sx * 4
+                               - bytes;
+                       dst = p->screen_base + (dy+height) * linesize + dx * 4
+                               - bytes;
+                       for (rows = height * p->fontheight ; rows-- ;) {
+                               mymemmove(dst, src, width * 4);
+                               src -= bytes;
+                               dst -= bytes;
+                       }
+               }
+       }
+}
+
+
+static void clear_4_packed(struct vc_data *conp, struct display *p, int sy,
+                           int sx, int height, int width)
+{
+       u_char *dest0,*dest;
+       int bytes=p->next_line,lines=height * p->fontheight, rows, i;
+       u_long bgx;
+
+       if(p->screen_base!=0xFDD00020)
+               mac_boom(1);
+       dest = p->screen_base + sy * p->fontheight * bytes + sx * 4;
+
+       bgx=attr_bgcol_ec(p,conp);
+       bgx |= (bgx << 4);      /* expand the colour to 32bits */
+       bgx |= (bgx << 8);
+       bgx |= (bgx << 16);
+
+       if (sx == 0 && width * 4 == bytes) {
+               for (i = 0 ; i < lines * width ; i++) {
+                       ((u_long *)dest)[0]=bgx;
+                       dest+=4;
+               }
+       } else {
+               dest0=dest;
+               for (rows = lines; rows-- ; dest0 += bytes) {
+                       dest=dest0;
+                       for (i = 0 ; i < width ; i++) {
+                               /* memset ?? */
+                               ((u_long *)dest)[0]=bgx;
+                               dest+=4;
+                       }
+               }
+       }
+}
+
+
+static void putc_4_packed(struct vc_data *conp, struct display *p, int c, int y,
+                          int x)
+{
+       u_char *dest,*cdat;
+       int bytes=p->next_line,rows;
+       ulong eorx,fgx,bgx;
+
+       c &= 0xff;
+
+       dest = p->screen_base + y * p->fontheight * bytes + x * 4;
+       cdat = p->fontdata + c * p->fontheight;
+
+       fgx=15;/*attr_fgcol(p,conp)&0x0F;*/
+       bgx=attr_bgcol(p,conp)&0x0F;
+       fgx |= (fgx << 4);
+       fgx |= (fgx << 8);
+       bgx |= (bgx << 4);
+       bgx |= (bgx << 8);
+       eorx = fgx ^ bgx;
+
+       for (rows = p->fontheight ; rows-- ; dest += bytes) {
+               ((u_short *)dest)[0]=
+                       (nibbletab_4_packed[*cdat >> 4] & eorx) ^ bgx;
+               ((u_short *)dest)[1]=
+                       (nibbletab_4_packed[*cdat++ & 0xf] & eorx) ^ bgx;
+       }
+}
+
+
+static void putcs_4_packed(struct vc_data *conp, struct display *p,
+                           const char *s, int count, int y, int x)
+{
+       u_char *cdat, c, *dest, *dest0;
+       int rows,bytes=p->next_line;
+       u_long eorx, fgx, bgx;
+
+       dest0 = p->screen_base + y * p->fontheight * bytes + x * 4;
+       fgx=15/*attr_fgcol(p,conp)*/;
+       bgx=attr_bgcol(p,conp);
+       fgx |= (fgx << 4);
+       fgx |= (fgx << 8);
+       fgx |= (fgx << 16);
+       bgx |= (bgx << 4);
+       bgx |= (bgx << 8);
+       bgx |= (bgx << 16);
+       eorx = fgx ^ bgx;
+       while (count--) {
+               c = *s++;
+               cdat = p->fontdata + c * p->fontheight;
+
+               for (rows = p->fontheight, dest = dest0; rows-- ; dest += bytes) {
+                       ((u_short *)dest)[0]=
+                       (nibbletab_4_packed[*cdat >> 4] & eorx) ^ bgx;
+                       ((u_short *)dest)[1]=
+                       (nibbletab_4_packed[*cdat++ & 0xf] & eorx) ^ bgx;
+               }
+               dest0+=4;
+       }
+}
+
+
+static void rev_char_4_packed(struct display *p, int x, int y)
+{
+       u_char *dest;
+       int bytes=p->next_line, rows;
+
+       dest = p->screen_base + y * p->fontheight * bytes + x * 4;
+       for (rows = p->fontheight ; rows-- ; dest += bytes) {
+               ((u_long *)dest)[0] ^= 0x0f0f0f0f;
+       }
+}
+
+#endif /* CONFIG_FBCON_4PACKED */
+
+/* ====================================================================== */
+
+
#ifdef CONFIG_FBCON_8PACKED

   /*
@@ -3766,8 +3980,13 @@
    *    The console `switch' structure for the frame buffer based console
    */

+unsigned long precookie=0x0DEC0DED;
+
struct consw fb_con = {
   fbcon_startup, fbcon_init, fbcon_deinit, fbcon_clear, fbcon_putc,
   fbcon_putcs, fbcon_cursor, fbcon_scroll, fbcon_bmove, fbcon_switch,
   fbcon_blank, fbcon_get_font, fbcon_set_font
};
+
+unsigned long postcookie=0xC0DEBA5E;
+
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/arch/m68k/kernel/console.c linux-2.0.29/arch/m68k/kernel/console.c
--- linux.vanilla/arch/m68k/kernel/console.c    Sat Oct  5 14:24:06 1996
+++ linux-2.0.29/arch/m68k/kernel/console.c     Mon Mar 24 11:16:58 1997
@@ -123,6 +123,8 @@
#include <asm/system.h>
#include <asm/bitops.h>

+#include <asm/setup.h>
+
#include "../../../drivers/char/kbd_kern.h"
#include "../../../drivers/char/vt_kern.h"
#include "../../../drivers/char/consolemap.h"
@@ -2215,11 +2217,12 @@
       console_driver.throttle = con_throttle;
       console_driver.unthrottle = con_unthrottle;

+
       if (tty_register_driver(&console_driver))
               panic("Couldn't register console driver\n");

       kmem_start = conswitchp->con_startup (kmem_start, &display_desc);
-
+
       timer_table[BLANK_TIMER].fn = blank_screen;
       timer_table[BLANK_TIMER].expires = 0;
       if (blankinterval) {
@@ -2252,6 +2255,8 @@

       gotoxy(currcons,0,0);
       csi_J(currcons, 0);
+       mac_debugging_penguin(3);
+
       printable = 1;
       update_screen(fg_console);
       sw->con_cursor(vc_cons[currcons].d, CM_DRAW);
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/arch/m68k/kernel/head.S linux-2.0.29/arch/m68k/kernel/head.S
--- linux.vanilla/arch/m68k/kernel/head.S       Sat Oct  5 14:24:07 1996
+++ linux-2.0.29/arch/m68k/kernel/head.S        Tue Apr 15 16:21:56 1997
@@ -143,13 +143,20 @@

#define is_not_amiga(lab) moveq &MACH_AMIGA,%d7; cmpl %d4,%d7; jne lab
#define is_not_atari(lab) moveq &MACH_ATARI,%d7; cmpl %d4,%d7; jne lab
+#define is_not_mac(lab) moveq &MACH_MAC,%d7; cmpl %d4,%d7; jne lab

#define is_040_or_060(lab) btst &D6B_0460,%d6; jne lab
#define is_not_040_or_060(lab) btst &D6B_0460,%d6; jeq lab
#define is_060(lab) btst &D6B_060,%d6; jne lab
#define is_not_060(lab) btst &D6B_060,%d6; jeq lab

+#define CONFIG_MAC
+
.text
+#ifdef CONFIG_MAC
+ENTRY(_true_stext)
+.equ   .,SYMBOL_NAME(_true_stext)+PAGESIZE
+#endif
ENTRY(_stext)
/*
 * Version numbers of the bootinfo interface
@@ -172,6 +179,69 @@

ENTRY(_start)

+
+#ifdef CONFIG_MAC
+#      lea     _fb_con,%a0
+#      movel   #_fbcon_startup,a0@
+
+       /*
+        *      Yes this isnt the clean way to do it. I need to revise
+        *      my 68k asm.
+        */
+       movel   %d5,%a0
+       movel   %a4, %d1
+       andl    #0xFFFF,%d1     /* rows */
+loopy:
+       movel   %a3, %d0
+loopx:
+       moveb   #0x55, %a0@+
+       dbra    %d0,loopx
+       dbra    %d1,loopy
+       /*
+        *      Check we have top of video right
+        */
+       movel   %a3,%d0
+       movel   %d5,%a0
+       addl    %d0,%d0
+       addl    %d0,%d0
+       addl    %d0,%d0
+       addl    %d0,%d0
+loopw:
+       moveb   #00, %a0@+
+       dbra    %d0,loopw
+
+       /*
+        *      Save the boot info
+        */
+       lea     %pc@(SYMBOL_NAME(boot_info)),%a0
+       movel   #MACH_MAC,%a0@(BI_machtype)
+       movel   %sp@-, %d0
+       movel   %d0, %a0@(BI_args)
+       movel   %d4,%d0
+       andl    #3,%d0
+       movel   #33,%d0 /* 68020+FPU hardcode */
+       movel   %d0, %a0@(BI_cputype)
+       movel   %a4, %a0@(BI_dimensions)
+       movel   %d5, %a0@(BI_videoaddr)
+       movel   %a3, %a0@(BI_videorow)
+       movel   %a2, %a0@(BI_videodepth)
+       lea     %pc@(SYMBOL_NAME(_stext):w),%sp
+       movel   #0, %a0@(BI_memory+MI_addr)
+       movel   #5242880, %a0@(BI_memory+MI_size)
+       jbsr Lserial_init
+
+
+       putr()
+       putc('L')
+       putc('i')
+       putc('n')
+       putc('u')
+       putc('x')
+       putc('.')
+       putr()
+
+
+#else
/*
 * Setup initial stack pointer
 */
@@ -187,6 +257,8 @@
1:     moveb   %a0@+,%a1@+
       dbra    %d0,1b

+#endif
+
/*
 * Record the CPU and machine type.
 */
@@ -206,6 +278,7 @@
       jra     2f
1:     /* '020 or '030: d6 := no CPU bit, cache mode unused */
       moveq   #0,%d6
+       movel   %d6, 0xFD008020 /* ac check */

2:     lea     %pc@(SYMBOL_NAME(m68k_pgtable_cachemode)),%a0
       moveq   #0,%d0
@@ -248,8 +321,7 @@
/*
 * Save physical start address of kernel
 */
-       lea     %pc@(SYMBOL_NAME(_stext)-PAGESIZE:w),%a0
-       movel   %a0,%d5
+       movel   #0,%d5
#ifdef HACKER_KERNEL
       lea     %pc@(Lkernel_start),%a0
       movel   %d5,%a0@
@@ -275,6 +347,7 @@

       putc('C')

+
/*
 * Initialize the pointer tables referred to above.  They either point
 * to page tables in the case of the 680[46]0 or contain early
@@ -405,6 +478,7 @@
       putc('I')

       moveq   #_PAGE_NOCACHE030+_PAGE_PRESENT,%d0
+       orl     #0x80000000, %d0
       movel   %d0,%a5@(0x40<<2)

       jra     Lmapphys
@@ -474,6 +548,128 @@
Lnotami:
#endif

+#ifdef CONFIG_MAC
+       is_not_mac(Lnotmac)
+
+/*
+ * Setup a mapping of the 0xFC range for 32M of physical address space
+ * at virtual address 0xFC000000, using early termination page descriptors
+ * for the 68030, and proper page tables for the 680[46]0.  Set this area
+ * as non-cacheable. (040/060 one still wrong XXX)
+ */
+
+       putc('H')
+
+       is_040_or_060(Lspmac68040)
+
+       /*
+        * for the 68030, just setup a translation to map in the
+        * 32M of physical address space at virtual address 0x80000000
+         * using an early termination page descriptor.
+        */
+
+       putc('I')
+
+       /*
+        *      Nubus space
+        */
+
+       moveq   #_PAGE_NOCACHE030+_PAGE_PRESENT,%d0
+       orl     #0xF8000000,%d0
+       movel   %d0,%a5@(0x7C<<2)
+       moveq   #_PAGE_NOCACHE030+_PAGE_PRESENT,%d0
+       orl     #0xFA000000,%d0
+       movel   %d0,%a5@(0x7D<<2)
+       moveq   #_PAGE_NOCACHE030+_PAGE_PRESENT,%d0
+       orl     #0xFC000000,%d0
+       movel   %d0,%a5@(0x7E<<2)
+       moveq   #_PAGE_NOCACHE030+_PAGE_PRESENT,%d0
+       orl     #0xFE000000,%d0
+       movel   %d0,%a5@(0x7F<<2)
+
+       /*
+        *      IO space identity at 0x50-> for now
+        */
+
+       moveq   #_PAGE_NOCACHE030+_PAGE_PRESENT,%d0
+       orl     #0x50000000,%d0
+       movel   %d0,%a5@(0x28<<2)
+
+       /*
+        *      MAC onboard video
+        */
+
+       moveq   #_PAGE_NOCACHE030+_PAGE_PRESENT,%d0
+       orl     #0x60000000,%d0
+       movel   %d0,%a5@(0x30<<2)
+
+       jra     Lmapphys
+
+Lspmac68040:
+
+       /*
+        * for the 680[46]0, use another pointer table, and allocate 4 more
+        * page tables.  Initialize the pointer table to point to the
+        * page tables.  Then initialize the page tables to point to
+        * the first 16M of memory, with no caching (noncachable/serialized).
+        */
+
+       /* clear the mac pointer table */
+       lea     %a4@(PTR_TABLE_SIZE<<2),%a4
+       moveq   #PTR_TABLE_SIZE-1,%d1
+1:     clrl    %a0@+
+       dbra    %d1,1b
+
+       /* allocate 4 pages for 64 page tables */
+       movel   %a6,%a3
+       addw    #4*PAGESIZE,%a6
+
+       /* initialize the pointer table */
+       movel   %a4,%a0
+       movel   %a3,%a1
+       addql   #_PAGE_TABLE,%a1        /* base descriptor */
+       movel   #PAGE_TABLE_SIZE<<2,%d2 /* increment */
+       moveq   #TABLENR_16MB-1,%d1
+
+1:     movel   %a1,%a0@+
+       addl    %d2,%a1
+       dbra    %d1,1b
+
+       /* ensure that the root table points to the pointer table */
+       movel   %a4,%a0
+       addql   #_PAGE_TABLE,%a0
+       movel   %a0,%a5@(0x40<<2)
+
+       /*
+        * initialize the page tables
+        * descriptor bits include noncachable/serialized and global bits.
+        */
+       movel   %a3,%a0
+       movew   #_PAGE_GLOBAL040+_PAGE_NOCACHE_S+_PAGE_PRESENT,%a1
+       movel   #PAGESIZE,%d2
+       movew   #(PAGE_TABLE_SIZE*TABLENR_16MB)-1,%d1
+
+1:     movel   %a1,%a0@+
+       addl    %d2,%a1
+       dbra    %d1,1b
+
+       /*
+        * Finally, since we just allocated 4 page tables, make sure that
+        * the virtual mapping of the 4 page tables indicates
+        * noncachable/serialized.
+        */
+       moveq   #3,%d0
+1:     movel   %a2@,%d1        /* a2 already points to root table offset */
+       andw    #_CACHEMASK040,%d1
+       orw     %d6,%d1
+       movel   %d1,%a2@+
+       dbra    %d0,1b
+
+       jra     Lmapphys
+
+Lnotmac:
+#endif
+
#ifdef CONFIG_ATARI
       is_not_atari(Lnotatari)

@@ -591,6 +787,7 @@
 * an Amiga since the first 16M is already identity mapped on the Amiga.
 */
Lmapphys:
+
       putc('J')

#ifdef CONFIG_AMIGA
@@ -680,6 +877,7 @@
Lmapphysnotamiga:
#endif

+
#ifdef CONFIG_ATARI
       is_not_atari(Lmapphysnotatari)

@@ -773,14 +971,64 @@
Lmapphysnotatari:
#endif

+#ifdef CONFIG_MAC
+       is_not_mac(Lmapphysnotmac)
+
+       putc('L')
+
+       is_040_or_060(Lmacmmu68040)
+
+       lea     %pc@(Lmmu),%a3
+       movel   %d5,%d0
+       /* We always follow this path as the kernel is at 0x0000 after
+          the MacBoot has finished */
+       lea     LdoneMMUenable:w,%a0
+       movel   #0x80000002,%a3@
+       movel   %a5,%a3@(4)
+       .long   0xf0134800      /* pmove %a3@,%srp */
+       .long   0xf0134c00      /* pmove %a3@,%crp */
+       .long   0xf0002400      /* pflusha */
+       /*
+        * enable,super root enable,4096 byte pages,7 bit root index,
+        * 7 bit pointer index, 6 bit page table index.
+        */
+       movel   #0x82c07760,%a3@
+       .long   0xf0134000      /* pmove %a3@,%tc (enable the MMU) */
+       jmp     %a0@
+
+Lmacmmu68040:
+       movel   %d5,%d0
+       lea     LdoneMMUenable:w,%a0
+       jra     2f
+2:     nop
+       .word   0xf518          /* pflusha */
+       .long   0x4e7bd807      /* movec %a5,%srp */
+       .long   0x4e7bd806      /* movec %a5,%urp */
+       movel   #TC_ENABLE+TC_PAGE4K,%d0
+       /*
+        * this value is also ok for the 68060, we don`t use the cache
+        * mode/protection defaults
+        */
+       .long   0x4e7b0003      /* movec %d0,%tc (enable the MMU) */
+       jmp     %a0@
+3:     moveq   #0,%d0
+       .long   0x4e7b0004      /* movec %d0,%itt0 */
+       tstl    %a1
+       jra     LdoneMMUenable
+
+Lmapphysnotmac:
+#endif
+
LdoneMMUenable:

+
/*
 * Fixup the addresses for the kernel pointer table and availmem.
 * Convert them from physical addresses to virtual addresses.
 */

-       putc('M')
+
+/*     putc('M')*/

       /*
        * d5 contains physaddr of kernel start
@@ -794,7 +1042,7 @@
       subl    %d5,%a6
       movel   %a6,SYMBOL_NAME(availmem) /* first available memory address */

-       putc('N')
+/*     putc('N')*/

#if 0
       putr()
@@ -823,10 +1071,24 @@
/*
 * Enable caches
 */
+       lea     %pc@(SYMBOL_NAME(boot_info)),%a0
+       movel   %a0@(BI_videoaddr),%a0
+       movel   #2048,%d0
+1:
+       moveb   #0x33,%a0@+
+       dbra    %d0,1b
       is_040_or_060(Lcache680460)

       movel   #CC3_ENABLE_DB+CC3_CLR_D+CC3_ENABLE_D+CC3_ENABLE_IB+CC3_CLR_I+CC3_ENABLE_I,%d0
       movec   %d0,%cacr
+
+       lea     %pc@(SYMBOL_NAME(boot_info)),%a0
+       movel   %a0@(BI_videoaddr),%a0
+       movel   #2048,%d0
+1:
+       moveb   #0xFF,%a0@+
+       dbra    %d0,1b
+
       jra     1f

Lcache680460:
@@ -854,7 +1116,7 @@
       lea     SYMBOL_NAME(init_user_stack)+PAGESIZE,%sp

/* jump to the kernel start */
-       putr()
+/*     putr()*/

       jbsr    SYMBOL_NAME(start_kernel)

@@ -903,6 +1165,25 @@
 * from the MFP or a serial port of the SCC
 */

+#ifdef CONFIG_MAC
+#define USE_SCC
+
+scc_initable_mac:
+       .byte   9,12            /* Reset */
+       .byte   4,0x44          /* x16, 1 stopbit, no parity */
+       .byte   3,0xc0          /* receiver: 8 bpc */
+       .byte   5,0xe2          /* transmitter: 8 bpc, assert dtr/rts */
+       .byte   9,0             /* no interrupts */
+       .byte   10,0            /* NRZ */
+       .byte   11,0x50         /* use baud rate generator */
+       .byte   12,10,13,0      /* 9600 baud */
+       .byte   14,2,14,3       /* use master clock for BRG, enable */
+       .byte   3,0xc1          /* enable receiver */
+       .byte   5,0xea          /* enable transmitter */
+       .byte   -1
+       .even
+#endif
+
#ifdef CONFIG_ATARI
/* #define USE_PRINTER */
/* #define USE_SCC */
@@ -1002,7 +1283,16 @@
       orb     #1,LMFP_TDCDR
       bset    #1,LMFP_TSR
#endif
+#endif
4:
+#ifdef defined(CONFIG_MAC)
+       lea     #50F04000,%a0
+       lea     %pc@(scc_initable_mac:w),%a1
+2:     moveb   %a1@+,%d0
+       jmi     3f
+       moveb   %d0,%a0@
+       moveb   %a1@+,%a0@
+       jra     2b
#endif
9:
       rts
@@ -1025,6 +1315,16 @@
       jeq     1b
       jra     9f
2:
+#endif
+#ifdef CONFIG_MAC
+#if 0
+       /* should check if MAC .. - note this base is for a MacII and later ! */
+       lea     0x50F04000,%a1
+3:     btst    #2,%a1@(0)
+       jeq     3b
+#endif
+       moveb   %d7,%a1@(4)
+       jra     9f
#endif
#ifdef CONFIG_ATARI
       cmpil   #MACH_ATARI,%d4
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/arch/m68k/kernel/ints.c linux-2.0.29/arch/m68k/kernel/ints.c
--- linux.vanilla/arch/m68k/kernel/ints.c       Sat Oct  5 14:24:07 1996
+++ linux-2.0.29/arch/m68k/kernel/ints.c        Tue Mar 25 14:45:51 1997
@@ -25,6 +25,7 @@
 *           which must be served                               /Roman Zippel
 */

+#include <linux/config.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/kernel_stat.h>
@@ -178,7 +179,7 @@
                       panic("Can't process interrupt vector %ld\n", vec);
               return;
       }
-
+
       vec -= VEC_SPUR;
       kstat.interrupts[vec]++;
       irq_list[vec].handler(vec, irq_list[vec].dev_id, fp);
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/arch/m68k/kernel/setup.c linux-2.0.29/arch/m68k/kernel/setup.c
--- linux.vanilla/arch/m68k/kernel/setup.c      Sat Oct  5 14:24:07 1996
+++ linux-2.0.29/arch/m68k/kernel/setup.c       Tue Mar 25 16:13:26 1997
@@ -127,6 +127,10 @@

       memory_start = availmem;
       memory_end = 0;
+
+       /* AC MAC HACK */
+       if(boot_info.num_memory==0)
+               boot_info.num_memory=1;

       for (i = 0; i < boot_info.num_memory; i++)
               memory_end += boot_info.memory[i].size & MASK_256K;
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/arch/m68k/kernel/traps.c linux-2.0.29/arch/m68k/kernel/traps.c
--- linux.vanilla/arch/m68k/kernel/traps.c      Sat Oct  5 14:24:07 1996
+++ linux-2.0.29/arch/m68k/kernel/traps.c       Wed Apr 16 16:11:31 1997
@@ -770,6 +770,7 @@
       for (i = 0; i < 10; i++)
               printk("%04x ", 0xffff & ((short *) fp->ptregs.pc)[i]);
       printk ("\n");
+       while(1);       /* AC trap */
}

void bad_super_trap (struct frame *fp)
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/arch/m68k/mac/Makefile linux-2.0.29/arch/m68k/mac/Makefile
--- linux.vanilla/arch/m68k/mac/Makefile        Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/arch/m68k/mac/Makefile Mon Apr 14 15:23:44 1997
@@ -0,0 +1,15 @@
+#
+# Makefile for Linux arch/m68k/atari source directory
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+EXTRA_CFLAGS := -Wa,-m68020
+
+O_TARGET := mac.o
+O_OBJS  := config.o ksyms.o bootparse.o macfb.o macints.o via6522.o adb-bus.o
+
+include $(TOPDIR)/Rules.make
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/arch/m68k/mac/adb-bus.c linux-2.0.29/arch/m68k/mac/adb-bus.c
--- linux.vanilla/arch/m68k/mac/adb-bus.c       Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/arch/m68k/mac/adb-bus.c        Tue Apr 15 15:52:04 1997
@@ -0,0 +1,644 @@
+/*
+ *     MACII   ADB keyboard handler.
+ *             Copyright (c) 1997 Alan Cox
+ *
+ *     Derived from code
+ *             Copyright (C) 1996 Paul Mackerras.
+ */
+
+#include <stdarg.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/miscdevice.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include "via6522.h"
+#include <asm/io.h>
+#include <asm/adb.h>
+#include <asm/system.h>
+#include <asm/segment.h>
+
+
+#define MACII          /* For now - will be a switch */
+
+/* Bits in B data register: all active low */
+#define TREQ           0x08            /* Transfer request (input) */
+#define TACK           0x10            /* Transfer acknowledge (output) */
+#define TIP            0x20            /* Transfer in progress (output) */
+
+/* Bits in ACR */
+#define SR_CTRL                0x1c            /* Shift register control bits */
+#define SR_EXT         0x0c            /* Shift on external clock */
+#define SR_OUT         0x10            /* Shift out if 1 */
+
+/* Bits in IFR and IER */
+#define IER_SET                0x80            /* set bits in IER */
+#define IER_CLR                0               /* clear bits in IER */
+#define SR_INT         0x04            /* Shift register full/empty */
+
+static struct adb_handler {
+    void (*handler)(unsigned char *, int, struct pt_regs *);
+} adb_handler[16];
+
+static enum adb_state {
+    idle,
+    sent_first_byte,
+    sending,
+    reading,
+    read_done,
+    awaiting_reply
+} adb_state;
+
+static struct adb_request *current_req;
+static struct adb_request *last_req;
+static unsigned char cuda_rbuf[16];
+static unsigned char *reply_ptr;
+static int reading_reply;
+static int data_index;
+
+static void adb_start(void);
+extern void adb_interrupt(int irq, void *arg, struct pt_regs *regs);
+static void adb_input(unsigned char *buf, int nb, struct pt_regs *regs);
+
+
+void adb_bus_init(void)
+{
+       unsigned long flags;
+       unsigned char c;
+       int ct;
+
+       save_flags(flags);
+       cli();
+
+       /*
+        *      Setup MacII style ADB - no CUDA
+        */
+#ifdef MACII
+       printk("adb: MacII style keyboard/mouse driver.\n");
+       /* Set the lines up. We want TREQ as input TACK|TIP as output */
+       via_write(via1, vDirB, ((via_read(via1,vDirB)|TACK|TIP)&~TREQ));
+       /* Shift register on input */
+       c=via_read(via1, vACR);
+       c&=~SR_CTRL;            /* Clear shift register bits */
+       c|=SR_EXT;              /* Shift on external clock */
+       via_write(via1, vACR, c);
+       /* Wipe any pending data and int */
+       via_read(via1, vSR);
+       /* This is interrupts on enable SR for keyboard */
+       via_write(via1, vIER, IER_SET|SR_INT);
+       /* This clears the interrupt bit */
+       via_write(via1, vIFR, SR_INT);
+       /* Lower the bus signals (MacII is active low it seems ???) */
+       via_write(via1, vBufB, via_read(via1, vBufB)&~TACK);
+
+       ct=1000;
+       while( ct-- && (via_read(via1, vBufB)&TREQ))
+               udelay(1000);
+       if(ct<0)
+               printk("No sync occured\n");
+       ct=1000;
+       while( ct-- && !(via_read(via1, vIFR)&SR_INT))
+               udelay(1000);
+       if(ct<0)
+               printk("No sync 2 occured\n");
+       via_read(via1, vSR);
+       via_write(via1, vBufB, via_read(via1, vBufB)|TACK);
+       while( ct-- && !(via_read(via1, vBufB)&TREQ))
+               udelay(1000);
+       if(ct<0)
+               printk("No sync 3 occured\n");
+       ct=1000;
+       while( ct-- && !(via_read(via1, vIFR)&SR_INT))
+               udelay(1000);
+       if(ct<0)
+               printk("No sync 4 occured\n");
+       via_read(via1, vSR);
+       via_write(via1, vBufB, via_read(via1, vBufB)|TIP);
+#endif
+       /*
+        *      Ok we probably ;) have a ready to use adb bus. Its also
+        *      hopefully idle (Im assuming the mac didnt leave a half
+        *      complete transaction on booting us).
+        */
+
+       adb_state = idle;
+       via_setup_keyboard();
+       restore_flags(flags);
+}
+
+#define WAIT_FOR(cond, what)                           \
+    do {                                               \
+       for (x = 1000; !(cond); --x) {                  \
+           if (x == 0) {                               \
+               printk("Timeout waiting for " what);    \
+               return 0;                               \
+           }                                           \
+           __delay(100*160);                                   \
+       }                                               \
+    } while (0)
+
+/* Construct and send an adb request */
+int adb_request(struct adb_request *req, void (*done)(struct adb_request *),
+            int nbytes, ...)
+{
+       va_list list;
+       int i;
+
+       req->nbytes = nbytes;
+       req->done = done;
+       va_start(list, nbytes);
+       for (i = 0; i < nbytes; ++i)
+               req->data[i] = va_arg(list, int);
+       va_end(list);
+       req->reply_expected = 1;
+       return adb_send_request(req);
+}
+
+int adb_send_request(struct adb_request *req)
+{
+       unsigned long flags;
+
+       req->next = 0;
+       req->sent = 0;
+       req->got_reply = 0;
+       req->reply_len = 0;
+       save_flags(flags);
+       cli();
+
+       if (current_req != NULL)
+       {
+               last_req->next = req;
+               last_req = req;
+       }
+       else
+       {
+               current_req = req;
+               last_req = req;
+               if (adb_state == idle)
+                       adb_start();
+       }
+
+       restore_flags(flags);
+       return 0;
+}
+
+static void adb_start(void)
+{
+       unsigned long flags;
+       struct adb_request *req;
+
+       /* assert adb_state == idle */
+       /* get the packet to send */
+       req = current_req;
+       if (req == 0)
+               return;
+       save_flags(flags);
+       cli();
+
+       printk("adb_start: ");
+
+       if ((via_read(via1, vBufB)& TREQ) == 0)
+       {
+               /*
+                *      FIXME - we need to restart this on a timer
+                *      or a collision at boot hangs us.
+                */
+               printk("device busy - fail\n");
+               restore_flags(flags);
+               return;                 /* a byte is coming in from the CUDA */
+       }
+
+#ifdef CUDA
+       /* set the shift register to shift out and send a byte */
+       via_write(via1, vACR, via_read(via1, vACR)|SR_OUT);
+       via_write(via1, vSR, req->data[0]);
+       via_write(via1, vBufB, via_read(via1, vBufB)&~TIP);
+#endif
+#ifdef MACII
+       /* Output mode */
+       via_write(via1, vACR, via_read(via1, vACR)|SR_OUT);
+       /* Load data */
+       via_write(via1, vSR, req->data[0]);
+       /* Turn off TIP/TACK */
+       via_write(via1, vBufB, via_read(via1, vBufB)&~(TIP|TACK));
+#endif
+       adb_state = sent_first_byte;
+       printk("sent first byte of %d\n", req->nbytes);
+       restore_flags(flags);
+}
+
+void adb_poll(void)
+{
+       unsigned char c;
+       unsigned long flags;
+       save_flags(flags);
+       cli();
+       c=via_read(via1, vIFR);
+       printk("adb_poll: %x   \r", c);
+       if (c & SR_INT)
+       {
+               printk("adb interrupt event\n");
+               adb_interrupt(0, 0, 0);
+       }
+       restore_flags(flags);
+}
+
+void adb_interrupt(int irq, void *arg, struct pt_regs *regs)
+{
+       int x, status;
+       struct adb_request *req;
+
+       /* if IRQ==0 its a poll and we've checked and cleared
+          vIFR already */
+
+       if (irq && (via_read(via1, vIFR)& SR_INT) == 0)
+               return;
+
+       status = (~via_read(via1, vBufB) & (TIP|TREQ)) | (via_read(via1, vACR) & SR_OUT);
+       printk("adb_interrupt: state=%d status=%x\n", adb_state, status);
+       switch (adb_state)
+       {
+               case idle:
+#ifdef CUDA
+                       /* CUDA has sent us the first byte of data - unsolicited */
+                       if (status != TREQ)
+                               printk("cuda: state=idle, status=%x\n", status);
+                       x = via_read(via1, vSR);
+                       via[B] &= ~TIP;
+#endif
+#ifdef MACII
+                       if (status != TREQ)
+                               printk("adb_macII: state=idle status=%x\n",
+                                       status);
+                       x = via_read(via1, vSR);
+                       via_write(via1, vBufB, via_read(via1, vBufB)&~(TIP|TACK));
+#endif
+                       adb_state = reading;
+                       reply_ptr = cuda_rbuf;
+                       reading_reply = 0;
+                       break;
+
+               case awaiting_reply:
+#ifdef CUDA
+                       /* CUDA has sent us the first byte of data of a reply */
+                       if (status != TREQ)
+                               printk("cuda: state=awaiting_reply, status=%x\n", status);
+                       x = via[SR];
+                       via[B] &= ~TIP;
+#endif
+                       adb_state = reading;
+                       reply_ptr = current_req->reply;
+                       reading_reply = 1;
+                       break;
+
+               case sent_first_byte:
+#ifdef CUDA
+                       if (status == TREQ + TIP + SR_OUT)
+                       {
+                               /* collision */
+                               via[ACR] &= ~SR_OUT;
+                               x = via[SR];
+                               via[B] |= TIP | TACK;
+                               adb_state = idle;
+                       }
+                       else
+                       {
+                               /* assert status == TIP + SR_OUT */
+                               if (status != TIP + SR_OUT)
+                                       printk("cuda: state=sent_first_byte status=%x\n", status);
+                               via[SR] = current_req->data[1];
+                               via[B] ^= TACK;
+                               data_index = 2;
+                               adb_state = sending;
+                       }
+#endif
+#ifdef MACII
+                       if(status!=TIP+SR_OUT)
+                               printk("adb_macII: state=send_first_byte status=%x\n", status);
+                       via_write(via1, vSR, current_req->data[1]);
+                       via_write(via1, vBufB,
+                               via_read(via1, vBufB)^TACK);
+                       data_index=2;
+                       adb_state = sending;
+#endif
+                       break;
+
+               case sending:
+                       req = current_req;
+                       if (data_index >= req->nbytes)
+                       {
+#ifdef CUDA
+                               via[ACR] &= ~SR_OUT;
+                               x = via[SR];
+                               via[B] |= TACK | TIP;
+#endif
+#ifdef MACII
+                               via_write(via1, vACR,
+                                       via_read(via1, vACR) & ~SR_OUT);
+                               x=via_read(via1, vSR);
+                               via_write(via1, vBufB,
+                                       via_read(via1, vBufB)|TACK|TIP);
+#endif
+                               req->sent = 1;
+                               if (req->reply_expected)
+                               {
+                                       adb_state = awaiting_reply;
+                               }
+                               else
+                               {
+                                       current_req = req->next;
+                                       if (req->done)
+                                               (*req->done)(req);
+                                       /* not sure about this */
+                                       adb_state = idle;
+                                       adb_start();
+                               }
+                       }
+                       else
+                       {
+#ifdef CUDA
+                               via[SR] = req->data[data_index++];
+                               via[B] ^= TACK;
+#endif
+#ifdef MACII
+                               via_write(via1, vSR, req->data[data_index++]);
+                               via_write(via1, vBufB,
+                                       via_read(via1, vBufB)^TACK);
+#endif
+                       }
+                       break;
+
+               case reading:
+                       *reply_ptr++ = via_read(via1, vSR);
+#ifdef CUDA
+                       if (status == TIP)
+                       {
+                               /* that's all folks */
+                               via[B] |= TACK | TIP;
+                               adb_state = read_done;
+                       }
+                       else
+                       {
+                               /* assert status == TIP | TREQ */
+                               if (status != TIP + TREQ)
+                                       printk("cuda: state=reading status=%x\n", status);
+                               via[B] ^= TACK;
+                       }
+#endif
+#ifdef MACII
+                       if( status == TIP)
+                       {
+                               via_write(via1, vBufB,
+                                       via_read(via1, vBufB)|TACK|TIP);
+                               adb_state = read_done;
+                       }
+                       else
+                       {
+                               if(status!=TIP+TREQ)
+                                       printk("macII_adb: state=reading status=%x\n", status);
+                               via_write(via1, vBufB,
+                                       via_read(via1, vBufB)^TACK);
+                       }
+#endif
+                       break;
+
+               case read_done:
+                       x = via_read(via1, vSR);
+                       if (reading_reply)
+                       {
+                               req = current_req;
+                               req->reply_len = reply_ptr - req->reply;
+                               req->got_reply = 1;
+                               current_req = req->next;
+                               if (req->done)
+                                       (*req->done)(req);
+                       }
+                       else
+                       {
+                               adb_input(cuda_rbuf, reply_ptr - cuda_rbuf, regs);
+                       }
+
+                       if (status == TREQ)
+                       {
+                               via_write(via1, vBufB,
+                                       via_read(via1, vBufB)|~TIP);
+                               adb_state = reading;
+                               reply_ptr = cuda_rbuf;
+                               reading_reply = 0;
+                       }
+                       else
+                       {
+                               adb_state = idle;
+                               adb_start();
+                       }
+                       break;
+
+               default:
+                       printk("adb_interrupt: unknown adb_state %d?\n", adb_state);
+       }
+}
+
+static void adb_input(unsigned char *buf, int nb, struct pt_regs *regs)
+{
+       int i, id;
+
+       switch (buf[0])
+       {
+               case ADB_PACKET:
+                       id = buf[2] >> 4;
+#if 0
+                       xmon_printf("adb packet: ");
+                       for (i = 0; i < nb; ++i)
+                               xmon_printf(" %x", buf[i]);
+                       xmon_printf(", id = %d\n", id);
+#endif
+                       if (adb_handler[id].handler != 0)
+                       {
+                               (*adb_handler[id].handler)(buf, nb, regs);
+                       }
+                       break;
+
+               default:
+                       printk("data from via (%d bytes):", nb);
+                       for (i = 0; i < nb; ++i)
+                               printk(" %.2x", buf[i]);
+                       printk("\n");
+       }
+}
+
+/* Ultimately this should return the number of devices with
+   the given default id. */
+
+int adb_register(int default_id,
+            void (*handler)(unsigned char *, int, struct pt_regs *))
+{
+       if (adb_handler[default_id].handler != 0)
+               panic("Two handlers for ADB device %d\n", default_id);
+       adb_handler[default_id].handler = handler;
+       return 1;
+}
+
+/*
+ * Here are the file operations we export for /dev/adb.
+ */
+
+#define ADB_MINOR      140     /* /dev/adb is c 10 140 */
+
+extern void adbdev_inits(void);
+
+struct adbdev_state {
+       struct adb_request req;
+};
+
+static struct wait_queue *adb_wait;
+
+static int adb_wait_reply(struct adbdev_state *state, struct file *file)
+{
+       int ret = 0;
+       struct wait_queue wait = { current, NULL };
+
+       add_wait_queue(&adb_wait, &wait);
+       current->state = TASK_INTERRUPTIBLE;
+
+       while (!state->req.got_reply) {
+               if (file->f_flags & O_NONBLOCK) {
+                       ret = -EAGAIN;
+                       break;
+               }
+               if (current->signal & ~current->blocked) {
+                       ret = -ERESTARTSYS;
+                       break;
+               }
+               schedule();
+       }
+
+       current->state = TASK_RUNNING;
+       remove_wait_queue(&adb_wait, &wait);
+
+       return ret;
+}
+
+static void adb_write_done(struct adb_request *req)
+{
+       if (!req->got_reply) {
+               req->reply_len = 0;
+               req->got_reply = 1;
+       }
+       wake_up_interruptible(&adb_wait);
+}
+
+static int adb_open(struct inode *inode, struct file *file)
+{
+       struct adbdev_state *state;
+
+       state = kmalloc(sizeof(struct adbdev_state), GFP_KERNEL);
+       if (state == 0)
+               return -ENOMEM;
+       file->private_data = state;
+       state->req.reply_expected = 0;
+       return 0;
+}
+
+static void adb_release(struct inode *inode, struct file *file)
+{
+       struct adbdev_state *state = file->private_data;
+
+       if (state) {
+               file->private_data = NULL;
+               if (state->req.reply_expected && !state->req.got_reply)
+                       if (adb_wait_reply(state, file))
+                               return;
+               kfree(state);
+       }
+       return;
+}
+
+static int adb_lseek(struct inode *inode, struct file *file,
+                    off_t offset, int origin)
+{
+       return -ESPIPE;
+}
+
+static int adb_read(struct inode *inode, struct file *file,
+                   char *buf, int count)
+{
+       int ret;
+       struct adbdev_state *state = file->private_data;
+
+       if (count < 2)
+               return -EINVAL;
+       if (count > sizeof(state->req.reply))
+               count = sizeof(state->req.reply);
+       ret = verify_area(VERIFY_WRITE, buf, count);
+       if (ret)
+               return ret;
+
+       if (!state->req.reply_expected)
+               return 0;
+
+       ret = adb_wait_reply(state, file);
+       if (ret)
+               return ret;
+
+       ret = state->req.reply_len;
+       memcpy_tofs(buf, state->req.reply, ret);
+       state->req.reply_expected = 0;
+
+       return ret;
+}
+
+static int adb_write(struct inode *inode, struct file *file,
+                    const char *buf, int count)
+{
+       int ret;
+       struct adbdev_state *state = file->private_data;
+
+       if (count < 2 || count > sizeof(state->req.data))
+               return -EINVAL;
+       ret = verify_area(VERIFY_READ, buf, count);
+       if (ret)
+               return ret;
+
+       if (state->req.reply_expected && !state->req.got_reply) {
+               /* A previous request is still being processed.
+                  Wait for it to finish. */
+               ret = adb_wait_reply(state, file);
+               if (ret)
+                       return ret;
+       }
+
+       state->req.nbytes = count;
+       state->req.done = adb_write_done;
+       memcpy_fromfs(state->req.data, buf, count);
+       state->req.reply_expected = 1;
+       state->req.got_reply = 0;
+       adb_send_request(&state->req);
+
+       return count;
+}
+
+static struct file_operations adb_fops = {
+       adb_lseek,
+       adb_read,
+       adb_write,
+       NULL,           /* no readdir */
+       NULL,           /* no select */
+       NULL,           /* no ioctl */
+       NULL,           /* no mmap */
+       adb_open,
+       adb_release
+};
+
+static struct miscdevice adb_dev = {
+       ADB_MINOR,
+       "adb",
+       &adb_fops
+};
+
+void adbdev_init()
+{
+       misc_register(&adb_dev);
+}
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/arch/m68k/mac/bootparse.c linux-2.0.29/arch/m68k/mac/bootparse.c
--- linux.vanilla/arch/m68k/mac/bootparse.c     Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/arch/m68k/mac/bootparse.c      Tue Mar 25 18:16:37 1997
@@ -0,0 +1,89 @@
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <asm/setup.h>
+
+/*
+ *     Booter vars
+ */
+
+int boothowto;
+
+
+/*
+ *     Called early to parse the environment (passed to us from the booter)
+ *     into a bootinfo struct. Will die as soon as we have our own booter
+ */
+
+#define atol(x)        simple_strtoul(x,NULL,0)
+
+void parse_booter(char *env)
+{
+       char *name;
+       char *value;
+       while(*env)
+       {
+               name=env;
+               value=name;
+               while(*value!='='&&*value)
+                       value++;
+               if(*value=='=')
+                       *value++=0;
+               env=value;
+               while(*value)
+                       value++;
+#if 0
+               if(strcmp(name,"VIDEO_ADDR")==0)
+                       boot_info.bi_mac.videoaddr=atol(value);
+               if(strcmp(name,"ROW_BYTES")==0)
+                       boot_info.bi_mac.videorow=atol(value);
+               if(strcmp(name,"SCREEN_DEPTH")==0)
+                       boot_info.bi_mac.videodepth=atol(value);
+               if(strcmp(name,"DIMENSIONS")==0)
+                       boot_info.bi_mac.dimensions=atol(value);
+#endif
+               if(strcmp(name,"BOOTTIME")==0)
+                       boot_info.bi_mac.boottime=atol(value);
+               if(strcmp(name,"GMTBIAS")==0)
+                       boot_info.bi_mac.gmtbias=atol(value);
+               if(strcmp(name,"BOOTERVER")==0)
+                       boot_info.bi_mac.bootver=atol(value);
+               if(strcmp(name,"MACOS_VIDEO")==0)
+                       boot_info.bi_mac.videological=atol(value);
+               if(strcmp(name,"MACOS_SCC")==0)
+                       boot_info.bi_mac.scc=atol(value);
+               if(strcmp(name,"MACHINEID")==0)
+                       boot_info.bi_mac.id=atol(value);
+               if(strcmp(name,"MEMSIZE")==0)
+                       boot_info.bi_mac.memsize=atol(value);
+               if(strcmp(name,"SERIAL_MODEM_FLAGS")==0)
+                       boot_info.bi_mac.serialmf=atol(value);
+               if(strcmp(name,"SERIAL_MODEM_HSKICLK")==0)
+                       boot_info.bi_mac.serialhsk=atol(value);
+               if(strcmp(name,"SERIAL_MODEM_GPICLK")==0)
+                       boot_info.bi_mac.serialgpi=atol(value);
+               if(strcmp(name,"SERIAL_PRINT_FLAGS")==0)
+                       boot_info.bi_mac.printf=atol(value);
+               if(strcmp(name,"SERIAL_PRINT_HSKICLK")==0)
+                       boot_info.bi_mac.printhsk=atol(value);
+               if(strcmp(name,"SERIAL_PRINT_GPICLK")==0)
+                       boot_info.bi_mac.printgpi=atol(value);
+               if(strcmp(name,"PROCESSOR")==0)
+                       boot_info.bi_mac.cpuid=atol(value);
+               if(strcmp(name,"ROMBASE")==0)
+                       boot_info.bi_mac.rombase=atol(value);
+               if(strcmp(name,"TIMEDBRA")==0)
+                       boot_info.bi_mac.timedbra=atol(value);
+               if(strcmp(name,"ADBDELAY")==0)
+                       boot_info.bi_mac.adbdelay=atol(value);
+       }
+       /* Fill in the base stuff */
+       boot_info.machtype=MACH_MAC;
+       /* Read this from the macinfo we got ! */
+       boot_info.cputype=CPU_68020|FPUB_68881;
+/*     boot_info.memory[0].addr=0;
+       boot_info.memory[0].size=boot_info.bi_mac.memsize;*/
+       boot_info.num_memory=1;         /* On a MacII */
+       boot_info.ramdisk_size=0;       /* For now */
+       *boot_info.command_line=0;
+ }
+
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/arch/m68k/mac/config.c linux-2.0.29/arch/m68k/mac/config.c
--- linux.vanilla/arch/m68k/mac/config.c        Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/arch/m68k/mac/config.c Tue Apr 15 12:01:40 1997
@@ -0,0 +1,190 @@
+/*
+ *  linux/arch/m68k/mac/config.c
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of this archive
+ * for more details.
+ */
+
+/*
+ * Miscellaneous linux stuff
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/kd.h>
+#include <linux/tty.h>
+#include <linux/console.h>
+#include <linux/interrupt.h>
+
+#include <asm/setup.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/pgtable.h>
+#include <asm/machdep.h>
+
+#include "via6522.h"
+
+void *mac_env;         /* Loaded by the boot asm */
+
+extern void (*kd_mksound)(unsigned int, unsigned int);
+
+void mac_get_model(char *str)
+{
+       strcpy(str,"Macintosh");
+}
+
+extern void mac_reset();
+
+void mac_bang(int irq, void *vector, struct pt_regs *p)
+{
+       mac_reset();
+}
+
+void mac_sched_init(void (*vector)(int, void *, struct pt_regs *))
+{
+       via_init_clock(vector);
+       request_irq(1, via1_irq, IRQ_FLG_LOCK, "via1", via1_irq);
+       request_irq(6, mac_bang, IRQ_FLG_LOCK, "offswitch", mac_bang);
+}
+
+int mac_keyb_init(void)
+{
+       panic("keyb_init:notused");
+}
+
+int mac_kbdrate(struct kbd_repeat *k)
+{
+       return 0;
+}
+
+void mac_kbd_leds(unsigned int leds)
+{
+       ;
+}
+
+
+unsigned long mac_gettimeoffset (void)
+{
+       return 0L;
+}
+
+void mac_mksound( unsigned int count, unsigned int ticks )
+{
+       ;
+}
+
+
+void mac_waitbut (void)
+{
+       ;
+}
+
+extern struct consw fb_con;
+extern struct fb_info *mac_fb_init(long *);
+extern void mac_video_setup(char *, int *);
+
+void mac_debug_init (void)
+{
+       ;
+}
+
+
+extern void mac_init_IRQ(void);
+extern int mac_request_irq (unsigned int, void (*)(int, void *,
+                               struct pt_regs *),
+                               unsigned long, const char *, void *);
+extern int mac_free_irq(unsigned int, void *);
+extern void mac_enable_irq(unsigned int);
+extern void mac_disable_irq(unsigned int);
+extern int mac_get_irq_list(char *);
+extern void mac_default_handler(int irq);
+
+
+void (*mac_handlers[8])(int, void *, struct pt_regs *)=
+{
+       mac_default_handler,
+       mac_default_handler,
+       mac_default_handler,
+       mac_default_handler,
+       mac_default_handler,
+       mac_default_handler,
+       mac_default_handler,
+       mac_default_handler
+};
+
+void config_mac(void)
+{
+    int xd,yd;
+    int y;
+    unsigned char c=0xF0;
+    unsigned char *bp=(unsigned char *)boot_info.bi_mac.videoaddr;
+
+    parse_booter(mac_env);
+
+    xd=boot_info.bi_mac.dimensions;
+
+    yd=xd>>16;
+    xd&=0xFFFF;
+
+    if(xd<512||yd <256|| boot_info.bi_mac.videoaddr!=0xFDD00020)
+    {
+       boot_info.bi_mac.videoaddr=0xFDD00020;
+       mac_boom(9);
+    }
+
+    for(y=0;y<16;y++)
+    {
+       memset(bp+y*boot_info.bi_mac.videorow,
+               0x00,boot_info.bi_mac.videorow);
+    }
+    memset(bp+y*boot_info.bi_mac.videorow,
+               0xFF,boot_info.bi_mac.videorow);
+
+    *bp=0xFF;
+    bp[boot_info.bi_mac.videorow-1]=0xFF;
+    bp[boot_info.bi_mac.videorow]=0xF0;
+    bp[boot_info.bi_mac.videorow*2-1]=0x0F;
+
+    for(y=17;y<yd;y++)
+    {
+       memset(bp+y*boot_info.bi_mac.videorow,
+               c,boot_info.bi_mac.videorow);
+       c^=0xFF;
+    }
+
+    mac_debugging_penguin(5);
+
+    mach_sched_init      = mac_sched_init;
+    mach_keyb_init       = mac_keyb_init;
+    mach_kbdrate         = mac_kbdrate;
+    mach_kbd_leds        = mac_kbd_leds;
+    mach_init_IRQ        = mac_init_IRQ;
+    mach_request_irq     = mac_request_irq;
+    mach_free_irq        = mac_free_irq;
+    mach_enable_irq      = mac_enable_irq;
+    mach_disable_irq     = mac_disable_irq;
+    mach_default_handler = mac_handlers;
+    mach_get_irq_list   = mac_get_irq_list;
+    mach_gettimeoffset   = mac_gettimeoffset;
+    mach_mksound         = mac_mksound;
+    mach_reset           = mac_reset;
+#ifdef CONFIG_BLK_DEV_FD
+    mach_floppy_init    = mac_floppy_init;
+    mach_floppy_setup   = mac_floppy_setup;
+#endif
+    conswitchp          = &fb_con;
+    waitbut             = mac_waitbut;
+    mach_fb_init         = mac_fb_init;
+    mach_max_dma_address = 0xffffffff;
+    mach_debug_init     = mac_debug_init;
+    mach_video_setup    = mac_video_setup;
+    kd_mksound          = mac_mksound;
+
+    /*
+     * Determine hardware present
+     */
+}
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/arch/m68k/mac/ksyms.c linux-2.0.29/arch/m68k/mac/ksyms.c
--- linux.vanilla/arch/m68k/mac/ksyms.c Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/arch/m68k/mac/ksyms.c  Fri Mar 14 12:34:34 1997
@@ -0,0 +1,13 @@
+#include <linux/module.h>
+#include <asm/ptrace.h>
+#include <asm/traps.h>
+
+static struct symbol_table mach_mac_symbol_table = {
+#include <linux/symtab_begin.h>
+#include <linux/symtab_end.h>
+};
+
+void mach_mac_syms_export(void)
+{
+       register_symtab(&mach_mac_symbol_table);
+}
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/arch/m68k/mac/mac.h linux-2.0.29/arch/m68k/mac/mac.h
--- linux.vanilla/arch/m68k/mac/mac.h   Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/arch/m68k/mac/mac.h    Fri Mar 21 14:43:30 1997
@@ -0,0 +1,74 @@
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0xFF,0x0F,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x0F,0x00,0x00,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0xF0,0xF0,0xF0,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0xF0,0x0F,0x00,0x00,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0F,0x00,0xF0,0x0F,0x00,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x0F,0x00,0xF0,0x00,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0xF0,0x00,0x00,0xF0,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0xF0,0xF0,0x00,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0xF0,0x00,0x00,0x00,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0xF0,0x00,0xF0,0xF0,0xF0,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0xF0,0x00,0x0F,0x00,0x00,0x00,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x0F,0x00,0x00,0xF0,0x00,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x0F,0x0F,0x00,0x00,0xF0,0x00,0xF0,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x0F,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x0F,0x0F,0x00,0x00,0x00,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0F,0x00,0xF0,0xF0,0xF0,0x0F,0x0F,0x00,0xF0,0x0F,0x0F,0x00,0x00,0x0F,0x0F,0x00,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0xF0,0x00,0xF0,0xF0,0x0F,0x0F,0x00,0xF0,0x0F,0x0F,0x00,0xF0,0x00,0x00,0x0F,0x00,0x00,0x00,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0xF0,0x0F,0x0F,0x0F,0x00,0x0F,0x00,0xF0,0xF0,0xF0,0x00,0xF0,0xF0,0x00,0xF0,0x0F,0x00,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0F,0xF0,0xFF,0x00,0x0F,0x0F,0x0F,0x00,0x0F,0x00,0xF0,0xF0,0x00,0x0F,0x00,0x00,0xF0,0x00,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0F,0x0F,0x0F,0xF0,0x0F,0x0F,0x00,0x0F,0x0F,0xF0,0x0F,0x00,0x00,0xF0,0x00,0x0F,0x00,0x00,0xF0,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0xFF,0xF0,0xF0,0xF0,0x0F,0xF0,0xF0,0xF0,0x00,0x00,0xF0,0xF0,0x0F,0x0F,0x00,0xF0,0x00,0x0F,0x00,0x00,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x0F,0x0F,0x0F,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0x00,0x00,0x00,0x0F,0x00,0x0F,0x00,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0F,0x00,0xFF,0xF0,0x0F,0x0F,0x00,0x0F,0x00,0xF0,0x00,0x00,0xF0,0xF0,0x0F,0x00,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0x00,0xF0,0x00,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x0F,0x00,0xF0,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0xF0,0xF0,0x0F,0x0F,0x00,0x0F,0x00,0x00,0x00,0x0F,0x00,0xF0,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x0F,0x0F,0x0F,0x0F,0xF0,0xF0,0xF0,0x0F,0x00,0x00,0x00,0xF0,0x0F,0x0F,0x00,0xF0,0xF0,0x0F,0x0F,0x0F,0x00,0x0F,0x00,
+0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0F,0x00,0x0F,0xFF,0x0F,0xF0,0xF0,0xF0,0x00,0x00,0x00,0x00,0x0F,0x0F,0x00,0xF0,0x0F,0x00,0x00,0xF0,0xF0,0x00,0xF0,0x00,0xF0,
+0xFF,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0x0F,0xFF,0xFF,0x0F,0x00,0x0F,0x0F,0x00,0xF0,0xF0,0xF0,0xF0,0xF0,0x00,0xF0,0x00,0xF0,0x00,
+0xFF,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0x0F,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xF0,0xF0,0xFF,0x00,0x00,0x0F,0xF0,0xF0,0xF0,0xF0,0x0F,0x00,0x0F,0x0F,0x00,0xF0,0x00,0xF0,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x0F,0xFF,0xFF,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0x0F,0x0F,0x00,0xF0,0xF0,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0F,0x00,0x0F,0x0F,0xFF,0xFF,0x0F,0x0F,0xF0,0xFF,0x00,0x00,0x0F,0xFF,0x0F,0x0F,0x0F,0x00,0xF0,0xF0,0xF0,0xF0,0x0F,0x0F,0x00,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x0F,0x00,0xFF,0xFF,0x0F,0xFF,0xFF,0x0F,0xFF,0x0F,0x00,0x0F,0xF0,0xFF,0xFF,0x0F,0x0F,0x0F,0x0F,0x0F,0x00,0xF0,0x00,0x0F,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x0F,0x0F,0xFF,0xFF,0x0F,0x0F,0xF0,0xFF,0x00,0x00,0x00,0x0F,0x0F,0x00,0xF0,0xF0,0xF0,0xF0,0x00,0xF0,0xF0,0xF0,0xF0,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x0F,0xFF,0xFF,0xF0,0xFF,0xFF,0x0F,0xFF,0x0F,0x00,0x00,0xFF,0xF0,0xFF,0x0F,0xF0,0xF0,0xF0,0xF0,0x0F,0x00,0xF0,0xF0,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x0F,0x0F,0x0F,0x0F,0xF0,0xF0,0xFF,0xFF,0x00,0x00,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x00,0xF0,0x0F,0x00,0xF0,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0xF0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0F,0x00,0x0F,0x0F,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x0F,0xFF,0xFF,0xF0,0xF0,0xF0,0xF0,0xFF,0x00,0x00,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x00,0xF0,0xF0,0xF0,0xF0,0x0F,
+0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x0F,0x0F,0xFF,0xFF,0xF0,0xFF,0x0F,0xFF,0x0F,0x00,0x0F,0x0F,0xFF,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x00,0xF0,0xF0,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xF0,0xFF,0xFF,0x00,0x00,0x00,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0x0F,0x0F,0x0F,0x0F,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x0F,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0F,0x00,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x00,0xF0,0xF0,0xF0,0xF0,0xF0,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x0F,0x00,0x0F,0xF0,0x00,0x00,0xFF,0xF0,0xF0,0xF0,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,
+0xF0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x0F,0x00,0x0F,0x00,0x00,0x0F,0x00,0x00,0x0F,0xF0,0xFF,0x0F,0x00,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xFF,0x0F,0x0F,0x0F,0x0F,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x0F,0xFF,0x0F,0x0F,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0xF0,0xF0,0x00,0xF0,0xF0,0xF0,0xF0,0xFF,0x0F,0x0F,0x0F,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x0F,0x00,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xFF,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0xF0,0xF0,0x00,0xF0,0xFF,0x00,0x0F,0x00,0xF0,0x0F,0x0F,0x0F,0x0F,0xFF,0xF0,0xF0,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0xF0,0x00,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,
+0xF0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x0F,0x00,0x00,0xF0,0xF0,0xF0,0x0F,0x0F,0x0F,0x0F,0x00,0xF0,0xF0,0xFF,0x0F,
+0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x00,0x00,0x0F,0x0F,0x0F,0x00,0xF0,0xF0,0xF0,0xFF,0x0F,0x0F,0x0F,0x0F,0x0F,
+0xF0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0xF0,0x00,0xF0,0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x00,0x0F,0x0F,0x0F,0x0F,0x00,0xF0,0xF0,0xF0,0x0F,0x00,0xF0,0xF0,0xF0,
+0xFF,0x00,0xF0,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0xF0,0x0F,0x0F,0x00,0x00,0xFF,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0xF0,0xFF,0x0F,0x0F,0x0F,
+0xFF,0xFF,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x00,0x00,0xF0,0xF0,0xFF,0x00,0xF0,0xF0,0x0F,0x00,0xF0,0xF0,0xF0,
+0x0F,0xF0,0xF0,0xF0,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x0F,0x00,0x00,0xF0,0x00,0x00,0xF0,0x00,0x00,0x00,0x0F,0x0F,0x0F,0x0F,0x00,0x0F,0xF0,0xF0,0x0F,0x0F,0x0F,0x0F,
+0xF0,0xF0,0xF0,0x0F,0x0F,0x0F,0x0F,0xFF,0xFF,0x0F,0x00,0xF0,0xF0,0x0F,0x0F,0x0F,0x00,0xFF,0x0F,0x0F,0x00,0xF0,0xF0,0xFF,0x0F,0x00,0xF0,0xF0,0xF0,0x0F,0x0F,0x00,
+0x0F,0x0F,0x0F,0x00,0xF0,0x0F,0x00,0x00,0xF0,0xF0,0x00,0x0F,0x00,0x00,0x0F,0x0F,0x0F,0x00,0xF0,0xF0,0x0F,0x0F,0x00,0x0F,0x0F,0x0F,0x00,0x00,0x0F,0x00,0x00,0x0F
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/arch/m68k/mac/macfb.c linux-2.0.29/arch/m68k/mac/macfb.c
--- linux.vanilla/arch/m68k/mac/macfb.c Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/arch/m68k/mac/macfb.c  Mon Mar 24 14:22:22 1997
@@ -0,0 +1,338 @@
+/*
+ *     We've been given MAC frame buffer info by the booter. Now go set it up
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/malloc.h>
+#include <linux/delay.h>
+
+#include <asm/setup.h>
+#include <asm/segment.h>
+#include <asm/pgtable.h>
+#include <asm/irq.h>
+
+#include <linux/fb.h>
+
+#define arraysize(x)    (sizeof(x)/sizeof(*(x)))
+
+static struct fb_var_screeninfo mac_fb_defined={
+       0,0,0,0,        /* W,H, W, H (virtual) load xres,xres_virtual*/
+       0,0,            /* virtual -> visible no offset */
+       8,              /* depth -> load bits_per_pixel */
+       0,              /* greyscale ? */
+       {0,0,0},        /* R */
+       {0,0,0},        /* G */
+       {0,0,0},        /* B */
+       {0,0,0},        /* transparency */
+       0,              /* standard pixel format */
+       FB_ACTIVATE_NOW,
+       274,195,        /* 14" monitor *Mikael Nykvist's anyway* */
+       FB_ACCEL_NONE,  /* The only way to accelerate a mac is .. */
+       0L,0L,0L,0L,0L,
+       0L,0L,0,        /* No sync info */
+       FB_VMODE_NONINTERLACED,
+       {0,0,0,0,0,0}
+};
+
+#define NUM_TOTAL_MODES                1
+#define NUM_PREDEF_MODES       1
+
+static struct display disp[MAX_NR_CONSOLES];
+static struct fb_info fb_info;
+static int node;
+
+struct mac_fb_par
+{
+       void *unused;
+};
+
+static int currcon = 0;
+static int current_par_valid = 0;
+struct mac_fb_par current_par;
+
+static int mac_xres,mac_yres,mac_depth, mac_xbytes, mac_vxres;
+static unsigned long mac_videobase;
+static unsigned long mac_videosize;
+
+
+static void mac_fb_encode_var(struct fb_var_screeninfo *var)
+{
+       int i=0;
+       var->xres=mac_xres;
+       var->yres=mac_yres;
+       var->xres_virtual=mac_vxres;
+       var->yres_virtual=var->yres;
+       var->xoffset=0;
+       var->yoffset=0;
+       var->bits_per_pixel = mac_depth;
+       var->grayscale=0;
+       var->transp.offset=0;
+       var->transp.length=0;
+       var->transp.msb_right=0;
+       var->nonstd=0;
+       var->activate=0;
+       var->height= -1;
+       var->width= -1;
+       var->accel=0;
+       var->vmode=FB_VMODE_NONINTERLACED;
+       var->pixclock=0;
+       var->sync=0;
+       var->left_margin=0;
+       var->right_margin=0;
+       var->upper_margin=0;
+       var->lower_margin=0;
+       var->hsync_len=0;
+       var->vsync_len=0;
+       for(i=0;i<arraysize(var->reserved);i++)
+               var->reserved[i]=0;
+       return;
+}
+
+
+static void mac_fb_get_par(struct mac_fb_par *par)
+{
+       *par=current_par;
+}
+
+static void mac_fb_set_par(struct mac_fb_par *par)
+{
+       current_par_valid=1;
+}
+
+static int fb_update_var(int con)
+{
+       return 0;
+}
+
+static int do_fb_set_var(struct fb_var_screeninfo *var, int isactive)
+{
+       mac_fb_encode_var(var);
+       return 0;
+}
+
+static void mac_fb_encode_fix(struct fb_fix_screeninfo *fix)
+{
+       int i;
+       strcpy(fix->id,"Macintosh");
+       fix->smem_start=mac_videobase;
+       fix->smem_len=mac_videosize;
+       fix->type = FB_TYPE_PACKED_PIXELS;
+       fix->visual = FB_VISUAL_PSEUDOCOLOR;
+       fix->xpanstep=0;
+       fix->ypanstep=0;
+       fix->ywrapstep=0;
+       fix->line_length=mac_xbytes;
+       for(i=0;i<arraysize(fix->reserved);i++)
+               fix->reserved[i]=0;
+       return;
+}
+
+
+
+static int mac_fb_get_fix(struct fb_fix_screeninfo *fix, int con)
+{
+       struct mac_fb_par par;
+       mac_fb_get_par(&par);
+       mac_fb_encode_fix(fix);
+       return 0;
+}
+
+static int mac_fb_get_var(struct fb_var_screeninfo *var, int con)
+{
+       struct mac_fb_par par;
+       if(con==-1)
+       {
+               mac_fb_get_par(&par);
+               mac_fb_encode_var(var);
+       }
+       else
+               *var=disp[con].var;
+       return 0;
+}
+
+static void mac_fb_set_disp(int con)
+{
+       struct fb_fix_screeninfo fix;
+
+       mac_fb_get_fix(&fix,con);
+       if (con == -1)
+               con=0;
+       disp[con].screen_base = (u_char *)fix.smem_start;
+       disp[con].visual = fix.visual;
+       disp[con].type = fix.type;
+       disp[con].type_aux = fix.type_aux;
+       disp[con].ypanstep = fix.ypanstep;
+       disp[con].ywrapstep = fix.ywrapstep;
+       disp[con].line_length = fix.line_length;
+       disp[con].next_line = fix.line_length;
+       disp[con].can_soft_blank = 0;
+       disp[con].inverse = 0;
+}
+
+static int mac_fb_set_var(struct fb_var_screeninfo *var, int con)
+{
+       int err;
+
+       if ((err=do_fb_set_var(var, 1)))
+               return err;
+       return 0;
+}
+
+static int mac_fb_get_cmap(struct fb_cmap *cmap, int kspc, int con)
+{
+       return -EINVAL;
+}
+
+static int mac_fb_set_cmap(struct fb_cmap *cmap, int kspc, int con)
+{
+       return -EINVAL;
+}
+
+static int mac_fb_pan_display(struct fb_var_screeninfo *var, int con)
+{
+       /* no panning */
+       return -EINVAL;
+}
+
+static int mac_fb_ioctl(struct inode *inode, struct file *file,
+       unsigned int cmd, unsigned long arg, int con)
+{
+       return -EINVAL;
+}
+
+static struct fb_ops mac_fb_ops = {
+       mac_fb_get_fix,
+       mac_fb_get_var,
+       mac_fb_set_var,
+       mac_fb_get_cmap,
+       mac_fb_set_cmap,
+       mac_fb_pan_display,
+       mac_fb_ioctl
+};
+
+void mac_video_setup(char *options, int *ints)
+{
+}
+
+static int macfb_switch(int con)
+{
+       do_fb_set_var(&disp[con].var,1);
+       currcon=con;
+       return 0;
+}
+
+/* 0 unblank, 1 blank, 2 no vsync, 3 no hsync, 4 off */
+
+static void macfb_blank(int blank)
+{
+       /* Not supported */
+}
+
+struct fb_info *mac_fb_init(long *mem_start)
+{
+       /* nubus_remap the video .. */
+       int err;
+
+       mac_xres=boot_info.bi_mac.dimensions&0xFFFF;
+       mac_yres=(boot_info.bi_mac.dimensions&0xFFFF0000)>>16;
+       mac_depth=boot_info.bi_mac.videodepth;
+       mac_xbytes=boot_info.bi_mac.videorow;
+       mac_vxres = (mac_xbytes/mac_depth)*8;
+       mac_videosize=mac_xbytes*mac_yres;
+       mac_videobase=boot_info.bi_mac.videoaddr;
+       mac_debugging_penguin(4);
+
+       /*
+        *      Fill in the available video resolution
+        */
+
+        mac_fb_defined.xres=mac_xres;
+        mac_fb_defined.yres=mac_yres;
+        mac_fb_defined.xres_virtual=mac_vxres;
+        mac_fb_defined.yres_virtual=mac_yres;
+        mac_fb_defined.bits_per_pixel=mac_depth;
+
+
+       /*
+        *      Let there be consoles..
+        */
+       err=register_framebuffer("Macintosh", &node, &mac_fb_ops, NUM_TOTAL_MODES, &mac_fb_defined);
+       if(err<0)
+       {
+               mac_boom(5);
+               return NULL;
+       }
+       fb_info.disp=disp;
+       fb_info.switch_con=&macfb_switch;
+       fb_info.updatevar=&fb_update_var;
+       fb_info.blank=&macfb_blank;
+       do_fb_set_var(&mac_fb_defined,1);
+       mac_fb_get_var(&disp[0].var, -1);
+       mac_fb_set_disp(-1);
+
+       return &fb_info;
+}
+
+static char that_penguin[]={
+#include "that_penguin.h"
+};
+
+void mac_debugging_penguin(int peng)
+{
+       unsigned char *pengoffset;
+       unsigned char *pptr;
+       unsigned char *pdptr=that_penguin;
+       int i;
+
+       pengoffset=(unsigned char *)(boot_info.bi_mac.videoaddr+
+                       64*boot_info.bi_mac.videorow)+40*peng;
+
+       pptr=pengoffset;
+
+       for(i=0;i<74;i++)
+       {
+               memcpy(pptr,pdptr,32);
+               pdptr+=32;
+               pptr+=boot_info.bi_mac.videorow;
+       }
+}
+
+static char kaboom_map[]={
+#include "mac.h"
+};
+
+static void mac_boom_boom(void)
+{
+       static unsigned char *boomoffset=NULL;
+       unsigned char *pptr;
+       unsigned char *pdptr=kaboom_map;
+       int i;
+
+       if(!boomoffset)
+               boomoffset=(unsigned char *)(boot_info.bi_mac.videoaddr+
+                       256*boot_info.bi_mac.videorow);
+       else
+               boomoffset+=32;
+
+       pptr=boomoffset;
+
+       for(i=0;i<74;i++)
+       {
+               memcpy(pptr,pdptr,32);
+               pdptr+=32;
+               pptr+=boot_info.bi_mac.videorow;
+       }
+}
+
+void mac_boom(int booms)
+{
+       int i;
+       for(i=0;i<booms;i++)
+               mac_boom_boom();
+       while(1);
+}
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/arch/m68k/mac/macints.c linux-2.0.29/arch/m68k/mac/macints.c
--- linux.vanilla/arch/m68k/mac/macints.c       Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/arch/m68k/mac/macints.c        Tue Mar 25 14:34:25 1997
@@ -0,0 +1,57 @@
+/*
+ *     Macintosh interrupts
+ *
+ *     2       - VIA1 ???
+ *     6       - Off switch
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/kernel_stat.h>
+
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/traps.h>
+
+asmlinkage void bad_interrupt(void);
+
+void mac_init_IRQ(void)
+{
+}
+
+/*
+ *     We have no machine specific interrupts on a macintoy
+ */
+
+int mac_request_irq (unsigned int irq, void (*handler)(int, void *, struct pt_regs *),
+                              unsigned long flags, const char *devname, void *dev_id)
+{
+       return -EINVAL;
+}
+
+int mac_free_irq (unsigned int irq, void *dev_id)
+{
+       return -EINVAL;
+}
+
+void mac_enable_irq (unsigned int irq)
+{
+       ;
+}
+
+void mac_disable_irq (unsigned int irq)
+{
+       ;
+}
+
+int mac_get_irq_list (char *buf)
+{
+       return 0;
+}
+
+void mac_default_handler(int irq)
+{
+/*     printk("Unexpected IRQ %d\n",irq);*/
+}
+
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/arch/m68k/mac/that_penguin.h linux-2.0.29/arch/m68k/mac/that_penguin.h
--- linux.vanilla/arch/m68k/mac/that_penguin.h  Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/arch/m68k/mac/that_penguin.h   Fri Mar 21 13:44:53 1997
@@ -0,0 +1,74 @@
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xF0,0x0F,0xFF,0xFF,0xF0,0x00,0x0F,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0xFF,0xF0,0xFF,0xFF,0x0F,0xF0,0xF0,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0xFF,0x00,0xFF,0xFF,0x0F,0xFF,0x00,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0xFF,0xF0,0x0F,0xFF,0x0F,0xFF,0xF0,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x0F,0xFF,0x00,0x0F,0x0F,0xFF,0xF0,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x0F,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xF0,0x00,0x00,0x00,0x00,0xFF,0x00,0xFF,0xF0,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x0F,0xF0,0x00,0x00,0xFF,0xF0,0x0F,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x0F,0xF0,0xFF,0xFF,0x00,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0xFF,0xF0,0x00,0x0F,0xFF,0xF0,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0x0F,0xFF,0x00,0xFF,0xF0,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xF0,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xF0,0x00,0x0F,0xFF,0xF0,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xF0,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xF0,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x0F,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xF0,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xF0,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x0F,0xF0,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x0F,0xF0,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0xFF,0xF0,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x0F,0xFF,0xFF,0xFF,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x0F,0xFF,0xF0,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x0F,0xFF,0xFF,0xFF,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x0F,0xFF,0xF0,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x0F,0xFF,0xFF,0xFF,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x0F,0xF0,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0xF0,0x00,0x00,0x0F,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xF0,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x0F,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x0F,0xFF,0xFF,0xFF,0x00,0x00,0xF0,0x00,0x00,
+0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x0F,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0xFF,0xFF,0xF0,0x00,0x00,0xF0,0x00,0x00,
+0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x00,0x00,
+0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,
+0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,
+0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xF0,0x00,
+0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xF0,
+0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,
+0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x0F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x0F,0xF0,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,
+0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xF0,0xFF,0xF0,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,
+0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0x00,0x00,
+0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0xFF,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0x00,0x00,0x00,
+0x0F,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0xFF,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,
+0x00,0x00,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xF0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0xFF,0xF0,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xF0,0x00,0x00,0x0F,0xFF,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0x00,0x0F,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/arch/m68k/mac/via6522.c linux-2.0.29/arch/m68k/mac/via6522.c
--- linux.vanilla/arch/m68k/mac/via6522.c       Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/arch/m68k/mac/via6522.c        Tue Apr 15 14:45:38 1997
@@ -0,0 +1,198 @@
+/*
+ *     6522 Versatile Interface Adapter (VIA)
+ *
+ *     There are two of these on the Mac II. Some IRQ's are vectored
+ *     via them as are assorted bits and bobs - eg rtc, adb.
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+
+#include "via6522.h"
+
+volatile unsigned char *via1=(unsigned char *)VIABASE;
+volatile unsigned char *via2=(unsigned char *)VIABASE2;
+
+/*
+ *     VIA1 - hardwired vectors
+ */
+
+extern void via_wtf(int slot, void *via, struct pt_regs *regs);
+extern void adb_interrupt(int slot, void *via, struct pt_regs *regs);
+
+static struct via_irq_tab via1_func_tab=
+{
+       {
+               via_wtf,
+               via_wtf,
+               via_wtf,        /* The frontdesk bus events on a MacII anyway */
+               via_wtf,
+               via_wtf,
+               via_wtf,
+               via_wtf,        /* Slot 6 is replaced by the timer */
+               via_wtf
+       }
+};
+
+#define MAC_CLOCK_TICK         (783300/HZ)     /* ticks per HZ */
+#define MAC_CLOCK_LOW          (MAC_CLOCK_TICK&0xFF)
+#define MAC_CLOCK_HIGH         (MAC_CLOCK_TICK>>8)
+
+
+void via_init_clock(void (*func)(int, void *, struct pt_regs *))
+{
+       unsigned char c;
+
+       mac_debugging_penguin(6);
+
+       /*
+        *      Shut it down
+        */
+
+       via_write(via1,vIER, 0x7F);
+       via_write(via2,vIER, 0x7F);
+
+       /*
+        *      Kill the timers
+        */
+
+       via_write(via1,vT1LL,0);
+       via_write(via1,vT1LH,0);
+       via_write(via1,vT1CL,0);
+       via_write(via1,vT1CH,0);
+       via_write(via1,vT2CL,0);
+       via_write(via1,vT2CH,0);
+
+       /*
+        *      Now do via2
+        */
+
+       via_write(via2,vT1LL,0);
+       via_write(via2,vT1LH,0);
+       via_write(via2,vT1CL,0);
+       via_write(via2,vT1CH,0);
+       via_write(via2,vT2CL,0);
+       via_write(via2,vT2CH,0);
+
+       /*
+        *      Disable the timer latches
+        */
+
+       c=via_read(via1,vACR);
+       via_write(via1,vACR,c&0x3F);
+
+       c=via_read(via2,vACR);
+       via_write(via2,vACR,c&0x3F);
+
+       /*
+        *      Now start the clock - we want 100Hz
+        */
+
+       via_write(via1,vACR,via_read(via1,vACR)|0x40);
+
+       via_write(via1,vT1LL, MAC_CLOCK_LOW);
+       via_write(via1,vT1LH, MAC_CLOCK_HIGH);
+       via_write(via1,vT1CL, MAC_CLOCK_LOW);
+       via_write(via1,vT1CH, MAC_CLOCK_HIGH);
+
+       /*
+        *      And enable its interrupt
+        */
+
+       via_write(via1, vIER, 0x80|(1<<6));
+
+       via1_func_tab.vector[6]=func;
+       mac_debugging_penguin(7);
+}
+
+
+static void via_irq(unsigned char *via, struct via_irq_tab *irqtab,
+       struct pt_regs *regs)
+{
+       unsigned char events=(via_read(via, vIFR)&via_read(via,vIER))&0x7F;
+       int i;
+
+       /*
+        *      Shouldnt happen
+        */
+
+       if(events==0)
+       {
+               printk("via_irq: nothing pending!\n");
+               return;
+       }
+
+       /*
+        *      Clear the pending flag
+        */
+
+       via_write(via, vIFR, events);
+
+       /*
+        *      Now see what bits are raised
+        */
+
+       for(i=0;i<7;i++)
+       {
+               if(events&(1<<i))
+                       (irqtab->vector[i])(i, via, regs);
+       }
+
+       /*
+        *      And done..
+        */
+}
+
+/*
+ *     System interrupts
+ */
+
+void via1_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+       via_irq(via1, &via1_func_tab, regs);
+}
+
+/*
+ *     Nubus interrupts
+ */
+
+void via2_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+#if 0
+       via_irq(via1, &nubus_func_tab, regs);
+#endif
+}
+
+/*
+ *     Unexpected via interrupt
+ */
+
+void via_wtf(int slot, void *via, struct pt_regs *regs)
+{
+       printk("Unexpected event %d on via %p\n",slot,via);
+}
+
+/*
+ *     The power switch - yes its software!
+ */
+
+void mac_reset(void)
+{
+       /* Direction of vDirB is output */
+       via_write(via2,vDirB,via_read(via2,vDirB)|0x04);
+       /* Send a value of 0 on that line */
+       via_write(via2,vBufB,via_read(via2,vBufB)&~0x04);
+       /* We never make it this far... */
+       /* XXX - delay do we need to spin here ? */
+}
+
+/*
+ *     Set up the keyboard
+ */
+
+void via_setup_keyboard(void)
+{
+       via1_func_tab.vector[2]=adb_interrupt;
+}
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/arch/m68k/mac/via6522.h linux-2.0.29/arch/m68k/mac/via6522.h
--- linux.vanilla/arch/m68k/mac/via6522.h       Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/arch/m68k/mac/via6522.h        Tue Apr 15 15:44:11 1997
@@ -0,0 +1,96 @@
+/*
+ *     6522 Versatile Interface Adapter (VIA)
+ *
+ *     There are two of these on the Mac II. Some IRQ's are vectored
+ *     via them as are assorted bits and bobs - eg rtc, adb. The picture
+ *     is a bit incomplete as the Mac documentation doesnt cover this well
+ */
+
+#define VIABASE                0x50F00000
+#define VIABASE2       0x50F02000
+
+/*
+ *     Not all of these are true post MacII I think
+ */
+
+#define VIA1A_vSccWrReq        0x80    /* SCC write */
+#define VIA1A_vRev8    0x40    /* Revision 8 board ??? */
+#define VIA1A_vHeadSel 0x20    /* Head select for IWM */
+#define VIA1A_vOverlay 0x10
+#define VIA1A_vSync    0x08
+#define VIA1A_vVolume  0x07    /* Audio volume mask */
+
+#define VIA1B_vSound   0x80    /* Audio on/off */
+#define VIA1B_vMystery 0x40
+#define VIA1B_vADBS2   0x20    /* ADB state 2 */
+#define VIA1B_vADBS1   0x10    /* ADB state 1 */
+#define VIA1B_vADBInt  0x08    /* ADB interrupt */
+#define VIA1B_vRTCEnb  0x04    /* Real time clock */
+#define VIA1B_vRTCClk  0x02
+#define VIA1B_vRTCData 0x01
+
+/*
+ *     VIA2 A register is the interrupt lines raised off the nubus
+ *     slots.
+ */
+
+#define VIA2A_vIRQE    0x20
+#define VIA2A_vIRQD    0x10
+#define VIA2A_vIRQC    0x08
+#define VIA2A_vIRQB    0x04
+#define VIA2A_vIRQA    0x02
+#define VIA2A_vIRQ9    0x01
+
+/*
+ *     Register B has the fun stuff in it
+ */
+
+#define VIA2B_vPower   0x04    /* Off switch */
+#define VIA2B_vBusLk   0x02
+#define VIA2B_vCDis    0x01
+
+extern __inline__ void via_write(volatile unsigned char *via,int reg, int v)
+{
+       via[reg]=v;
+}
+
+extern __inline__ int via_read(volatile unsigned char *via,int reg)
+{
+       return (int)via[reg];
+}
+
+extern volatile unsigned char *via1,*via2;
+
+/*
+ *     6522 registers - see databook
+ */
+
+#define vBufB  0x0000
+#define vBufA  0x0200
+#define vDirB  0x0400
+#define vDirA  0x0600
+#define vT1CL  0x0800
+#define vT1CH  0x0a00
+#define vT1LL  0x0c00
+#define vT1LH  0x0e00
+#define vT2CL  0x1000
+#define vT2CH  0x1200
+#define vSR    0x1400
+#define vACR   0x1600
+#define vPCR   0x1800
+#define vIFR   0x1a00
+#define vIER   0x1c00
+#define vANH   0x1e00  /* register A (no shake) */
+
+/*
+ *     VIA interrupt
+ */
+
+struct via_irq_tab
+{
+       void (*vector[8])(int, void *, struct pt_regs *);
+};
+
+extern void via1_irq(int, void *, struct pt_regs *);
+extern void via2_irq(int, void *, struct pt_regs *);
+
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/arch/m68k/mm/memory.c linux-2.0.29/arch/m68k/mm/memory.c
--- linux.vanilla/arch/m68k/mm/memory.c Wed Feb 19 14:44:42 1997
+++ linux-2.0.29/arch/m68k/mm/memory.c  Tue Mar 25 17:55:20 1997
@@ -288,8 +288,18 @@
       unsigned long voff = vaddr;
       unsigned long offset = 0;

+       if(boot_info.memory[0].size==0)
+       {
+               mac_boom(2);
+       }
+
       for (i = 0; i < boot_info.num_memory; i++)
       {
+#if 0
+               printk("Want %lx virtual, Try slot %d (%lx,%lx)\n",
+                       vaddr, i, boot_info.memory[i].addr,
+                               boot_info.memory[i].size);
+#endif
               if (voff < offset + boot_info.memory[i].size) {
#ifdef DEBUGPV
                       printk ("VTOP(%lx)=%lx\n", vaddr,
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/drivers/Makefile linux-2.0.29/drivers/Makefile
--- linux.vanilla/drivers/Makefile      Sat Oct  5 14:24:24 1996
+++ linux-2.0.29/drivers/Makefile       Tue Apr 15 16:49:47 1997
@@ -9,7 +9,7 @@

SUB_DIRS     := block char net #streams
MOD_SUB_DIRS := $(SUB_DIRS)
-ALL_SUB_DIRS := $(SUB_DIRS) pci sbus scsi sound cdrom isdn
+ALL_SUB_DIRS := $(SUB_DIRS) pci sbus scsi sound cdrom isdn nubus

ifdef CONFIG_PCI
SUB_DIRS += pci
@@ -17,6 +17,10 @@

ifdef CONFIG_SBUS
SUB_DIRS += sbus
+endif
+
+ifdef CONFIG_MAC
+SUB_DIRS += nubus
endif

# If CONFIG_SCSI is set, the core of scsi support will be added to the kernel,
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/drivers/block/genhd.c linux-2.0.29/drivers/block/genhd.c
--- linux.vanilla/drivers/block/genhd.c Sat Oct  5 14:24:26 1996
+++ linux-2.0.29/drivers/block/genhd.c  Thu Apr 10 12:42:10 1997
@@ -780,6 +780,170 @@
}
#endif /* CONFIG_ATARI_PARTITION */

+#ifdef CONFIG_MAC_PARTITION
+#include <linux/ctype.h>
+
+/*
+ * Code to understand MacOS partition tables.
+ */
+
+#define MAC_PARTITION_MAGIC    0x504d
+
+/* type field value for A/UX or other Unix partitions */
+#define APPLE_AUX_TYPE "Apple_UNIX_SVR2"
+
+struct mac_partition {
+       __u8    signature[2];   /* expected to be MAC_PARTITION_MAGIC */
+       __u8    res1[2];
+       __u8    map_count[4];   /* # blocks in partition map */
+       __u8    start_block[4]; /* absolute starting block # of partition */
+       __u8    block_count[4]; /* number of blocks in partition */
+       char    name[32];       /* partition name */
+       char    type[32];       /* string type description */
+       __u8    data_start[4];  /* rel block # of first data block */
+       __u8    data_count[4];  /* number of data blocks */
+       __u8    status[4];      /* partition status bits */
+       __u8    boot_start[4];
+       __u8    boot_size[4];
+       __u8    boot_load[4];
+       __u8    boot_load2[4];
+       __u8    boot_entry[4];
+       __u8    boot_entry2[4];
+       __u8    boot_cksum[4];
+       char    processor[16];  /* identifies ISA of boot */
+       /* there is more stuff after this that we don't need */
+};
+
+#define MAC_STATUS_BOOTABLE    8       /* partition is bootable */
+
+#define MAC_DRIVER_MAGIC       0x4552
+
+/* Driver descriptor structure, in block 0 */
+struct mac_driver_desc {
+       __u8    signature[2];   /* expected to be MAC_DRIVER_MAGIC */
+       __u8    block_size[2];
+       __u8    block_count[4];
+    /* ... more stuff */
+};
+
+/* Retrieve a 16-bit bigendian number */
+static inline __u16 get16_be(void *x)
+{
+       __u8 *p = x;
+       return (p[0] << 8) + p[1];
+}
+
+/* Retrieve a 32-bit bigendian number */
+static inline __u32 get32_be(void *x)
+{
+       __u8 *p = x;
+       return (p[0] << 24) + (p[1] << 16) + (p[2] << 8) + p[3];
+}
+
+static int mac_partition(struct gendisk *hd, kdev_t dev, unsigned long fsec)
+{
+       struct buffer_head *bh;
+       int blk, blocks_in_map, cblk, b, i;
+       extern kdev_t boot_dev;
+       unsigned secsize;
+       struct mac_partition *part;
+       struct mac_driver_desc *md;
+       char *cpu = "powerpc";          /* XXX */
+
+       /* XXX At this stage the block-device stuff thinks that
+          the disk has 1024-byte blocks, each of which is 2
+          512-byte disk sectors (for a disk with 512-byte sectors).
+          So then we have to look at two entries in each "block". */
+       /* Get 0th block and look at the first partition map entry. */
+       if ((bh = bread(dev, 0, 1024)) == 0) {
+           printk("%s: error reading partition table\n",
+                  kdevname(dev));
+           return -1;
+       }
+       cblk = 0;
+       md = (struct mac_driver_desc *) bh->b_data;
+       if (get16_be(md->signature) != MAC_DRIVER_MAGIC) {
+               brelse(bh);
+               return 0;
+       }
+       secsize = get16_be(md->block_size);
+       if (secsize < 1024)
+               part = (struct mac_partition *) (bh->b_data + secsize);
+       else {
+               brelse(bh);
+               cblk = secsize / 1024;
+               if ((bh = bread(dev, cblk, 1024)) == 0) {
+                       printk("%s: error reading partition table\n",
+                              kdevname(dev));
+                       return -1;
+               }
+               part = (struct mac_partition *) bh->b_data;
+       }
+       if (get16_be(part->signature) != MAC_PARTITION_MAGIC) {
+               brelse(bh);
+               return 0;               /* not a MacOS disk */
+       }
+       blocks_in_map = get32_be(part->map_count);
+       for (blk = 1; blk <= blocks_in_map; ++blk) {
+               b = (blk * secsize) / 1024;
+               if (b != cblk) {
+                       brelse(bh);
+                       cblk = b;
+                       if ((bh = bread(dev, cblk, 1024)) == 0) {
+                               printk("%s: error reading partition table\n",
+                                      kdevname(dev));
+                               return -1;
+                       }
+               }
+               part = (struct mac_partition *)
+                       (bh->b_data + (blk * secsize) % 1024);
+               if (get16_be(part->signature) != MAC_PARTITION_MAGIC)
+                       break;
+               blocks_in_map = get32_be(part->map_count);
+#if 0
+               printk("mac partition %d(%d) name '%s' type '%s' bim %d\n",
+                      get32_be(part->start_block),
+                      get32_be(part->block_count),
+                      part->name, part->type, blocks_in_map);
+#endif
+#if 0
+               /* for now, ignore non-unix partitions */
+               if (strcmp(part->type, APPLE_AUX_TYPE) != 0)
+                       continue;
+#endif
+               add_partition(hd, current_minor,
+                       fsec + get32_be(part->start_block) * (secsize / 512),
+                       get32_be(part->block_count) * (secsize / 512));
+#if 0
+               /*
+                * XXX check if this is the first bootable partition
+                * on the boot disk, and make it the root if so.
+                * We rely on arch/ppc/kernel/setup.c leaving boot_dev
+                * as 0 if an explicit root was given on the command line.
+                */
+               if (get32_be(part->status) & MAC_STATUS_BOOTABLE
+                   && kdev_t_to_nr(dev) == kdev_t_to_nr(boot_dev)) {
+                       /* i wanna strcasecmp */
+                       for (i = 0; cpu[i] != 0 && part->processor[i] != 0; ++i)
+                               if (tolower(part->processor[i]) != cpu[i])
+                                       break;
+                       if (cpu[i] == 0 && part->processor[i] == 0) {
+                               ROOT_DEV = MKDEV(MAJOR(dev), current_minor);
+                               boot_dev = MKDEV(0, 0);
+                               printk(" (root)");
+                       }
+               }
+#endif
+               ++current_minor;
+       }
+       brelse(bh);
+       printk("\n");
+       return 1;
+}
+
+#endif /* CONFIG_MAC_PARTITION */
+
+
static void check_partition(struct gendisk *hd, kdev_t dev)
{
       static int first_time = 1;
@@ -819,6 +983,10 @@
#endif
#ifdef CONFIG_ATARI_PARTITION
       if(atari_partition(hd, dev, first_sector))
+               return;
+#endif
+#ifdef CONFIG_MAC_PARTITION
+       if (mac_partition(hd, dev, first_sector))
               return;
#endif
       printk(" unknown partition table\n");
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/drivers/char/Makefile linux-2.0.29/drivers/char/Makefile
--- linux.vanilla/drivers/char/Makefile Sat Oct  5 14:24:30 1996
+++ linux-2.0.29/drivers/char/Makefile  Tue Apr 15 11:53:01 1997
@@ -32,7 +32,15 @@
endif

ifeq "$(ARCH)" "m68k"
-L_OBJS += fbmem.o keyboard.o defkeymap.o
+L_OBJS += fbmem.o defkeymap.o
+
+ifeq ($(CONFIG_MAC),y)
+L_OBJS += macserial.o mackeymap.o keyb-mac.o
+M = y
+else
+L_OBJS += keyboard.o
+endif
+
endif

ifeq ($(CONFIG_AMIGA_GSP),y)
Binary files linux.vanilla/drivers/char/conmakehash and linux-2.0.29/drivers/char/conmakehash differ
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/drivers/char/keyb-mac.c linux-2.0.29/drivers/char/keyb-mac.c
--- linux.vanilla/drivers/char/keyb-mac.c       Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/drivers/char/keyb-mac.c        Tue Apr 15 18:54:40 1997
@@ -0,0 +1,990 @@
+/*
+ * drivers/char/keyb-mac.c
+ *
+ * Keyboard driver for Power Macintosh computers.
+ * Extended for M68K Mac's by Alan Cox.
+ *
+ * Adapted from drivers/char/keyboard.c by Paul Mackerras
+ * (see that file for its authors and contributors).
+ *
+ * Copyright (C) 1996 Paul Mackerras.
+ */
+
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/mm.h>
+#include <linux/ptrace.h>
+#include <linux/signal.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/random.h>
+
+#include <asm/bitops.h>
+#include <asm/cuda.h>
+
+#include "kbd_kern.h"
+#include "diacr.h"
+#include "vt_kern.h"
+
+#define KEYB_KEYREG    0       /* register # for key up/down data */
+#define KEYB_LEDREG    2       /* register # for leds on ADB keyboard */
+#define MOUSE_DATAREG  0       /* reg# for movement/button codes from mouse */
+
+#define SIZE(x) (sizeof(x)/sizeof((x)[0]))
+
+#define KBD_REPORT_ERR
+#define KBD_REPORT_UNKN
+
+#ifndef KBD_DEFMODE
+#define KBD_DEFMODE ((1 << VC_REPEAT) | (1 << VC_META))
+#endif
+
+#ifndef KBD_DEFLEDS
+#define KBD_DEFLEDS 0
+#endif
+
+#ifndef KBD_DEFLOCK
+#define KBD_DEFLOCK 0
+#endif
+
+extern void poke_blanked_console(void);
+extern void ctrl_alt_del(void);
+extern void reset_vc(unsigned int new_console);
+extern void scrollback(int);
+extern void scrollfront(int);
+
+static void kbd_repeat(unsigned long);
+static struct timer_list repeat_timer = { NULL, NULL, 0, 0, kbd_repeat };
+static int last_keycode;
+
+/*
+ * global state includes the following, and various static variables
+ * in this module: prev_scancode, shift_state, diacr, npadch, dead_key_next.
+ * (last_console is now a global variable)
+ */
+
+/* shift state counters.. */
+static unsigned char k_down[NR_SHIFT] = {0, };
+/* keyboard key bitmap */
+#define BITS_PER_LONG (8*sizeof(unsigned long))
+static unsigned long key_down[256/BITS_PER_LONG] = { 0, };
+
+static int dead_key_next = 0;
+
+/*
+ * shift_state is global so the mouse driver can get at it.
+ */
+int shift_state = 0;
+static int npadch = -1;                        /* -1 or number assembled on pad */
+static unsigned char diacr = 0;
+static char rep = 0;                   /* flag telling character repeat */
+struct kbd_struct kbd_table[MAX_NR_CONSOLES];
+static struct tty_struct **ttytab;
+static struct kbd_struct * kbd = kbd_table;
+static struct tty_struct * tty = NULL;
+
+extern void compute_shiftstate(void);
+
+typedef void (*k_hand)(unsigned char value, char up_flag);
+typedef void (k_handfn)(unsigned char value, char up_flag);
+
+static k_handfn
+       do_self, do_fn, do_spec, do_pad, do_dead, do_cons, do_cur, do_shift,
+       do_meta, do_ascii, do_lock, do_lowercase, do_slock, do_ignore;
+
+static k_hand key_handler[16] = {
+       do_self, do_fn, do_spec, do_pad, do_dead, do_cons, do_cur, do_shift,
+       do_meta, do_ascii, do_lock, do_lowercase, do_slock,
+       do_ignore, do_ignore, do_ignore
+};
+
+typedef void (*void_fnp)(void);
+typedef void (void_fn)(void);
+
+static void_fn do_null, enter, show_ptregs, send_intr, lastcons, caps_toggle,
+       num, hold, scroll_forw, scroll_back, boot_it, caps_on, compose,
+       SAK, decr_console, incr_console, spawn_console, bare_num;
+
+static void_fnp spec_fn_table[] = {
+       do_null,        enter,          show_ptregs,    show_mem,
+       show_state,     send_intr,      lastcons,       caps_toggle,
+       num,            hold,           scroll_forw,    scroll_back,
+       boot_it,        caps_on,        compose,        SAK,
+       decr_console,   incr_console,   spawn_console,  bare_num
+};
+
+/* maximum values each key_handler can handle */
+const int max_vals[] = {
+       255, SIZE(func_table) - 1, SIZE(spec_fn_table) - 1, NR_PAD - 1,
+       NR_DEAD - 1, 255, 3, NR_SHIFT - 1,
+       255, NR_ASCII - 1, NR_LOCK - 1, 255,
+       NR_LOCK - 1
+};
+
+const int NR_TYPES = SIZE(max_vals);
+
+static void put_queue(int);
+static unsigned char handle_diacr(unsigned char);
+static void keyboard_input(unsigned char *, int, struct pt_regs *);
+static void input_keycode(int, int);
+static void leds_done(struct adb_request *);
+
+/* pt_regs - set by keyboard_interrupt(), used by show_ptregs() */
+static struct pt_regs * pt_regs;
+
+/* this map indicates which keys shouldn't autorepeat. */
+static unsigned char dont_repeat[128] = {
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* esc...option */
+       0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, /* num lock */
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, /* scroll lock */
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+/*
+ * Many other routines do put_queue, but I think either
+ * they produce ASCII, or they produce some user-assigned
+ * string, and in both cases we might assume that it is
+ * in utf-8 already.
+ */
+void to_utf8(ushort c) {
+    if (c < 0x80)
+       put_queue(c);                   /*  0*******  */
+    else if (c < 0x800) {
+       put_queue(0xc0 | (c >> 6));     /*  110***** 10******  */
+       put_queue(0x80 | (c & 0x3f));
+    } else {
+       put_queue(0xe0 | (c >> 12));    /*  1110**** 10****** 10******  */
+       put_queue(0x80 | ((c >> 6) & 0x3f));
+       put_queue(0x80 | (c & 0x3f));
+    }
+    /* UTF-8 is defined for words of up to 31 bits,
+       but we need only 16 bits here */
+}
+
+int setkeycode(unsigned int scancode, unsigned int keycode)
+{
+       return -EINVAL;
+}
+
+int getkeycode(unsigned int scancode)
+{
+       return -EINVAL;
+}
+
+static void keyboard_input(unsigned char *data, int nb, struct pt_regs *regs)
+{
+       /* first check this is from register 0 */
+       if (nb != 5 || (data[2] & 3) != KEYB_KEYREG)
+               return;         /* ignore it */
+
+       pt_regs = regs;
+       do_poke_blanked_console = 1;
+       mark_bh(CONSOLE_BH);
+       mark_bh(KEYBOARD_BH);
+       add_keyboard_randomness(data[3]);
+
+       input_keycode(data[3], 0);
+       if (data[4] != 0xff && data[3] != 0x7f)
+               input_keycode(data[4], 0);
+}
+
+static void
+mouse_input(unsigned char *data, int nb, struct pt_regs *regs)
+{
+       int i;
+
+       if (nb != 5 || (data[2] & 3) != MOUSE_DATAREG) {
+               printk("data from mouse:");
+               for (i = 0; i < nb; ++i)
+                       printk(" %x", data[i]);
+               printk("\n");
+               return;
+       }
+
+       tty = ttytab[fg_console];
+       kbd = kbd_table + fg_console;
+       if (kbd->kbdmode == VC_RAW) {
+               put_queue(0x7e);
+               put_queue(data[3]);
+               put_queue(data[4]);
+       }
+}
+
+static void
+input_keycode(int keycode, int repeat)
+{
+       int up_flag, raw_mode;
+
+       tty = ttytab[fg_console];
+       kbd = kbd_table + fg_console;
+       if ((raw_mode = (kbd->kbdmode == VC_RAW))) {
+               put_queue(keycode);
+               /* we do not return yet, because we want to maintain
+                  the key_down array, so that we have the correct
+                  values when finishing RAW mode or when changing VT's */
+       }
+
+       up_flag = (keycode & 0200);
+        keycode &= 0x7f;
+       del_timer(&repeat_timer);
+
+       /*
+        * Convert R-shift/control/option to L version.
+        */
+       switch (keycode) {
+       case 0x7b: keycode = 0x38; break; /* R-shift */
+       case 0x7c: keycode = 0x3a; break; /* R-option */
+       case 0x7d: keycode = 0x36; break; /* R-control */
+       }
+
+       /*
+        * At this point the variable `keycode' contains the keycode.
+        * We keep track of the up/down status of the key, and
+        * return the keycode if in MEDIUMRAW mode.
+        */
+       if (up_flag) {
+               rep = 0;
+               clear_bit(keycode, key_down);
+       } else {
+               if (!dont_repeat[keycode]) {
+                       last_keycode = keycode;
+                       repeat_timer.expires = jiffies + (repeat? HZ/15: HZ/2);
+                       add_timer(&repeat_timer);
+               }
+               rep = set_bit(keycode, key_down);
+       }
+
+       if (raw_mode)
+               return;
+
+       if (kbd->kbdmode == VC_MEDIUMRAW) {
+               /* soon keycodes will require more than one byte */
+               put_queue(keycode + up_flag);
+               return;
+       }
+
+       /*
+        * Small change in philosophy: earlier we defined repetition by
+        *       rep = keycode == prev_keycode;
+        *       prev_keycode = keycode;
+        * but now by the fact that the depressed key was down already.
+        * Does this ever make a difference? Yes.
+        */
+
+       /*
+        *  Repeat a key only if the input buffers are empty or the
+        *  characters get echoed locally. This makes key repeat usable
+        *  with slow applications and under heavy loads.
+        */
+       if (!rep ||
+           (vc_kbd_mode(kbd,VC_REPEAT) && tty &&
+            (L_ECHO(tty) || (tty->driver.chars_in_buffer(tty) == 0)))) {
+               u_short keysym;
+               u_char type;
+
+               /* the XOR below used to be an OR */
+               int shift_final = shift_state ^ kbd->lockstate ^ kbd->slockstate;
+               ushort *key_map = key_maps[shift_final];
+
+               if (key_map != NULL) {
+                       keysym = key_map[keycode];
+                       type = KTYP(keysym);
+
+                       if (type >= 0xf0) {
+                           type -= 0xf0;
+                           if (type == KT_LETTER) {
+                               type = KT_LATIN;
+                               if (vc_kbd_led(kbd, VC_CAPSLOCK)) {
+                                   key_map = key_maps[shift_final ^ (1<<KG_SHIFT)];
+                                   if (key_map)
+                                     keysym = key_map[keycode];
+                               }
+                           }
+                           (*key_handler[type])(keysym & 0xff, up_flag);
+                           if (type != KT_SLOCK)
+                               kbd->slockstate = 0;
+                       } else {
+                           /* maybe only if (kbd->kbdmode == VC_UNICODE) ? */
+                           if (!up_flag)
+                               to_utf8(keysym);
+                       }
+               } else {
+                       /* maybe beep? */
+                       /* we have at least to update shift_state */
+#if 1                  /* how? two almost equivalent choices follow */
+                       compute_shiftstate();
+#else
+                       keysym = U(plain_map[keycode]);
+                       type = KTYP(keysym);
+                       if (type == KT_SHIFT)
+                         (*key_handler[type])(keysym & 0xff, up_flag);
+#endif
+               }
+        }
+}
+
+static void
+kbd_repeat(unsigned long xxx)
+{
+       unsigned long flags;
+
+       save_flags(flags);
+       cli();
+       input_keycode(last_keycode, 1);
+       restore_flags(flags);
+}
+
+static void put_queue(int ch)
+{
+       wake_up(&keypress_wait);
+       if (tty) {
+               tty_insert_flip_char(tty, ch, 0);
+               tty_schedule_flip(tty);
+       }
+}
+
+static void puts_queue(char *cp)
+{
+       wake_up(&keypress_wait);
+       if (!tty)
+               return;
+
+       while (*cp) {
+               tty_insert_flip_char(tty, *cp, 0);
+               cp++;
+       }
+       tty_schedule_flip(tty);
+}
+
+static void applkey(int key, char mode)
+{
+       static char buf[] = { 0x1b, 'O', 0x00, 0x00 };
+
+       buf[1] = (mode ? 'O' : '[');
+       buf[2] = key;
+       puts_queue(buf);
+}
+
+static void enter(void)
+{
+       put_queue(13);
+       if (vc_kbd_mode(kbd,VC_CRLF))
+               put_queue(10);
+}
+
+static void caps_toggle(void)
+{
+       if (rep)
+               return;
+       chg_vc_kbd_led(kbd, VC_CAPSLOCK);
+}
+
+static void caps_on(void)
+{
+       if (rep)
+               return;
+       set_vc_kbd_led(kbd, VC_CAPSLOCK);
+}
+
+static void show_ptregs(void)
+{
+       if (pt_regs)
+               show_regs(pt_regs);
+}
+
+static void hold(void)
+{
+       if (rep || !tty)
+               return;
+
+       /*
+        * Note: SCROLLOCK will be set (cleared) by stop_tty (start_tty);
+        * these routines are also activated by ^S/^Q.
+        * (And SCROLLOCK can also be set by the ioctl KDSKBLED.)
+        */
+       if (tty->stopped)
+               start_tty(tty);
+       else
+               stop_tty(tty);
+}
+
+static void num(void)
+{
+       if (vc_kbd_mode(kbd,VC_APPLIC))
+               applkey('P', 1);
+       else
+               bare_num();
+}
+
+/*
+ * Bind this to Shift-NumLock if you work in application keypad mode
+ * but want to be able to change the NumLock flag.
+ * Bind this to NumLock if you prefer that the NumLock key always
+ * changes the NumLock flag.
+ */
+static void bare_num(void)
+{
+       if (!rep)
+               chg_vc_kbd_led(kbd,VC_NUMLOCK);
+}
+
+static void lastcons(void)
+{
+       /* switch to the last used console, ChN */
+       set_console(last_console);
+}
+
+static void decr_console(void)
+{
+       int i;
+
+       for (i = fg_console-1; i != fg_console; i--) {
+               if (i == -1)
+                       i = MAX_NR_CONSOLES-1;
+               if (vc_cons_allocated(i))
+                       break;
+       }
+       set_console(i);
+}
+
+static void incr_console(void)
+{
+       int i;
+
+       for (i = fg_console+1; i != fg_console; i++) {
+               if (i == MAX_NR_CONSOLES)
+                       i = 0;
+               if (vc_cons_allocated(i))
+                       break;
+       }
+       set_console(i);
+}
+
+static void send_intr(void)
+{
+       if (!tty)
+               return;
+       tty_insert_flip_char(tty, 0, TTY_BREAK);
+       tty_schedule_flip(tty);
+}
+
+static void scroll_forw(void)
+{
+       scrollfront(0);
+}
+
+static void scroll_back(void)
+{
+       scrollback(0);
+}
+
+static void boot_it(void)
+{
+       ctrl_alt_del();
+}
+
+static void compose(void)
+{
+       dead_key_next = 1;
+}
+
+int spawnpid, spawnsig;
+
+static void spawn_console(void)
+{
+        if (spawnpid)
+          if(kill_proc(spawnpid, spawnsig, 1))
+            spawnpid = 0;
+}
+
+static void SAK(void)
+{
+       do_SAK(tty);
+#if 0
+       /*
+        * Need to fix SAK handling to fix up RAW/MEDIUM_RAW and
+        * vt_cons modes before we can enable RAW/MEDIUM_RAW SAK
+        * handling.
+        *
+        * We should do this some day --- the whole point of a secure
+        * attention key is that it should be guaranteed to always
+        * work.
+        */
+       reset_vc(fg_console);
+       do_unblank_screen();    /* not in interrupt routine? */
+#endif
+}
+
+static void do_ignore(unsigned char value, char up_flag)
+{
+}
+
+static void do_null()
+{
+       compute_shiftstate();
+}
+
+static void do_spec(unsigned char value, char up_flag)
+{
+       if (up_flag)
+               return;
+       if (value >= SIZE(spec_fn_table))
+               return;
+       spec_fn_table[value]();
+}
+
+static void do_lowercase(unsigned char value, char up_flag)
+{
+       printk(KERN_ERR "keyboard.c: do_lowercase was called - impossible\n");
+}
+
+static void do_self(unsigned char value, char up_flag)
+{
+       if (up_flag)
+               return;         /* no action, if this is a key release */
+
+       if (diacr)
+               value = handle_diacr(value);
+
+       if (dead_key_next) {
+               dead_key_next = 0;
+               diacr = value;
+               return;
+       }
+
+       put_queue(value);
+}
+
+#define A_GRAVE  '`'
+#define A_ACUTE  '\''
+#define A_CFLEX  '^'
+#define A_TILDE  '~'
+#define A_DIAER  '"'
+static unsigned char ret_diacr[] =
+       {A_GRAVE, A_ACUTE, A_CFLEX, A_TILDE, A_DIAER };
+
+/* If a dead key pressed twice, output a character corresponding to it,        */
+/* otherwise just remember the dead key.                               */
+
+static void do_dead(unsigned char value, char up_flag)
+{
+       if (up_flag)
+               return;
+
+       value = ret_diacr[value];
+       if (diacr == value) {   /* pressed twice */
+               diacr = 0;
+               put_queue(value);
+               return;
+       }
+       diacr = value;
+}
+
+
+/* If space is pressed, return the character corresponding the pending */
+/* dead key, otherwise try to combine the two.                         */
+
+unsigned char handle_diacr(unsigned char ch)
+{
+       int d = diacr;
+       int i;
+
+       diacr = 0;
+       if (ch == ' ')
+               return d;
+
+       for (i = 0; i < accent_table_size; i++) {
+               if (accent_table[i].diacr == d && accent_table[i].base == ch)
+                       return accent_table[i].result;
+       }
+
+       put_queue(d);
+       return ch;
+}
+
+static void do_cons(unsigned char value, char up_flag)
+{
+       if (up_flag)
+               return;
+       set_console(value);
+}
+
+static void do_fn(unsigned char value, char up_flag)
+{
+       if (up_flag)
+               return;
+       if (value < SIZE(func_table)) {
+               if (func_table[value])
+                       puts_queue(func_table[value]);
+       } else
+               printk(KERN_ERR "do_fn called with value=%d\n", value);
+}
+
+static void do_pad(unsigned char value, char up_flag)
+{
+       static const char *pad_chars = "0123456789+-*/\015,.?";
+       static const char *app_map = "pqrstuvwxylSRQMnn?";
+
+       if (up_flag)
+               return;         /* no action, if this is a key release */
+
+       /* kludge... shift forces cursor/number keys */
+       if (vc_kbd_mode(kbd,VC_APPLIC) && !k_down[KG_SHIFT]) {
+               applkey(app_map[value], 1);
+               return;
+       }
+
+       if (!vc_kbd_led(kbd,VC_NUMLOCK))
+               switch (value) {
+                       case KVAL(K_PCOMMA):
+                       case KVAL(K_PDOT):
+                               do_fn(KVAL(K_REMOVE), 0);
+                               return;
+                       case KVAL(K_P0):
+                               do_fn(KVAL(K_INSERT), 0);
+                               return;
+                       case KVAL(K_P1):
+                               do_fn(KVAL(K_SELECT), 0);
+                               return;
+                       case KVAL(K_P2):
+                               do_cur(KVAL(K_DOWN), 0);
+                               return;
+                       case KVAL(K_P3):
+                               do_fn(KVAL(K_PGDN), 0);
+                               return;
+                       case KVAL(K_P4):
+                               do_cur(KVAL(K_LEFT), 0);
+                               return;
+                       case KVAL(K_P6):
+                               do_cur(KVAL(K_RIGHT), 0);
+                               return;
+                       case KVAL(K_P7):
+                               do_fn(KVAL(K_FIND), 0);
+                               return;
+                       case KVAL(K_P8):
+                               do_cur(KVAL(K_UP), 0);
+                               return;
+                       case KVAL(K_P9):
+                               do_fn(KVAL(K_PGUP), 0);
+                               return;
+                       case KVAL(K_P5):
+                               applkey('G', vc_kbd_mode(kbd, VC_APPLIC));
+                               return;
+               }
+
+       put_queue(pad_chars[value]);
+       if (value == KVAL(K_PENTER) && vc_kbd_mode(kbd, VC_CRLF))
+               put_queue(10);
+}
+
+static void do_cur(unsigned char value, char up_flag)
+{
+       static const char *cur_chars = "BDCA";
+       if (up_flag)
+               return;
+
+       applkey(cur_chars[value], vc_kbd_mode(kbd,VC_CKMODE));
+}
+
+static void do_shift(unsigned char value, char up_flag)
+{
+       int old_state = shift_state;
+
+       if (rep)
+               return;
+
+       /* Mimic typewriter:
+          a CapsShift key acts like Shift but undoes CapsLock */
+       if (value == KVAL(K_CAPSSHIFT)) {
+               value = KVAL(K_SHIFT);
+               if (!up_flag)
+                       clr_vc_kbd_led(kbd, VC_CAPSLOCK);
+       }
+
+       if (up_flag) {
+               /* handle the case that two shift or control
+                  keys are depressed simultaneously */
+               if (k_down[value])
+                       k_down[value]--;
+       } else
+               k_down[value]++;
+
+       if (k_down[value])
+               shift_state |= (1 << value);
+       else
+               shift_state &= ~ (1 << value);
+
+       /* kludge */
+       if (up_flag && shift_state != old_state && npadch != -1) {
+               if (kbd->kbdmode == VC_UNICODE)
+                 to_utf8(npadch & 0xffff);
+               else
+                 put_queue(npadch & 0xff);
+               npadch = -1;
+       }
+}
+
+/* called after returning from RAW mode or when changing consoles -
+   recompute k_down[] and shift_state from key_down[] */
+/* maybe called when keymap is undefined, so that shiftkey release is seen */
+void compute_shiftstate(void)
+{
+       int i, j, k, sym, val;
+
+       shift_state = 0;
+       for (i = 0; i < SIZE(k_down); i++)
+           k_down[i] = 0;
+
+       for (i = 0; i < SIZE(key_down); i++)
+           if (key_down[i]) {  /* skip this word if not a single bit on */
+               k = i*BITS_PER_LONG;
+               for (j = 0; j<BITS_PER_LONG; j++, k++)
+                   if (test_bit(k, key_down)) {
+                       sym = U(plain_map[k]);
+                       if (KTYP(sym) == KT_SHIFT) {
+                           val = KVAL(sym);
+                           if (val == KVAL(K_CAPSSHIFT))
+                               val = KVAL(K_SHIFT);
+                           k_down[val]++;
+                           shift_state |= (1<<val);
+                       }
+                   }
+           }
+}
+
+static void do_meta(unsigned char value, char up_flag)
+{
+       if (up_flag)
+               return;
+
+       if (vc_kbd_mode(kbd, VC_META)) {
+               put_queue('\033');
+               put_queue(value);
+       } else
+               put_queue(value | 0x80);
+}
+
+static void do_ascii(unsigned char value, char up_flag)
+{
+       int base;
+
+       if (up_flag)
+               return;
+
+       if (value < 10)    /* decimal input of code, while Alt depressed */
+           base = 10;
+       else {       /* hexadecimal input of code, while AltGr depressed */
+           value -= 10;
+           base = 16;
+       }
+
+       if (npadch == -1)
+         npadch = value;
+       else
+         npadch = npadch * base + value;
+}
+
+static void do_lock(unsigned char value, char up_flag)
+{
+       if (up_flag || rep)
+               return;
+       chg_vc_kbd_lock(kbd, value);
+}
+
+static void do_slock(unsigned char value, char up_flag)
+{
+       if (up_flag || rep)
+               return;
+       chg_vc_kbd_slock(kbd, value);
+}
+
+/*
+ * The leds display either (i) the status of NumLock, CapsLock, ScrollLock,
+ * or (ii) whatever pattern of lights people want to show using KDSETLED,
+ * or (iii) specified bits of specified words in kernel memory.
+ */
+
+static unsigned char ledstate = 0xff; /* undefined */
+static unsigned char ledioctl;
+
+unsigned char getledstate(void) {
+    return ledstate;
+}
+
+void setledstate(struct kbd_struct *kbd, unsigned int led) {
+    if (!(led & ~7)) {
+       ledioctl = led;
+       kbd->ledmode = LED_SHOW_IOCTL;
+    } else
+       kbd->ledmode = LED_SHOW_FLAGS;
+    set_leds();
+}
+
+static struct ledptr {
+    unsigned int *addr;
+    unsigned int mask;
+    unsigned char valid:1;
+} ledptrs[3];
+
+void register_leds(int console, unsigned int led,
+                  unsigned int *addr, unsigned int mask) {
+    struct kbd_struct *kbd = kbd_table + console;
+    if (led < 3) {
+       ledptrs[led].addr = addr;
+       ledptrs[led].mask = mask;
+       ledptrs[led].valid = 1;
+       kbd->ledmode = LED_SHOW_MEM;
+    } else
+       kbd->ledmode = LED_SHOW_FLAGS;
+}
+
+/* Map led flags as defined in kbd_kern.h to bits for Apple keyboard. */
+static unsigned char mac_ledmap[8] = {
+    0,         /* none */
+    4,         /* scroll lock */
+    1,         /* num lock */
+    5,         /* scroll + num lock */
+    2,         /* caps lock */
+    6,         /* caps + scroll lock */
+    3,         /* caps + num lock */
+    7,         /* caps + num + scroll lock */
+};
+
+static inline unsigned char getleds(void){
+    struct kbd_struct *kbd = kbd_table + fg_console;
+    unsigned char leds;
+
+    if (kbd->ledmode == LED_SHOW_IOCTL)
+      return ledioctl;
+    leds = mac_ledmap[kbd->ledflagstate];
+    if (kbd->ledmode == LED_SHOW_MEM) {
+       if (ledptrs[0].valid) {
+           if (*ledptrs[0].addr & ledptrs[0].mask)
+             leds |= 1;
+           else
+             leds &= ~1;
+       }
+       if (ledptrs[1].valid) {
+           if (*ledptrs[1].addr & ledptrs[1].mask)
+             leds |= 2;
+           else
+             leds &= ~2;
+       }
+       if (ledptrs[2].valid) {
+           if (*ledptrs[2].addr & ledptrs[2].mask)
+             leds |= 4;
+           else
+             leds &= ~4;
+       }
+    }
+    return leds;
+}
+
+/*
+ * This routine is the bottom half of the keyboard interrupt
+ * routine, and runs with all interrupts enabled. It does
+ * console changing, led setting and copy_to_cooked, which can
+ * take a reasonably long time.
+ *
+ * Aside from timing (which isn't really that important for
+ * keyboard interrupts as they happen often), using the software
+ * interrupt routines for this thing allows us to easily mask
+ * this when we don't want any of the above to happen. Not yet
+ * used, but this allows for easy and efficient race-condition
+ * prevention later on.
+ */
+static struct adb_request led_request;
+
+static void kbd_bh(void)
+{
+       unsigned char leds = getleds();
+
+       if (leds != ledstate && led_request.got_reply) {
+               ledstate = leds;
+               adb_request(&led_request, leds_done, 4, ADB_PACKET,
+                            ADB_WRITEREG(ADB_KEYBOARD, KEYB_LEDREG),
+                            ~leds >> 8, ~leds);
+       }
+}
+
+static void leds_done(struct adb_request *req)
+{
+       mark_bh(KEYBOARD_BH);
+}
+
+int kbd_init(void)
+{
+       int i;
+       struct kbd_struct kbd0;
+       extern struct tty_driver console_driver;
+       struct adb_request req;
+
+       adb_bus_init();
+
+       kbd0.ledflagstate = kbd0.default_ledflagstate = KBD_DEFLEDS;
+       kbd0.ledmode = LED_SHOW_FLAGS;
+       kbd0.lockstate = KBD_DEFLOCK;
+       kbd0.slockstate = 0;
+       kbd0.modeflags = KBD_DEFMODE;
+       kbd0.kbdmode = VC_XLATE;
+
+       for (i = 0 ; i < MAX_NR_CONSOLES ; i++)
+               kbd_table[i] = kbd0;
+
+       ttytab = console_driver.table;
+
+       init_bh(KEYBOARD_BH, kbd_bh);
+       mark_bh(KEYBOARD_BH);
+
+       adb_register(ADB_KEYBOARD, keyboard_input);
+       adb_register(ADB_MOUSE, mouse_input);
+
+       /* turn on ADB auto-polling in the CUDA */
+
+       /*
+        *      Older boxes don't support CUDA_* targets and CUDA commands
+        *      instead we emulate them in the adb_request hook to make
+        *      the code interfaces saner.
+        *
+        *      Note XXX: the Linux PMac and this code both assume the
+        *      devices are at their primary ids and do not do device
+        *      assignment. This isn't ideal. We should fix it to follow
+        *      the reassignment specs.
+        */
+
+#if 0
+       adb_request(&req, NULL, 3, CUDA_PACKET, CUDA_AUTOPOLL, 1);
+       while (!req.got_reply)
+           adb_poll();
+#endif
+       printk("Configuring keyboard\n");
+       /* turn off all leds */
+       adb_request(&req, NULL, 4, ADB_PACKET,
+                    ADB_WRITEREG(ADB_KEYBOARD, KEYB_LEDREG), 0xff, 0xff);
+       printk("SKIP\n");
+       return 0;
+       printk("Wait keyboard reply\n");
+       while (!req.got_reply)
+           adb_poll();
+
+       printk("Configuring coding mode\n");
+       /* get the keyboard to send separate codes for
+          left and right shift, control, option keys. */
+       adb_request(&req, NULL, 4, ADB_PACKET,
+                    ADB_WRITEREG(ADB_KEYBOARD, 3), 0, 3);
+       while (!req.got_reply)
+           adb_poll();
+
+       led_request.got_reply = 1;
+
+       printk("Keyboard init done\n");
+
+       return 0;
+}
+
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/drivers/char/mackeymap.c linux-2.0.29/drivers/char/mackeymap.c
--- linux.vanilla/drivers/char/mackeymap.c      Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/drivers/char/mackeymap.c       Thu Apr 10 12:43:54 1997
@@ -0,0 +1,262 @@
+/* Do not edit this file! It was automatically generated by   */
+/*    loadkeys --mktable defkeymap.map > defkeymap.c          */
+
+#include <linux/types.h>
+#include <linux/keyboard.h>
+#include <linux/kd.h>
+
+u_short plain_map[NR_KEYS] = {
+       0xfb61, 0xfb73, 0xfb64, 0xfb66, 0xfb68, 0xfb67, 0xfb7a, 0xfb78,
+       0xfb63, 0xfb76, 0xf200, 0xfb62, 0xfb71, 0xfb77, 0xfb65, 0xfb72,
+       0xfb79, 0xfb74, 0xf031, 0xf032, 0xf033, 0xf034, 0xf036, 0xf035,
+       0xf03d, 0xf039, 0xf037, 0xf02d, 0xf038, 0xf030, 0xf05d, 0xfb6f,
+       0xfb75, 0xf05b, 0xfb69, 0xfb70, 0xf201, 0xfb6c, 0xfb6a, 0xf027,
+       0xfb6b, 0xf03b, 0xf05c, 0xf02c, 0xf02f, 0xfb6e, 0xfb6d, 0xf02e,
+       0xf009, 0xf020, 0xf060, 0xf07f, 0xf200, 0xf01b, 0xf702, 0xf703,
+       0xf700, 0xf207, 0xf701, 0xf601, 0xf602, 0xf600, 0xf603, 0xf200,
+       0xf200, 0xf310, 0xf200, 0xf30c, 0xf200, 0xf30a, 0xf200, 0xf208,
+       0xf200, 0xf200, 0xf200, 0xf30d, 0xf30e, 0xf200, 0xf30b, 0xf200,
+       0xf200, 0xf200, 0xf300, 0xf301, 0xf302, 0xf303, 0xf304, 0xf305,
+       0xf306, 0xf307, 0xf200, 0xf308, 0xf309, 0xf200, 0xf200, 0xf200,
+       0xf104, 0xf105, 0xf106, 0xf102, 0xf107, 0xf108, 0xf200, 0xf10a,
+       0xf200, 0xf10c, 0xf200, 0xf209, 0xf200, 0xf109, 0xf200, 0xf10b,
+       0xf200, 0xf11d, 0xf115, 0xf114, 0xf118, 0xf116, 0xf103, 0xf117,
+       0xf101, 0xf119, 0xf100, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+};
+
+u_short shift_map[NR_KEYS] = {
+       0xfb41, 0xfb53, 0xfb44, 0xfb46, 0xfb48, 0xfb47, 0xfb5a, 0xfb58,
+       0xfb43, 0xfb56, 0xf200, 0xfb42, 0xfb51, 0xfb57, 0xfb45, 0xfb52,
+       0xfb59, 0xfb54, 0xf021, 0xf040, 0xf023, 0xf024, 0xf05e, 0xf025,
+       0xf02b, 0xf028, 0xf026, 0xf05f, 0xf02a, 0xf029, 0xf07d, 0xfb4f,
+       0xfb55, 0xf07b, 0xfb49, 0xfb50, 0xf201, 0xfb4c, 0xfb4a, 0xf022,
+       0xfb4b, 0xf03a, 0xf07c, 0xf03c, 0xf03f, 0xfb4e, 0xfb4d, 0xf03e,
+       0xf009, 0xf020, 0xf07e, 0xf07f, 0xf200, 0xf01b, 0xf702, 0xf703,
+       0xf700, 0xf207, 0xf701, 0xf601, 0xf602, 0xf600, 0xf603, 0xf200,
+       0xf200, 0xf310, 0xf200, 0xf30c, 0xf200, 0xf30a, 0xf200, 0xf208,
+       0xf200, 0xf200, 0xf200, 0xf30d, 0xf30e, 0xf200, 0xf30b, 0xf200,
+       0xf200, 0xf200, 0xf300, 0xf301, 0xf302, 0xf303, 0xf304, 0xf305,
+       0xf306, 0xf307, 0xf200, 0xf308, 0xf309, 0xf200, 0xf200, 0xf200,
+       0xf10e, 0xf10f, 0xf110, 0xf10c, 0xf111, 0xf112, 0xf200, 0xf10a,
+       0xf200, 0xf10c, 0xf200, 0xf203, 0xf200, 0xf113, 0xf200, 0xf10b,
+       0xf200, 0xf11d, 0xf115, 0xf114, 0xf20b, 0xf116, 0xf10d, 0xf117,
+       0xf10b, 0xf20a, 0xf10a, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+};
+
+u_short altgr_map[NR_KEYS] = {
+       0xf914, 0xfb73, 0xf917, 0xf919, 0xfb68, 0xfb67, 0xfb7a, 0xfb78,
+       0xf916, 0xfb76, 0xf200, 0xf915, 0xfb71, 0xfb77, 0xf918, 0xfb72,
+       0xfb79, 0xfb74, 0xf200, 0xf040, 0xf200, 0xf024, 0xf200, 0xf200,
+       0xf200, 0xf05d, 0xf07b, 0xf05c, 0xf05b, 0xf07d, 0xf07e, 0xfb6f,
+       0xfb75, 0xf200, 0xfb69, 0xfb70, 0xf201, 0xfb6c, 0xfb6a, 0xf200,
+       0xfb6b, 0xf200, 0xf200, 0xf200, 0xf200, 0xfb6e, 0xfb6d, 0xf200,
+       0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf702, 0xf703,
+       0xf700, 0xf207, 0xf701, 0xf601, 0xf602, 0xf600, 0xf603, 0xf200,
+       0xf200, 0xf310, 0xf200, 0xf30c, 0xf200, 0xf30a, 0xf200, 0xf208,
+       0xf200, 0xf200, 0xf200, 0xf30d, 0xf30e, 0xf200, 0xf30b, 0xf200,
+       0xf200, 0xf200, 0xf90a, 0xf90b, 0xf90c, 0xf90d, 0xf90e, 0xf90f,
+       0xf910, 0xf911, 0xf200, 0xf912, 0xf913, 0xf200, 0xf200, 0xf200,
+       0xf510, 0xf511, 0xf512, 0xf50e, 0xf513, 0xf514, 0xf200, 0xf516,
+       0xf200, 0xf10c, 0xf200, 0xf202, 0xf200, 0xf515, 0xf200, 0xf517,
+       0xf200, 0xf11d, 0xf115, 0xf114, 0xf118, 0xf116, 0xf50f, 0xf117,
+       0xf50d, 0xf119, 0xf50c, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+};
+
+u_short ctrl_map[NR_KEYS] = {
+       0xf001, 0xf013, 0xf004, 0xf006, 0xf008, 0xf007, 0xf01a, 0xf018,
+       0xf003, 0xf016, 0xf200, 0xf002, 0xf011, 0xf017, 0xf005, 0xf012,
+       0xf019, 0xf014, 0xf200, 0xf000, 0xf01b, 0xf01c, 0xf01e, 0xf01d,
+       0xf200, 0xf200, 0xf01f, 0xf01f, 0xf07f, 0xf200, 0xf01d, 0xf00f,
+       0xf015, 0xf01b, 0xf009, 0xf010, 0xf201, 0xf00c, 0xf00a, 0xf007,
+       0xf00b, 0xf200, 0xf01c, 0xf200, 0xf07f, 0xf00e, 0xf00d, 0xf20e,
+       0xf200, 0xf000, 0xf000, 0xf008, 0xf200, 0xf200, 0xf702, 0xf703,
+       0xf700, 0xf207, 0xf701, 0xf601, 0xf602, 0xf600, 0xf603, 0xf200,
+       0xf200, 0xf310, 0xf200, 0xf30c, 0xf200, 0xf30a, 0xf200, 0xf208,
+       0xf200, 0xf200, 0xf200, 0xf30d, 0xf30e, 0xf200, 0xf30b, 0xf200,
+       0xf200, 0xf200, 0xf300, 0xf301, 0xf302, 0xf303, 0xf304, 0xf305,
+       0xf306, 0xf307, 0xf200, 0xf308, 0xf309, 0xf200, 0xf200, 0xf200,
+       0xf104, 0xf105, 0xf106, 0xf102, 0xf107, 0xf108, 0xf200, 0xf10a,
+       0xf200, 0xf10c, 0xf200, 0xf204, 0xf200, 0xf109, 0xf200, 0xf10b,
+       0xf200, 0xf11d, 0xf115, 0xf114, 0xf118, 0xf116, 0xf103, 0xf117,
+       0xf101, 0xf119, 0xf100, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+};
+
+u_short shift_ctrl_map[NR_KEYS] = {
+       0xf001, 0xf013, 0xf004, 0xf006, 0xf008, 0xf007, 0xf01a, 0xf018,
+       0xf003, 0xf016, 0xf200, 0xf002, 0xf011, 0xf017, 0xf005, 0xf012,
+       0xf019, 0xf014, 0xf200, 0xf000, 0xf200, 0xf200, 0xf200, 0xf200,
+       0xf200, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf200, 0xf200, 0xf00f,
+       0xf015, 0xf200, 0xf009, 0xf010, 0xf201, 0xf00c, 0xf00a, 0xf200,
+       0xf00b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf00e, 0xf00d, 0xf200,
+       0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf702, 0xf703,
+       0xf700, 0xf207, 0xf701, 0xf601, 0xf602, 0xf600, 0xf603, 0xf200,
+       0xf200, 0xf310, 0xf200, 0xf30c, 0xf200, 0xf30a, 0xf200, 0xf208,
+       0xf200, 0xf200, 0xf200, 0xf30d, 0xf30e, 0xf200, 0xf30b, 0xf200,
+       0xf200, 0xf200, 0xf300, 0xf301, 0xf302, 0xf303, 0xf304, 0xf305,
+       0xf306, 0xf307, 0xf200, 0xf308, 0xf309, 0xf200, 0xf200, 0xf200,
+       0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+       0xf200, 0xf10c, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+       0xf200, 0xf11d, 0xf115, 0xf114, 0xf118, 0xf116, 0xf200, 0xf117,
+       0xf200, 0xf119, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf20c,
+};
+
+u_short alt_map[NR_KEYS] = {
+       0xf861, 0xf873, 0xf864, 0xf866, 0xf868, 0xf867, 0xf87a, 0xf878,
+       0xf863, 0xf876, 0xf200, 0xf862, 0xf871, 0xf877, 0xf865, 0xf872,
+       0xf879, 0xf874, 0xf831, 0xf832, 0xf833, 0xf834, 0xf836, 0xf835,
+       0xf83d, 0xf839, 0xf837, 0xf82d, 0xf838, 0xf830, 0xf85d, 0xf86f,
+       0xf875, 0xf85b, 0xf869, 0xf870, 0xf80d, 0xf86c, 0xf86a, 0xf827,
+       0xf86b, 0xf83b, 0xf85c, 0xf82c, 0xf82f, 0xf86e, 0xf86d, 0xf82e,
+       0xf809, 0xf820, 0xf860, 0xf87f, 0xf200, 0xf81b, 0xf702, 0xf703,
+       0xf700, 0xf207, 0xf701, 0xf210, 0xf211, 0xf600, 0xf603, 0xf200,
+       0xf200, 0xf310, 0xf200, 0xf30c, 0xf200, 0xf30a, 0xf200, 0xf208,
+       0xf200, 0xf200, 0xf200, 0xf30d, 0xf30e, 0xf200, 0xf30b, 0xf200,
+       0xf200, 0xf200, 0xf900, 0xf901, 0xf902, 0xf903, 0xf904, 0xf905,
+       0xf906, 0xf907, 0xf200, 0xf908, 0xf909, 0xf200, 0xf200, 0xf200,
+       0xf504, 0xf505, 0xf506, 0xf502, 0xf507, 0xf508, 0xf200, 0xf50a,
+       0xf200, 0xf10c, 0xf200, 0xf209, 0xf200, 0xf509, 0xf200, 0xf50b,
+       0xf200, 0xf11d, 0xf115, 0xf114, 0xf118, 0xf116, 0xf503, 0xf117,
+       0xf501, 0xf119, 0xf500, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+};
+
+u_short ctrl_alt_map[NR_KEYS] = {
+       0xf801, 0xf813, 0xf804, 0xf806, 0xf808, 0xf807, 0xf81a, 0xf818,
+       0xf803, 0xf816, 0xf200, 0xf802, 0xf811, 0xf817, 0xf805, 0xf812,
+       0xf819, 0xf814, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+       0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf80f,
+       0xf815, 0xf200, 0xf809, 0xf810, 0xf201, 0xf80c, 0xf80a, 0xf200,
+       0xf80b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf80e, 0xf80d, 0xf200,
+       0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf702, 0xf703,
+       0xf700, 0xf207, 0xf701, 0xf601, 0xf602, 0xf600, 0xf603, 0xf200,
+       0xf200, 0xf310, 0xf200, 0xf30c, 0xf200, 0xf30a, 0xf200, 0xf208,
+       0xf200, 0xf200, 0xf200, 0xf30d, 0xf30e, 0xf200, 0xf30b, 0xf200,
+       0xf200, 0xf200, 0xf300, 0xf301, 0xf302, 0xf303, 0xf304, 0xf305,
+       0xf306, 0xf307, 0xf200, 0xf308, 0xf309, 0xf200, 0xf200, 0xf200,
+       0xf504, 0xf505, 0xf506, 0xf502, 0xf507, 0xf508, 0xf200, 0xf50a,
+       0xf200, 0xf10c, 0xf200, 0xf200, 0xf200, 0xf509, 0xf200, 0xf50b,
+       0xf200, 0xf11d, 0xf115, 0xf114, 0xf118, 0xf116, 0xf503, 0xf117,
+       0xf501, 0xf119, 0xf500, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+};
+
+ushort *key_maps[MAX_NR_KEYMAPS] = {
+       plain_map, shift_map, altgr_map, 0,
+       ctrl_map, shift_ctrl_map, 0, 0,
+       alt_map, 0, 0, 0,
+       ctrl_alt_map,   0
+};
+
+unsigned int keymap_count = 7;
+
+/*
+ * Philosophy: most people do not define more strings, but they who do
+ * often want quite a lot of string space. So, we statically allocate
+ * the default and allocate dynamically in chunks of 512 bytes.
+ */
+
+char func_buf[] = {
+       '\033', '[', '[', 'A', 0,
+       '\033', '[', '[', 'B', 0,
+       '\033', '[', '[', 'C', 0,
+       '\033', '[', '[', 'D', 0,
+       '\033', '[', '[', 'E', 0,
+       '\033', '[', '1', '7', '~', 0,
+       '\033', '[', '1', '8', '~', 0,
+       '\033', '[', '1', '9', '~', 0,
+       '\033', '[', '2', '0', '~', 0,
+       '\033', '[', '2', '1', '~', 0,
+       '\033', '[', '2', '3', '~', 0,
+       '\033', '[', '2', '4', '~', 0,
+       '\033', '[', '2', '5', '~', 0,
+       '\033', '[', '2', '6', '~', 0,
+       '\033', '[', '2', '8', '~', 0,
+       '\033', '[', '2', '9', '~', 0,
+       '\033', '[', '3', '1', '~', 0,
+       '\033', '[', '3', '2', '~', 0,
+       '\033', '[', '3', '3', '~', 0,
+       '\033', '[', '3', '4', '~', 0,
+       '\033', '[', '1', '~', 0,
+       '\033', '[', '2', '~', 0,
+       '\033', '[', '3', '~', 0,
+       '\033', '[', '4', '~', 0,
+       '\033', '[', '5', '~', 0,
+       '\033', '[', '6', '~', 0,
+       '\033', '[', 'M', 0,
+       '\033', '[', 'P', 0,
+};
+
+char *funcbufptr = func_buf;
+int funcbufsize = sizeof(func_buf);
+int funcbufleft = 0;          /* space left */
+
+char *func_table[MAX_NR_FUNC] = {
+       func_buf + 0,
+       func_buf + 5,
+       func_buf + 10,
+       func_buf + 15,
+       func_buf + 20,
+       func_buf + 25,
+       func_buf + 31,
+       func_buf + 37,
+       func_buf + 43,
+       func_buf + 49,
+       func_buf + 55,
+       func_buf + 61,
+       func_buf + 67,
+       func_buf + 73,
+       func_buf + 79,
+       func_buf + 85,
+       func_buf + 91,
+       func_buf + 97,
+       func_buf + 103,
+       func_buf + 109,
+       func_buf + 115,
+       func_buf + 120,
+       func_buf + 125,
+       func_buf + 130,
+       func_buf + 135,
+       func_buf + 140,
+       func_buf + 145,
+       0,
+       0,
+       func_buf + 149,
+       0,
+};
+
+struct kbdiacr accent_table[MAX_DIACR] = {
+       {'`', 'A', '\300'},     {'`', 'a', '\340'},
+       {'\'', 'A', '\301'},    {'\'', 'a', '\341'},
+       {'^', 'A', '\302'},     {'^', 'a', '\342'},
+       {'~', 'A', '\303'},     {'~', 'a', '\343'},
+       {'"', 'A', '\304'},     {'"', 'a', '\344'},
+       {'O', 'A', '\305'},     {'o', 'a', '\345'},
+       {'0', 'A', '\305'},     {'0', 'a', '\345'},
+       {'A', 'A', '\305'},     {'a', 'a', '\345'},
+       {'A', 'E', '\306'},     {'a', 'e', '\346'},
+       {',', 'C', '\307'},     {',', 'c', '\347'},
+       {'`', 'E', '\310'},     {'`', 'e', '\350'},
+       {'\'', 'E', '\311'},    {'\'', 'e', '\351'},
+       {'^', 'E', '\312'},     {'^', 'e', '\352'},
+       {'"', 'E', '\313'},     {'"', 'e', '\353'},
+       {'`', 'I', '\314'},     {'`', 'i', '\354'},
+       {'\'', 'I', '\315'},    {'\'', 'i', '\355'},
+       {'^', 'I', '\316'},     {'^', 'i', '\356'},
+       {'"', 'I', '\317'},     {'"', 'i', '\357'},
+       {'-', 'D', '\320'},     {'-', 'd', '\360'},
+       {'~', 'N', '\321'},     {'~', 'n', '\361'},
+       {'`', 'O', '\322'},     {'`', 'o', '\362'},
+       {'\'', 'O', '\323'},    {'\'', 'o', '\363'},
+       {'^', 'O', '\324'},     {'^', 'o', '\364'},
+       {'~', 'O', '\325'},     {'~', 'o', '\365'},
+       {'"', 'O', '\326'},     {'"', 'o', '\366'},
+       {'/', 'O', '\330'},     {'/', 'o', '\370'},
+       {'`', 'U', '\331'},     {'`', 'u', '\371'},
+       {'\'', 'U', '\332'},    {'\'', 'u', '\372'},
+       {'^', 'U', '\333'},     {'^', 'u', '\373'},
+       {'"', 'U', '\334'},     {'"', 'u', '\374'},
+       {'\'', 'Y', '\335'},    {'\'', 'y', '\375'},
+       {'T', 'H', '\336'},     {'t', 'h', '\376'},
+       {'s', 's', '\337'},     {'"', 'y', '\377'},
+       {'s', 'z', '\337'},     {'i', 'j', '\377'},
+};
+
+unsigned int accent_table_size = 68;
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/drivers/char/mackeymap.map linux-2.0.29/drivers/char/mackeymap.map
--- linux.vanilla/drivers/char/mackeymap.map    Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/drivers/char/mackeymap.map     Thu Apr 10 12:43:55 1997
@@ -0,0 +1,345 @@
+# Kernel keymap for Macintoshes. This uses 7 modifier combinations.
+keymaps 0-2,4-5,8,12
+# We use the Command (pretzel) key as Alt, and the Option key as AltGr.
+#
+keycode 0x00 = a
+       altgr   keycode 0x00 = Hex_A
+keycode 0x01 = s
+keycode 0x02 = d
+       altgr   keycode 0x02 = Hex_D
+keycode 0x03 = f
+       altgr   keycode 0x03 = Hex_F
+keycode 0x04 = h
+keycode 0x05 = g
+keycode 0x06 = z
+keycode 0x07 = x
+keycode 0x08 = c
+       altgr   keycode 0x08 = Hex_C
+keycode 0x09 = v
+keycode 0x0a =
+keycode 0x0b = b
+       altgr   keycode 0x0b = Hex_B
+keycode 0x0c = q
+keycode 0x0d = w
+keycode 0x0e = e
+       altgr   keycode 0x0e = Hex_E
+keycode 0x0f = r
+keycode 0x10 = y
+keycode 0x11 = t
+keycode 0x12 = one              exclam
+       alt     keycode 0x12 = Meta_one
+keycode 0x13 = two              at               at
+       control keycode 0x13 = nul
+       shift   control keycode 0x13 = nul
+       alt     keycode 0x13 = Meta_two
+keycode 0x14 = three            numbersign
+       control keycode 0x14 = Escape
+       alt     keycode 0x14 = Meta_three
+keycode 0x15 = four             dollar           dollar
+       control keycode 0x15 = Control_backslash
+       alt     keycode 0x15 = Meta_four
+keycode 0x16 = six              asciicircum
+       control keycode 0x16 = Control_asciicircum
+       alt     keycode 0x16 = Meta_six
+keycode 0x17 = five             percent
+       control keycode 0x17 = Control_bracketright
+       alt     keycode 0x17 = Meta_five
+keycode 0x18 = equal            plus
+       alt     keycode 0x18 = Meta_equal
+keycode 0x19 = nine             parenleft        bracketright
+       alt     keycode 0x19 = Meta_nine
+keycode 0x1a = seven            ampersand        braceleft
+       control keycode 0x1a = Control_underscore
+       alt     keycode 0x1a = Meta_seven
+keycode 0x1b = minus            underscore       backslash
+       control keycode 0x1b = Control_underscore
+       shift   control keycode 0x1b = Control_underscore
+       alt     keycode 0x1b = Meta_minus
+keycode 0x1c = eight            asterisk         bracketleft
+       control keycode 0x1c = Delete
+       alt     keycode 0x1c = Meta_eight
+keycode 0x1d = zero             parenright       braceright
+       alt     keycode 0x1d = Meta_zero
+keycode 0x1e = bracketright     braceright       asciitilde
+       control keycode 0x1e = Control_bracketright
+       alt     keycode 0x1e = Meta_bracketright
+keycode 0x1f = o
+keycode 0x20 = u
+keycode 0x21 = bracketleft      braceleft
+       control keycode 0x21 = Escape
+       alt     keycode 0x21 = Meta_bracketleft
+keycode 0x22 = i
+keycode 0x23 = p
+keycode 0x24 = Return
+       alt     keycode 0x24 = Meta_Control_m
+keycode 0x25 = l
+keycode 0x26 = j
+keycode 0x27 = apostrophe       quotedbl
+       control keycode 0x27 = Control_g
+       alt     keycode 0x27 = Meta_apostrophe
+keycode 0x28 = k
+keycode 0x29 = semicolon        colon
+       alt     keycode 0x29 = Meta_semicolon
+keycode 0x2a = backslash        bar
+       control keycode 0x2a = Control_backslash
+       alt     keycode 0x2a = Meta_backslash
+keycode 0x2b = comma            less
+       alt     keycode 0x2b = Meta_comma
+keycode 0x2c = slash            question
+       control keycode 0x2c = Delete
+       alt     keycode 0x2c = Meta_slash
+keycode 0x2d = n
+keycode 0x2e = m
+keycode 0x2f = period           greater
+       control keycode 0x2f = Compose
+       alt     keycode 0x2f = Meta_period
+keycode 0x30 = Tab              Tab
+       alt     keycode 0x30 = Meta_Tab
+keycode 0x31 = space            space
+       control keycode 0x31 = nul
+       alt     keycode 0x31 = Meta_space
+keycode 0x32 = grave            asciitilde
+       control keycode 0x32 = nul
+       alt     keycode 0x32 = Meta_grave
+keycode 0x33 = Delete           Delete
+       control keycode 0x33 = BackSpace
+       alt     keycode 0x33 = Meta_Delete
+keycode 0x34 =
+keycode 0x35 = Escape           Escape
+       alt     keycode 0x35 = Meta_Escape
+keycode 0x36 = Control
+keycode 0x37 = Alt
+keycode 0x38 = Shift
+keycode 0x39 = Caps_Lock
+keycode 0x3a = AltGr
+keycode 0x3b = Left
+       alt     keycode 0x3b = Decr_Console
+keycode 0x3c = Right
+       alt     keycode 0x3c = Incr_Console
+keycode 0x3d = Down
+keycode 0x3e = Up
+keycode 0x3f =
+keycode 0x40 =
+keycode 0x41 = KP_Period
+keycode 0x42 =
+keycode 0x43 = KP_Multiply
+keycode 0x44 =
+keycode 0x45 = KP_Add
+keycode 0x46 =
+keycode 0x47 = Num_Lock
+#      shift   keycode 0x47 = Bare_Num_Lock
+keycode 0x48 =
+keycode 0x49 =
+keycode 0x4a =
+keycode 0x4b = KP_Divide
+keycode 0x4c = KP_Enter
+keycode 0x4d =
+keycode 0x4e = KP_Subtract
+keycode 0x4f =
+keycode 0x50 =
+keycode 0x51 =
+#keycode 0x51 = KP_Equals
+keycode 0x52 = KP_0
+       alt     keycode 0x52 = Ascii_0
+       altgr   keycode 0x52 = Hex_0
+keycode 0x53 = KP_1
+       alt     keycode 0x53 = Ascii_1
+       altgr   keycode 0x53 = Hex_1
+keycode 0x54 = KP_2
+       alt     keycode 0x54 = Ascii_2
+       altgr   keycode 0x54 = Hex_2
+keycode 0x55 = KP_3
+       alt     keycode 0x55 = Ascii_3
+       altgr   keycode 0x55 = Hex_3
+keycode 0x56 = KP_4
+       alt     keycode 0x56 = Ascii_4
+       altgr   keycode 0x56 = Hex_4
+keycode 0x57 = KP_5
+       alt     keycode 0x57 = Ascii_5
+       altgr   keycode 0x57 = Hex_5
+keycode 0x58 = KP_6
+       alt     keycode 0x58 = Ascii_6
+       altgr   keycode 0x58 = Hex_6
+keycode 0x59 = KP_7
+       alt     keycode 0x59 = Ascii_7
+       altgr   keycode 0x59 = Hex_7
+keycode 0x5a =
+keycode 0x5b = KP_8
+       alt     keycode 0x5b = Ascii_8
+       altgr   keycode 0x5b = Hex_8
+keycode 0x5c = KP_9
+       alt     keycode 0x5c = Ascii_9
+       altgr   keycode 0x5c = Hex_9
+keycode 0x5d =
+keycode 0x5e =
+keycode 0x5f =
+keycode 0x60 = F5               F15              Console_17
+       control keycode 0x60 = F5
+       alt     keycode 0x60 = Console_5
+       control alt     keycode 0x60 = Console_5
+keycode 0x61 = F6               F16              Console_18
+       control keycode 0x61 = F6
+       alt     keycode 0x61 = Console_6
+       control alt     keycode 0x61 = Console_6
+keycode 0x62 = F7               F17              Console_19
+       control keycode 0x62 = F7
+       alt     keycode 0x62 = Console_7
+       control alt     keycode 0x62 = Console_7
+keycode 0x63 = F3               F13              Console_15
+       control keycode 0x63 = F3
+       alt     keycode 0x63 = Console_3
+       control alt     keycode 0x63 = Console_3
+keycode 0x64 = F8               F18              Console_20
+       control keycode 0x64 = F8
+       alt     keycode 0x64 = Console_8
+       control alt     keycode 0x64 = Console_8
+keycode 0x65 = F9               F19              Console_21
+       control keycode 0x65 = F9
+       alt     keycode 0x65 = Console_9
+       control alt     keycode 0x65 = Console_9
+keycode 0x66 =
+keycode 0x67 = F11              F11              Console_23
+       control keycode 0x67 = F11
+       alt     keycode 0x67 = Console_11
+       control alt     keycode 0x67 = Console_11
+keycode 0x68 =
+keycode 0x69 = F13
+keycode 0x6a =
+keycode 0x6b = Scroll_Lock      Show_Memory      Show_Registers
+       control keycode 0x6b = Show_State
+       alt     keycode 0x6b = Scroll_Lock
+keycode 0x6c =
+keycode 0x6d = F10              F20              Console_22
+       control keycode 0x6d = F10
+       alt     keycode 0x6d = Console_10
+       control alt     keycode 0x6d = Console_10
+keycode 0x6e =
+keycode 0x6f = F12              F12              Console_24
+       control keycode 0x6f = F12
+       alt     keycode 0x6f = Console_12
+       control alt     keycode 0x6f = Console_12
+keycode 0x70 =
+keycode 0x71 = Pause
+keycode 0x72 = Insert
+keycode 0x73 = Home
+keycode 0x74 = Prior
+       shift   keycode 0x74 = Scroll_Backward
+keycode 0x75 = Remove
+keycode 0x76 = F4               F14              Console_16
+       control keycode 0x76 = F4
+       alt     keycode 0x76 = Console_4
+       control alt     keycode 0x76 = Console_4
+keycode 0x77 = End
+keycode 0x78 = F2               F12              Console_14
+       control keycode 0x78 = F2
+       alt     keycode 0x78 = Console_2
+       control alt     keycode 0x78 = Console_2
+keycode 0x79 = Next
+       shift   keycode 0x79 = Scroll_Forward
+keycode 0x7a = F1               F11              Console_13
+       control keycode 0x7a = F1
+       alt     keycode 0x7a = Console_1
+       control alt     keycode 0x7a = Console_1
+keycode 0x7b = Shift
+keycode 0x7c = AltGr
+keycode 0x7d = Control
+keycode 0x7e =
+keycode 0x7f =
+#keycode 0x7f = Power
+       control shift   keycode 0x7f = Boot
+string F1 = "\033[[A"
+string F2 = "\033[[B"
+string F3 = "\033[[C"
+string F4 = "\033[[D"
+string F5 = "\033[[E"
+string F6 = "\033[17~"
+string F7 = "\033[18~"
+string F8 = "\033[19~"
+string F9 = "\033[20~"
+string F10 = "\033[21~"
+string F11 = "\033[23~"
+string F12 = "\033[24~"
+string F13 = "\033[25~"
+string F14 = "\033[26~"
+string F15 = "\033[28~"
+string F16 = "\033[29~"
+string F17 = "\033[31~"
+string F18 = "\033[32~"
+string F19 = "\033[33~"
+string F20 = "\033[34~"
+string Find = "\033[1~"
+string Insert = "\033[2~"
+string Remove = "\033[3~"
+string Select = "\033[4~"
+string Prior = "\033[5~"
+string Next = "\033[6~"
+string Macro = "\033[M"
+string Pause = "\033[P"
+compose '`' 'A' to '�'
+compose '`' 'a' to '�'
+compose '\'' 'A' to '�'
+compose '\'' 'a' to '�'
+compose '^' 'A' to '�'
+compose '^' 'a' to '�'
+compose '~' 'A' to '�'
+compose '~' 'a' to '�'
+compose '"' 'A' to '�'
+compose '"' 'a' to '�'
+compose 'O' 'A' to '�'
+compose 'o' 'a' to '�'
+compose '0' 'A' to '�'
+compose '0' 'a' to '�'
+compose 'A' 'A' to '�'
+compose 'a' 'a' to '�'
+compose 'A' 'E' to '�'
+compose 'a' 'e' to '�'
+compose ',' 'C' to '�'
+compose ',' 'c' to '�'
+compose '`' 'E' to '�'
+compose '`' 'e' to '�'
+compose '\'' 'E' to '�'
+compose '\'' 'e' to '�'
+compose '^' 'E' to '�'
+compose '^' 'e' to '�'
+compose '"' 'E' to '�'
+compose '"' 'e' to '�'
+compose '`' 'I' to '�'
+compose '`' 'i' to '�'
+compose '\'' 'I' to '�'
+compose '\'' 'i' to '�'
+compose '^' 'I' to '�'
+compose '^' 'i' to '�'
+compose '"' 'I' to '�'
+compose '"' 'i' to '�'
+compose '-' 'D' to '�'
+compose '-' 'd' to '�'
+compose '~' 'N' to '�'
+compose '~' 'n' to '�'
+compose '`' 'O' to '�'
+compose '`' 'o' to '�'
+compose '\'' 'O' to '�'
+compose '\'' 'o' to '�'
+compose '^' 'O' to '�'
+compose '^' 'o' to '�'
+compose '~' 'O' to '�'
+compose '~' 'o' to '�'
+compose '"' 'O' to '�'
+compose '"' 'o' to '�'
+compose '/' 'O' to '�'
+compose '/' 'o' to '�'
+compose '`' 'U' to '�'
+compose '`' 'u' to '�'
+compose '\'' 'U' to '�'
+compose '\'' 'u' to '�'
+compose '^' 'U' to '�'
+compose '^' 'u' to '�'
+compose '"' 'U' to '�'
+compose '"' 'u' to '�'
+compose '\'' 'Y' to '�'
+compose '\'' 'y' to '�'
+compose 'T' 'H' to '�'
+compose 't' 'h' to '�'
+compose 's' 's' to '�'
+compose '"' 'y' to '�'
+compose 's' 'z' to '�'
+compose 'i' 'j' to '�'
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/drivers/char/macserial.c linux-2.0.29/drivers/char/macserial.c
--- linux.vanilla/drivers/char/macserial.c      Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/drivers/char/macserial.c       Tue Apr 15 10:55:02 1997
@@ -0,0 +1,1988 @@
+/*
+ * macserial.c: Serial port driver for Power Macintoshes.
+ *             Extended for the 68K mac by Alan Cox.
+ *
+ * Derived from drivers/sbus/char/sunserial.c by Paul Mackerras.
+ *
+ * Copyright (C) 1996 Paul Mackerras ([email protected])
+ * Copyright (C) 1995 David S. Miller ([email protected])
+ */
+
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/config.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#ifndef CONFIG_MAC
+#include <asm/prom.h>
+#endif
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <asm/bitops.h>
+
+#include "macserial.h"
+
+/*
+ * It would be nice to dynamically allocate everything that
+ * depends on NUM_SERIAL, so we could support any number of
+ * Z8530s, but for now...
+ */
+#define NUM_SERIAL     2               /* Max number of ZS chips supported */
+#define NUM_CHANNELS   (NUM_SERIAL * 2)        /* 2 channels per chip */
+
+#ifdef CONFIG_MAC
+/*
+ *     All the Macintosh 68K boxes that have an MMU also have hardware
+ *     recovery delays.
+ */
+#define RECOVERY_DELAY
+#else
+/* On PowerMacs, the hardware takes care of the SCC recovery time,
+   but we need the eieio to make sure that the accesses occur
+   in the order we want. */
+#define RECOVERY_DELAY eieio()
+#endif
+
+struct mac_zschannel *zs_kgdbchan;
+struct mac_zschannel zs_channels[NUM_CHANNELS];
+
+struct mac_serial zs_soft[NUM_CHANNELS];
+int zs_channels_found;
+struct mac_serial *zs_chain;   /* list of all channels */
+
+struct tty_struct zs_ttys[NUM_CHANNELS];
+/** struct tty_struct *zs_constty; **/
+
+/* Console hooks... */
+static int zs_cons_chanout = 0;
+static int zs_cons_chanin = 0;
+struct mac_serial *zs_consinfo = 0;
+struct mac_zschannel *zs_conschan;
+
+static unsigned char kgdb_regs[16] = {
+       0, 0, 0,                /* write 0, 1, 2 */
+       (Rx8 | RxENABLE),       /* write 3 */
+       (X16CLK | SB1 | PAR_EVEN), /* write 4 */
+       (Tx8 | TxENAB),         /* write 5 */
+       0, 0, 0,                /* write 6, 7, 8 */
+       (NV),                   /* write 9 */
+       (NRZ),                  /* write 10 */
+       (TCBR | RCBR),          /* write 11 */
+       1, 0,                   /* 38400 baud divisor, write 12 + 13 */
+       (BRENABL),              /* write 14 */
+       (DCDIE)                 /* write 15 */
+};
+
+#define ZS_CLOCK         3686400       /* Z8530 RTxC input clock rate */
+
+DECLARE_TASK_QUEUE(tq_serial);
+
+struct tty_driver serial_driver, callout_driver;
+static int serial_refcount;
+
+/* serial subtype definitions */
+#define SERIAL_TYPE_NORMAL     1
+#define SERIAL_TYPE_CALLOUT    2
+
+/* number of characters left in xmit buffer before we ask for more */
+#define WAKEUP_CHARS 256
+
+/* Debugging... DEBUG_INTR is bad to use when one of the zs
+ * lines is your console ;(
+ */
+#undef SERIAL_DEBUG_INTR
+#undef SERIAL_DEBUG_OPEN
+#undef SERIAL_DEBUG_FLOW
+
+#define RS_STROBE_TIME 10
+#define RS_ISR_PASS_LIMIT 256
+
+#define _INLINE_ inline
+
+static void probe_sccs(void);
+static void change_speed(struct mac_serial *info);
+
+static struct tty_struct *serial_table[NUM_CHANNELS];
+static struct termios *serial_termios[NUM_CHANNELS];
+static struct termios *serial_termios_locked[NUM_CHANNELS];
+
+#ifndef MIN
+#define MIN(a,b)       ((a) < (b) ? (a) : (b))
+#endif
+
+/*
+ * tmp_buf is used as a temporary buffer by serial_write.  We need to
+ * lock it in case the memcpy_fromfs blocks while swapping in a page,
+ * and some other program tries to do a serial write at the same time.
+ * Since the lock will only come under contention when the system is
+ * swapping and available memory is low, it makes sense to share one
+ * buffer across all the serial ports, since it significantly saves
+ * memory if large numbers of serial ports are open.
+ */
+static unsigned char tmp_buf[4096]; /* This is cheating */
+static struct semaphore tmp_buf_sem = MUTEX;
+
+static inline int serial_paranoia_check(struct mac_serial *info,
+                                       dev_t device, const char *routine)
+{
+#ifdef SERIAL_PARANOIA_CHECK
+       static const char *badmagic =
+               "Warning: bad magic number for serial struct (%d, %d) in %s\n";
+       static const char *badinfo =
+               "Warning: null mac_serial for (%d, %d) in %s\n";
+
+       if (!info) {
+               printk(badinfo, MAJOR(device), MINOR(device), routine);
+               return 1;
+       }
+       if (info->magic != SERIAL_MAGIC) {
+               printk(badmagic, MAJOR(device), MINOR(device), routine);
+               return 1;
+       }
+#endif
+       return 0;
+}
+
+/*
+ * This is used to figure out the divisor speeds and the timeouts
+ */
+static int baud_table[] = {
+       0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
+       9600, 19200, 38400, 57600, 115200, 0 };
+
+/*
+ * Reading and writing Z8530 registers.
+ */
+static inline unsigned char read_zsreg(struct mac_zschannel *channel,
+                                      unsigned char reg)
+{
+       unsigned char retval;
+
+       if (reg != 0) {
+               *channel->control = reg;
+               RECOVERY_DELAY;
+       }
+       retval = *channel->control;
+       RECOVERY_DELAY;
+       return retval;
+}
+
+static inline void write_zsreg(struct mac_zschannel *channel,
+                              unsigned char reg, unsigned char value)
+{
+       if (reg != 0) {
+               *channel->control = reg;
+               RECOVERY_DELAY;
+       }
+       *channel->control = value;
+       RECOVERY_DELAY;
+       return;
+}
+
+static inline unsigned char read_zsdata(struct mac_zschannel *channel)
+{
+       unsigned char retval;
+
+       retval = *channel->data;
+       RECOVERY_DELAY;
+       return retval;
+}
+
+static inline void write_zsdata(struct mac_zschannel *channel,
+                               unsigned char value)
+{
+       *channel->data = value;
+       RECOVERY_DELAY;
+       return;
+}
+
+static inline void load_zsregs(struct mac_zschannel *channel,
+                              unsigned char *regs)
+{
+       ZS_CLEARERR(channel);
+       ZS_CLEARFIFO(channel);
+       /* Load 'em up */
+       write_zsreg(channel, R4, regs[R4]);
+       write_zsreg(channel, R10, regs[R10]);
+       write_zsreg(channel, R3, regs[R3] & ~RxENABLE);
+       write_zsreg(channel, R5, regs[R5] & ~TxENAB);
+       write_zsreg(channel, R1, regs[R1]);
+       write_zsreg(channel, R9, regs[R9]);
+       write_zsreg(channel, R11, regs[R11]);
+       write_zsreg(channel, R12, regs[R12]);
+       write_zsreg(channel, R13, regs[R13]);
+       write_zsreg(channel, R14, regs[R14]);
+       write_zsreg(channel, R15, regs[R15]);
+       write_zsreg(channel, R3, regs[R3]);
+       write_zsreg(channel, R5, regs[R5]);
+       return;
+}
+
+/* Sets or clears DTR/RTS on the requested line */
+static inline void zs_rtsdtr(struct mac_serial *ss, int set)
+{
+       if (set)
+               ss->curregs[5] |= (RTS | DTR);
+       else
+               ss->curregs[5] &= ~(RTS | DTR);
+       write_zsreg(ss->zs_channel, 5, ss->curregs[5]);
+       return;
+}
+
+static inline void kgdb_chaninit(struct mac_serial *ss, int intson, int bps)
+{
+       int brg;
+
+       if (intson) {
+               kgdb_regs[R1] = INT_ALL_Rx;
+               kgdb_regs[R9] |= MIE;
+       } else {
+               kgdb_regs[R1] = 0;
+               kgdb_regs[R9] &= ~MIE;
+       }
+       brg = BPS_TO_BRG(bps, ZS_CLOCK/16);
+       kgdb_regs[R12] = brg;
+       kgdb_regs[R13] = brg >> 8;
+       load_zsregs(ss->zs_channel, kgdb_regs);
+}
+
+/* Utility routines for the Zilog */
+static inline int get_zsbaud(struct mac_serial *ss)
+{
+       struct mac_zschannel *channel = ss->zs_channel;
+       int brg;
+
+       /* The baud rate is split up between two 8-bit registers in
+        * what is termed 'BRG time constant' format in my docs for
+        * the chip, it is a function of the clk rate the chip is
+        * receiving which happens to be constant.
+        */
+       brg = (read_zsreg(channel, 13) << 8);
+       brg |= read_zsreg(channel, 12);
+       return BRG_TO_BPS(brg, (ZS_CLOCK/(ss->clk_divisor)));
+}
+
+/* On receive, this clears errors and the receiver interrupts */
+static inline void rs_recv_clear(struct mac_zschannel *zsc)
+{
+       write_zsreg(zsc, 0, ERR_RES);
+       write_zsreg(zsc, 0, RES_H_IUS); /* XXX this is unnecessary */
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Here starts the interrupt handling routines.  All of the following
+ * subroutines are declared as inline and are folded into
+ * rs_interrupt().  They were separated out for readability's sake.
+ *
+ *                             - Ted Ts'o ([email protected]), 7-Mar-93
+ * -----------------------------------------------------------------------
+ */
+
+/*
+ * This routine is used by the interrupt handler to schedule
+ * processing in the software interrupt portion of the driver.
+ */
+static _INLINE_ void rs_sched_event(struct mac_serial *info,
+                                 int event)
+{
+       info->event |= 1 << event;
+       queue_task_irq_off(&info->tqueue, &tq_serial);
+       mark_bh(SERIAL_BH);
+}
+
+extern void breakpoint(void);  /* For the KGDB frame character */
+
+static _INLINE_ void receive_chars(struct mac_serial *info,
+                                  struct pt_regs *regs)
+{
+       struct tty_struct *tty = info->tty;
+       unsigned char ch, stat, flag;
+
+       while ((read_zsreg(info->zs_channel, 0) & Rx_CH_AV) != 0) {
+
+               stat = read_zsreg(info->zs_channel, R1);
+               ch = read_zsdata(info->zs_channel);
+
+#if 0  /* KGDB not yet supported */
+               /* Look for kgdb 'stop' character, consult the gdb documentation
+                * for remote target debugging and arch/sparc/kernel/sparc-stub.c
+                * to see how all this works.
+                */
+               if ((info->kgdb_channel) && (ch =='\003')) {
+                       breakpoint();
+                       continue;
+               }
+#endif
+
+               if (!tty)
+                       continue;
+
+               if (tty->flip.count >= TTY_FLIPBUF_SIZE)
+                       queue_task_irq_off(&tty->flip.tqueue, &tq_timer);
+               tty->flip.count++;
+               if (stat & Rx_OVR) {
+                       flag = TTY_OVERRUN;
+                       /* reset the error indication */
+                       write_zsreg(info->zs_channel, 0, ERR_RES);
+               } else if (stat & FRM_ERR) {
+                       /* this error is not sticky */
+                       flag = TTY_FRAME;
+               } else if (stat & PAR_ERR) {
+                       flag = TTY_PARITY;
+                       /* reset the error indication */
+                       write_zsreg(info->zs_channel, 0, ERR_RES);
+               } else
+                       flag = 0;
+               *tty->flip.flag_buf_ptr++ = flag;
+               *tty->flip.char_buf_ptr++ = ch;
+
+               queue_task_irq_off(&tty->flip.tqueue, &tq_timer);
+       }
+#if 0
+clear_and_exit:
+       rs_recv_clear(info->zs_channel);
+#endif
+}
+
+static void transmit_chars(struct mac_serial *info)
+{
+       if ((read_zsreg(info->zs_channel, 0) & Tx_BUF_EMP) == 0)
+               return;
+       info->tx_active = 0;
+
+       if (info->x_char) {
+               /* Send next char */
+               write_zsdata(info->zs_channel, info->x_char);
+               info->x_char = 0;
+               info->tx_active = 1;
+               return;
+       }
+
+       if ((info->xmit_cnt <= 0) || info->tty->stopped || info->tx_stopped) {
+               write_zsreg(info->zs_channel, 0, RES_Tx_P);
+               return;
+       }
+
+       /* Send char */
+       write_zsdata(info->zs_channel, info->xmit_buf[info->xmit_tail++]);
+       info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
+       info->xmit_cnt--;
+       info->tx_active = 1;
+
+       if (info->xmit_cnt < WAKEUP_CHARS)
+               rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
+}
+
+static _INLINE_ void status_handle(struct mac_serial *info)
+{
+       unsigned char status;
+
+       /* Get status from Read Register 0 */
+       status = read_zsreg(info->zs_channel, 0);
+
+       /* Check for DCD transitions */
+       if (((status ^ info->read_reg_zero) & DCD) != 0
+           && info->tty && C_CLOCAL(info->tty)) {
+               if (status & DCD) {
+                       wake_up_interruptible(&info->open_wait);
+               } else if (!(info->flags & ZILOG_CALLOUT_ACTIVE)) {
+                       queue_task_irq_off(&info->tqueue_hangup,
+                                          &tq_scheduler);
+               }
+       }
+
+       /* Check for CTS transitions */
+       if (info->tty && C_CRTSCTS(info->tty)) {
+               /*
+                * For some reason, on the Power Macintosh,
+                * it seems that the CTS bit is 1 when CTS is
+                * *negated* and 0 when it is asserted.
+                * The DCD bit doesn't seem to be inverted
+                * like this.
+                */
+               if ((status & CTS) == 0) {
+                       if (info->tx_stopped) {
+                               info->tx_stopped = 0;
+                               if (!info->tx_active)
+                                       transmit_chars(info);
+                       }
+               } else {
+                       info->tx_stopped = 1;
+               }
+       }
+
+       /* Clear status condition... */
+       write_zsreg(info->zs_channel, 0, RES_EXT_INT);
+       info->read_reg_zero = status;
+}
+
+/*
+ * This is the serial driver's generic interrupt routine
+ */
+void rs_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+{
+       struct mac_serial *info = (struct mac_serial *) dev_id;
+       unsigned char zs_intreg;
+       int shift;
+
+       /* NOTE: The read register 3, which holds the irq status,
+        *       does so for both channels on each chip.  Although
+        *       the status value itself must be read from the A
+        *       channel and is only valid when read from channel A.
+        *       Yes... broken hardware...
+        */
+#define CHAN_IRQMASK (CHBRxIP | CHBTxIP | CHBEXT)
+
+       if (info->zs_chan_a == info->zs_channel)
+               shift = 3;      /* Channel A */
+       else
+               shift = 0;      /* Channel B */
+
+       for (;;) {
+               zs_intreg = read_zsreg(info->zs_chan_a, 3) >> shift;
+               if ((zs_intreg & CHAN_IRQMASK) == 0)
+                       break;
+
+               if (zs_intreg & CHBRxIP)
+                       receive_chars(info, regs);
+               if (zs_intreg & CHBTxIP)
+                       transmit_chars(info);
+               if (zs_intreg & CHBEXT)
+                       status_handle(info);
+       }
+}
+
+/*
+ * -------------------------------------------------------------------
+ * Here ends the serial interrupt routines.
+ * -------------------------------------------------------------------
+ */
+
+/*
+ * ------------------------------------------------------------
+ * rs_stop() and rs_start()
+ *
+ * This routines are called before setting or resetting tty->stopped.
+ * ------------------------------------------------------------
+ */
+static void rs_stop(struct tty_struct *tty)
+{
+       struct mac_serial *info = (struct mac_serial *)tty->driver_data;
+
+       if (serial_paranoia_check(info, tty->device, "rs_stop"))
+               return;
+
+#if 0
+       save_flags(flags); cli();
+       if (info->curregs[5] & TxENAB) {
+               info->curregs[5] &= ~TxENAB;
+               info->pendregs[5] &= ~TxENAB;
+               write_zsreg(info->zs_channel, 5, info->curregs[5]);
+       }
+       restore_flags(flags);
+#endif
+}
+
+static void rs_start(struct tty_struct *tty)
+{
+       struct mac_serial *info = (struct mac_serial *)tty->driver_data;
+       unsigned long flags;
+
+       if (serial_paranoia_check(info, tty->device, "rs_start"))
+               return;
+
+       save_flags(flags); cli();
+#if 0
+       if (info->xmit_cnt && info->xmit_buf && !(info->curregs[5] & TxENAB)) {
+               info->curregs[5] |= TxENAB;
+               info->pendregs[5] = info->curregs[5];
+               write_zsreg(info->zs_channel, 5, info->curregs[5]);
+       }
+#else
+       if (info->xmit_cnt && info->xmit_buf && !info->tx_active) {
+               transmit_chars(info);
+       }
+#endif
+       restore_flags(flags);
+}
+
+/*
+ * This routine is used to handle the "bottom half" processing for the
+ * serial driver, known also the "software interrupt" processing.
+ * This processing is done at the kernel interrupt level, after the
+ * rs_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON.  This
+ * is where time-consuming activities which can not be done in the
+ * interrupt driver proper are done; the interrupt driver schedules
+ * them using rs_sched_event(), and they get done here.
+ */
+static void do_serial_bh(void)
+{
+       run_task_queue(&tq_serial);
+}
+
+static void do_softint(void *private_)
+{
+       struct mac_serial       *info = (struct mac_serial *) private_;
+       struct tty_struct       *tty;
+
+       tty = info->tty;
+       if (!tty)
+               return;
+
+       if (clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {
+               if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+                   tty->ldisc.write_wakeup)
+                       (tty->ldisc.write_wakeup)(tty);
+               wake_up_interruptible(&tty->write_wait);
+       }
+}
+
+/*
+ * This routine is called from the scheduler tqueue when the interrupt
+ * routine has signalled that a hangup has occurred.  The path of
+ * hangup processing is:
+ *
+ *     serial interrupt routine -> (scheduler tqueue) ->
+ *     do_serial_hangup() -> tty->hangup() -> rs_hangup()
+ *
+ */
+static void do_serial_hangup(void *private_)
+{
+       struct mac_serial       *info = (struct mac_serial *) private_;
+       struct tty_struct       *tty;
+
+       tty = info->tty;
+       if (!tty)
+               return;
+
+       tty_hangup(tty);
+}
+
+static void rs_timer(void)
+{
+}
+
+static int startup(struct mac_serial * info)
+{
+       unsigned long flags;
+
+       if (info->flags & ZILOG_INITIALIZED)
+               return 0;
+
+       if (!info->xmit_buf) {
+               info->xmit_buf = (unsigned char *) get_free_page(GFP_KERNEL);
+               if (!info->xmit_buf)
+                       return -ENOMEM;
+       }
+
+       save_flags(flags); cli();
+
+#ifdef SERIAL_DEBUG_OPEN
+       printk("starting up ttyS%d (irq %d)...", info->line, info->irq);
+#endif
+
+       /*
+        * Clear the receive FIFO.
+        */
+       ZS_CLEARFIFO(info->zs_channel);
+       info->xmit_fifo_size = 1;
+
+       /*
+        * Clear the interrupt registers.
+        */
+       write_zsreg(info->zs_channel, 0, ERR_RES);
+       write_zsreg(info->zs_channel, 0, RES_H_IUS);
+
+       /*
+        * Turn on RTS and DTR.
+        */
+       zs_rtsdtr(info, 1);
+
+       /*
+        * Finally, enable sequencing and interrupts
+        */
+       info->curregs[1] = (info->curregs[1] & ~0x18) | (EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB);
+       info->pendregs[1] = info->curregs[1];
+       info->curregs[3] |= (RxENABLE | Rx8);
+       info->pendregs[3] = info->curregs[3];
+       info->curregs[5] |= (TxENAB | Tx8);
+       info->pendregs[5] = info->curregs[5];
+       info->curregs[9] |= (NV | MIE);
+       info->pendregs[9] = info->curregs[9];
+       write_zsreg(info->zs_channel, 3, info->curregs[3]);
+       write_zsreg(info->zs_channel, 5, info->curregs[5]);
+       write_zsreg(info->zs_channel, 9, info->curregs[9]);
+
+       if (info->tty)
+               clear_bit(TTY_IO_ERROR, &info->tty->flags);
+       info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+
+       /*
+        * Set the speed of the serial port
+        */
+       change_speed(info);
+
+       /* Save the current value of RR0 */
+       info->read_reg_zero = read_zsreg(info->zs_channel, 0);
+
+       info->flags |= ZILOG_INITIALIZED;
+       restore_flags(flags);
+       return 0;
+}
+
+/*
+ * This routine will shutdown a serial port; interrupts are disabled, and
+ * DTR is dropped if the hangup on close termio flag is on.
+ */
+static void shutdown(struct mac_serial * info)
+{
+       unsigned long   flags;
+
+       if (!(info->flags & ZILOG_INITIALIZED))
+               return;
+
+#ifdef SERIAL_DEBUG_OPEN
+       printk("Shutting down serial port %d (irq %d)....", info->line,
+              info->irq);
+#endif
+
+       save_flags(flags); cli(); /* Disable interrupts */
+
+       if (info->xmit_buf) {
+               free_page((unsigned long) info->xmit_buf);
+               info->xmit_buf = 0;
+       }
+
+       info->pendregs[1] = info->curregs[1] = 0;
+       write_zsreg(info->zs_channel, 1, 0);    /* no interrupts */
+
+       info->curregs[3] &= ~RxENABLE;
+       info->pendregs[3] = info->curregs[3];
+       write_zsreg(info->zs_channel, 3, info->curregs[3]);
+
+       info->curregs[5] &= ~TxENAB;
+       if (!info->tty || C_HUPCL(info->tty))
+               info->curregs[5] &= ~(DTR | RTS);
+       info->pendregs[5] = info->curregs[5];
+       write_zsreg(info->zs_channel, 5, info->curregs[5]);
+
+       if (info->tty)
+               set_bit(TTY_IO_ERROR, &info->tty->flags);
+
+       info->flags &= ~ZILOG_INITIALIZED;
+       restore_flags(flags);
+}
+
+/*
+ * This routine is called to set the UART divisor registers to match
+ * the specified baud rate for a serial port.
+ */
+static void change_speed(struct mac_serial *info)
+{
+       unsigned short port;
+       unsigned cflag;
+       int     i;
+       int     brg;
+       unsigned long flags;
+
+       if (!info->tty || !info->tty->termios)
+               return;
+       cflag = info->tty->termios->c_cflag;
+       if (!(port = info->port))
+               return;
+       i = cflag & CBAUD;
+       if (i & CBAUDEX) {
+               /* XXX CBAUDEX is not obeyed.
+                * It is impossible at a 32bits PPC.  XXX??
+                * But we have to report this to user ... someday.
+                */
+               i = B9600;
+       }
+
+       save_flags(flags); cli();
+       info->zs_baud = baud_table[i];
+       info->clk_divisor = 16;
+
+       info->curregs[4] = X16CLK;
+       info->curregs[11] = TCBR | RCBR;
+       brg = BPS_TO_BRG(info->zs_baud, ZS_CLOCK/info->clk_divisor);
+       info->curregs[12] = (brg & 255);
+       info->curregs[13] = ((brg >> 8) & 255);
+       info->curregs[14] = BRENABL;
+
+       /* byte size and parity */
+       info->curregs[3] &= ~RxNBITS_MASK;
+       info->curregs[5] &= ~TxNBITS_MASK;
+       switch (cflag & CSIZE) {
+       case CS5:
+               info->curregs[3] |= Rx5;
+               info->curregs[5] |= Tx5;
+               break;
+       case CS6:
+               info->curregs[3] |= Rx6;
+               info->curregs[5] |= Tx6;
+               break;
+       case CS7:
+               info->curregs[3] |= Rx7;
+               info->curregs[5] |= Tx7;
+               break;
+       case CS8:
+       default: /* defaults to 8 bits */
+               info->curregs[3] |= Rx8;
+               info->curregs[5] |= Tx8;
+               break;
+       }
+       info->pendregs[3] = info->curregs[3];
+       info->pendregs[5] = info->curregs[5];
+
+       info->curregs[4] &= ~(SB_MASK | PAR_ENA | PAR_EVEN);
+       if (cflag & CSTOPB) {
+               info->curregs[4] |= SB2;
+       } else {
+               info->curregs[4] |= SB1;
+       }
+       if (cflag & PARENB) {
+               info->curregs[4] |= PAR_ENA;
+       }
+       if (!(cflag & PARODD)) {
+               info->curregs[4] |= PAR_EVEN;
+       }
+       info->pendregs[4] = info->curregs[4];
+
+       info->curregs[15] &= ~(DCDIE | CTSIE);
+       if (!(cflag & CLOCAL)) {
+               info->curregs[15] |= DCDIE;
+       }
+       if (cflag & CRTSCTS) {
+               info->curregs[15] |= CTSIE;
+               if ((read_zsreg(info->zs_channel, 0) & CTS) != 0)
+                       info->tx_stopped = 1;
+       } else
+               info->tx_stopped = 0;
+       info->pendregs[15] = info->curregs[15];
+
+       /* Load up the new values */
+       load_zsregs(info->zs_channel, info->curregs);
+
+       restore_flags(flags);
+}
+
+/* This is for console output over ttya/ttyb */
+static void rs_put_char(char ch)
+{
+       struct mac_zschannel *chan = zs_conschan;
+       int loops = 0;
+       unsigned long flags;
+
+       if(!chan)
+               return;
+
+       save_flags(flags); cli();
+       while ((read_zsreg(chan, 0) & Tx_BUF_EMP) == 0 && loops < 10000) {
+               loops++;
+               udelay(5);
+       }
+       write_zsdata(chan, ch);
+       restore_flags(flags);
+}
+
+/* These are for receiving and sending characters under the kgdb
+ * source level kernel debugger.
+ */
+void putDebugChar(char kgdb_char)
+{
+       struct mac_zschannel *chan = zs_kgdbchan;
+
+       while ((read_zsreg(chan, 0) & Tx_BUF_EMP) == 0)
+               udelay(5);
+       write_zsdata(chan, kgdb_char);
+}
+
+char getDebugChar(void)
+{
+       struct mac_zschannel *chan = zs_kgdbchan;
+
+       while ((read_zsreg(chan, 0) & Rx_CH_AV) == 0)
+               udelay(5);
+       return read_zsdata(chan);
+}
+
+/*
+ * Fair output driver allows a process to speak.
+ */
+static void rs_fair_output(void)
+{
+       int left;               /* Output no more than that */
+       unsigned long flags;
+       struct mac_serial *info = zs_consinfo;
+       char c;
+
+       if (info == 0) return;
+       if (info->xmit_buf == 0) return;
+
+       save_flags(flags);  cli();
+       left = info->xmit_cnt;
+       while (left != 0) {
+               c = info->xmit_buf[info->xmit_tail];
+               info->xmit_tail = (info->xmit_tail+1) & (SERIAL_XMIT_SIZE-1);
+               info->xmit_cnt--;
+               restore_flags(flags);
+
+               rs_put_char(c);
+
+               save_flags(flags);  cli();
+               left = MIN(info->xmit_cnt, left-1);
+       }
+
+       restore_flags(flags);
+       return;
+}
+
+/*
+ * zs_console_print is registered for printk.
+ */
+static void zs_console_print(const char *p)
+{
+       char c;
+
+       while ((c = *(p++)) != 0) {
+               if (c == '\n')
+                       rs_put_char('\r');
+               rs_put_char(c);
+       }
+
+       /* Comment this if you want to have a strict interrupt-driven output */
+       rs_fair_output();
+}
+
+static void rs_flush_chars(struct tty_struct *tty)
+{
+       struct mac_serial *info = (struct mac_serial *)tty->driver_data;
+       unsigned long flags;
+
+       if (serial_paranoia_check(info, tty->device, "rs_flush_chars"))
+               return;
+
+       if (info->xmit_cnt <= 0 || tty->stopped || info->tx_stopped ||
+           !info->xmit_buf)
+               return;
+
+       /* Enable transmitter */
+       save_flags(flags); cli();
+       transmit_chars(info);
+       restore_flags(flags);
+}
+
+static int rs_write(struct tty_struct * tty, int from_user,
+                   const unsigned char *buf, int count)
+{
+       int     c, total = 0;
+       struct mac_serial *info = (struct mac_serial *)tty->driver_data;
+       unsigned long flags;
+
+       if (serial_paranoia_check(info, tty->device, "rs_write"))
+               return 0;
+
+       if (!tty || !info->xmit_buf)
+               return 0;
+
+       save_flags(flags);
+       while (1) {
+               cli();
+               c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+                                  SERIAL_XMIT_SIZE - info->xmit_head));
+               if (c <= 0)
+                       break;
+
+               if (from_user) {
+                       down(&tmp_buf_sem);
+                       memcpy_fromfs(tmp_buf, buf, c);
+                       c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+                                      SERIAL_XMIT_SIZE - info->xmit_head));
+                       memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c);
+                       up(&tmp_buf_sem);
+               } else
+                       memcpy(info->xmit_buf + info->xmit_head, buf, c);
+               info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
+               info->xmit_cnt += c;
+               restore_flags(flags);
+               buf += c;
+               count -= c;
+               total += c;
+       }
+       if (info->xmit_cnt && !tty->stopped && !info->tx_stopped
+           && !info->tx_active)
+               transmit_chars(info);
+       restore_flags(flags);
+       return total;
+}
+
+static int rs_write_room(struct tty_struct *tty)
+{
+       struct mac_serial *info = (struct mac_serial *)tty->driver_data;
+       int     ret;
+
+       if (serial_paranoia_check(info, tty->device, "rs_write_room"))
+               return 0;
+       ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1;
+       if (ret < 0)
+               ret = 0;
+       return ret;
+}
+
+static int rs_chars_in_buffer(struct tty_struct *tty)
+{
+       struct mac_serial *info = (struct mac_serial *)tty->driver_data;
+
+       if (serial_paranoia_check(info, tty->device, "rs_chars_in_buffer"))
+               return 0;
+       return info->xmit_cnt;
+}
+
+static void rs_flush_buffer(struct tty_struct *tty)
+{
+       struct mac_serial *info = (struct mac_serial *)tty->driver_data;
+
+       if (serial_paranoia_check(info, tty->device, "rs_flush_buffer"))
+               return;
+       cli();
+       info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+       sti();
+       wake_up_interruptible(&tty->write_wait);
+       if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+           tty->ldisc.write_wakeup)
+               (tty->ldisc.write_wakeup)(tty);
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_throttle()
+ *
+ * This routine is called by the upper-layer tty layer to signal that
+ * incoming characters should be throttled.
+ * ------------------------------------------------------------
+ */
+static void rs_throttle(struct tty_struct * tty)
+{
+       struct mac_serial *info = (struct mac_serial *)tty->driver_data;
+       unsigned long flags;
+#ifdef SERIAL_DEBUG_THROTTLE
+       char    buf[64];
+
+       printk("throttle %s: %d....\n", _tty_name(tty, buf),
+              tty->ldisc.chars_in_buffer(tty));
+#endif
+
+       if (serial_paranoia_check(info, tty->device, "rs_throttle"))
+               return;
+
+       if (I_IXOFF(tty)) {
+               save_flags(flags); cli();
+               info->x_char = STOP_CHAR(tty);
+               if (!info->tx_active)
+                       transmit_chars(info);
+               restore_flags(flags);
+       }
+
+       if (C_CRTSCTS(tty)) {
+               /*
+                * Here we want to turn off the RTS line.  On Macintoshes,
+                * we only get the DTR line, which goes to both DTR and
+                * RTS on the modem.  RTS doesn't go out to the serial
+                * port socket.  So you should make sure your modem is
+                * set to ignore DTR if you're using CRTSCTS.
+                */
+               save_flags(flags); cli();
+               info->curregs[5] &= ~(DTR | RTS);
+               info->pendregs[5] &= ~(DTR | RTS);
+               write_zsreg(info->zs_channel, 5, info->curregs[5]);
+               restore_flags(flags);
+       }
+}
+
+static void rs_unthrottle(struct tty_struct * tty)
+{
+       struct mac_serial *info = (struct mac_serial *)tty->driver_data;
+       unsigned long flags;
+#ifdef SERIAL_DEBUG_THROTTLE
+       char    buf[64];
+
+       printk("unthrottle %s: %d....\n", _tty_name(tty, buf),
+              tty->ldisc.chars_in_buffer(tty));
+#endif
+
+       if (serial_paranoia_check(info, tty->device, "rs_unthrottle"))
+               return;
+
+       if (I_IXOFF(tty)) {
+               save_flags(flags); cli();
+               if (info->x_char)
+                       info->x_char = 0;
+               else {
+                       info->x_char = START_CHAR(tty);
+                       if (!info->tx_active)
+                               transmit_chars(info);
+               }
+               restore_flags(flags);
+       }
+
+       if (C_CRTSCTS(tty)) {
+               /* Assert RTS and DTR lines */
+               save_flags(flags); cli();
+               info->curregs[5] |= DTR | RTS;
+               info->pendregs[5] |= DTR | RTS;
+               write_zsreg(info->zs_channel, 5, info->curregs[5]);
+               restore_flags(flags);
+       }
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_ioctl() and friends
+ * ------------------------------------------------------------
+ */
+
+static int get_serial_info(struct mac_serial * info,
+                          struct serial_struct * retinfo)
+{
+       struct serial_struct tmp;
+
+       if (!retinfo)
+               return -EFAULT;
+       memset(&tmp, 0, sizeof(tmp));
+       tmp.type = info->type;
+       tmp.line = info->line;
+       tmp.port = info->port;
+       tmp.irq = info->irq;
+       tmp.flags = info->flags;
+       tmp.baud_base = info->baud_base;
+       tmp.close_delay = info->close_delay;
+       tmp.closing_wait = info->closing_wait;
+       tmp.custom_divisor = info->custom_divisor;
+       memcpy_tofs(retinfo,&tmp,sizeof(*retinfo));
+       return 0;
+}
+
+static int set_serial_info(struct mac_serial * info,
+                          struct serial_struct * new_info)
+{
+       struct serial_struct new_serial;
+       struct mac_serial old_info;
+       int                     retval = 0;
+
+       if (!new_info)
+               return -EFAULT;
+       memcpy_fromfs(&new_serial,new_info,sizeof(new_serial));
+       old_info = *info;
+
+       if (!suser()) {
+               if ((new_serial.baud_base != info->baud_base) ||
+                   (new_serial.type != info->type) ||
+                   (new_serial.close_delay != info->close_delay) ||
+                   ((new_serial.flags & ~ZILOG_USR_MASK) !=
+                    (info->flags & ~ZILOG_USR_MASK)))
+                       return -EPERM;
+               info->flags = ((info->flags & ~ZILOG_USR_MASK) |
+                              (new_serial.flags & ZILOG_USR_MASK));
+               info->custom_divisor = new_serial.custom_divisor;
+               goto check_and_exit;
+       }
+
+       if (info->count > 1)
+               return -EBUSY;
+
+       /*
+        * OK, past this point, all the error checking has been done.
+        * At this point, we start making changes.....
+        */
+
+       info->baud_base = new_serial.baud_base;
+       info->flags = ((info->flags & ~ZILOG_FLAGS) |
+                       (new_serial.flags & ZILOG_FLAGS));
+       info->type = new_serial.type;
+       info->close_delay = new_serial.close_delay;
+       info->closing_wait = new_serial.closing_wait;
+
+check_and_exit:
+       retval = startup(info);
+       return retval;
+}
+
+/*
+ * get_lsr_info - get line status register info
+ *
+ * Purpose: Let user call ioctl() to get info when the UART physically
+ *         is emptied.  On bus types like RS485, the transmitter must
+ *         release the bus after transmitting. This must be done when
+ *         the transmit shift register is empty, not be done when the
+ *         transmit holding register is empty.  This functionality
+ *         allows an RS485 driver to be written in user space.
+ */
+static int get_lsr_info(struct mac_serial * info, unsigned int *value)
+{
+       unsigned char status;
+
+       cli();
+       status = read_zsreg(info->zs_channel, 0);
+       sti();
+       put_user(status,value);
+       return 0;
+}
+
+static int get_modem_info(struct mac_serial *info, unsigned int *value)
+{
+       unsigned char control, status;
+       unsigned int result;
+
+       cli();
+       control = info->curregs[5];
+       status = read_zsreg(info->zs_channel, 0);
+       sti();
+       result =  ((control & RTS) ? TIOCM_RTS: 0)
+               | ((control & DTR) ? TIOCM_DTR: 0)
+               | ((status  & DCD) ? TIOCM_CAR: 0)
+               | ((status  & CTS) ? 0: TIOCM_CTS);
+       put_user(result,value);
+       return 0;
+}
+
+static int set_modem_info(struct mac_serial *info, unsigned int cmd,
+                         unsigned int *value)
+{
+       int error;
+       unsigned int arg, bits;
+
+       error = verify_area(VERIFY_READ, value, sizeof(int));
+       if (error)
+               return error;
+       arg = get_user(value);
+       bits = (arg & TIOCM_RTS? RTS: 0) + (arg & TIOCM_DTR? DTR: 0);
+       cli();
+       switch (cmd) {
+       case TIOCMBIS:
+               info->curregs[5] |= bits;
+               break;
+       case TIOCMBIC:
+               info->curregs[5] &= ~bits;
+               break;
+       case TIOCMSET:
+               info->curregs[5] = (info->curregs[5] & ~(DTR | RTS)) | bits;
+               break;
+       default:
+               sti();
+               return -EINVAL;
+       }
+       info->pendregs[5] = info->curregs[5];
+       write_zsreg(info->zs_channel, 5, info->curregs[5]);
+       sti();
+       return 0;
+}
+
+/*
+ * This routine sends a break character out the serial port.
+ */
+static void send_break(        struct mac_serial * info, int duration)
+{
+       if (!info->port)
+               return;
+       current->state = TASK_INTERRUPTIBLE;
+       current->timeout = jiffies + duration;
+       cli();
+       info->curregs[5] |= SND_BRK;
+       write_zsreg(info->zs_channel, 5, info->curregs[5]);
+       schedule();
+       info->curregs[5] &= ~SND_BRK;
+       write_zsreg(info->zs_channel, 5, info->curregs[5]);
+       sti();
+}
+
+static int rs_ioctl(struct tty_struct *tty, struct file * file,
+                   unsigned int cmd, unsigned long arg)
+{
+       int error;
+       struct mac_serial * info = (struct mac_serial *)tty->driver_data;
+       int retval;
+
+       if (serial_paranoia_check(info, tty->device, "rs_ioctl"))
+               return -ENODEV;
+
+       if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
+           (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD)  &&
+           (cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT)) {
+               if (tty->flags & (1 << TTY_IO_ERROR))
+                   return -EIO;
+       }
+
+       switch (cmd) {
+               case TCSBRK:    /* SVID version: non-zero arg --> no break */
+                       retval = tty_check_change(tty);
+                       if (retval)
+                               return retval;
+                       tty_wait_until_sent(tty, 0);
+                       if (!arg)
+                               send_break(info, HZ/4); /* 1/4 second */
+                       return 0;
+               case TCSBRKP:   /* support for POSIX tcsendbreak() */
+                       retval = tty_check_change(tty);
+                       if (retval)
+                               return retval;
+                       tty_wait_until_sent(tty, 0);
+                       send_break(info, arg ? arg*(HZ/10) : HZ/4);
+                       return 0;
+               case TIOCGSOFTCAR:
+                       error = verify_area(VERIFY_WRITE, (void *) arg,sizeof(long));
+                       if (error)
+                               return error;
+                       put_fs_long(C_CLOCAL(tty) ? 1 : 0,
+                                   (unsigned long *) arg);
+                       return 0;
+               case TIOCSSOFTCAR:
+                       arg = get_fs_long((unsigned long *) arg);
+                       tty->termios->c_cflag =
+                               ((tty->termios->c_cflag & ~CLOCAL) |
+                                (arg ? CLOCAL : 0));
+                       return 0;
+               case TIOCMGET:
+                       error = verify_area(VERIFY_WRITE, (void *) arg,
+                               sizeof(unsigned int));
+                       if (error)
+                               return error;
+                       return get_modem_info(info, (unsigned int *) arg);
+               case TIOCMBIS:
+               case TIOCMBIC:
+               case TIOCMSET:
+                       return set_modem_info(info, cmd, (unsigned int *) arg);
+               case TIOCGSERIAL:
+                       error = verify_area(VERIFY_WRITE, (void *) arg,
+                                               sizeof(struct serial_struct));
+                       if (error)
+                               return error;
+                       return get_serial_info(info,
+                                              (struct serial_struct *) arg);
+               case TIOCSSERIAL:
+                       return set_serial_info(info,
+                                              (struct serial_struct *) arg);
+               case TIOCSERGETLSR: /* Get line status register */
+                       error = verify_area(VERIFY_WRITE, (void *) arg,
+                               sizeof(unsigned int));
+                       if (error)
+                               return error;
+                       else
+                           return get_lsr_info(info, (unsigned int *) arg);
+
+               case TIOCSERGSTRUCT:
+                       error = verify_area(VERIFY_WRITE, (void *) arg,
+                                               sizeof(struct mac_serial));
+                       if (error)
+                               return error;
+                       memcpy_tofs((struct mac_serial *) arg,
+                                   info, sizeof(struct mac_serial));
+                       return 0;
+
+               default:
+                       return -ENOIOCTLCMD;
+               }
+       return 0;
+}
+
+static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios)
+{
+       struct mac_serial *info = (struct mac_serial *)tty->driver_data;
+       int was_stopped;
+
+       if (tty->termios->c_cflag == old_termios->c_cflag)
+               return;
+       was_stopped = info->tx_stopped;
+
+       change_speed(info);
+
+       if (was_stopped && !info->tx_stopped)
+               rs_start(tty);
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_close()
+ *
+ * This routine is called when the serial port gets closed.
+ * Wait for the last remaining data to be sent.
+ * ------------------------------------------------------------
+ */
+static void rs_close(struct tty_struct *tty, struct file * filp)
+{
+       struct mac_serial * info = (struct mac_serial *)tty->driver_data;
+       unsigned long flags;
+       unsigned long timeout;
+
+       if (!info || serial_paranoia_check(info, tty->device, "rs_close"))
+               return;
+
+       save_flags(flags); cli();
+
+       if (tty_hung_up_p(filp)) {
+               restore_flags(flags);
+               return;
+       }
+
+#ifdef SERIAL_DEBUG_OPEN
+       printk("rs_close ttys%d, count = %d\n", info->line, info->count);
+#endif
+       if ((tty->count == 1) && (info->count != 1)) {
+               /*
+                * Uh, oh.  tty->count is 1, which means that the tty
+                * structure will be freed.  Info->count should always
+                * be one in these conditions.  If it's greater than
+                * one, we've got real problems, since it means the
+                * serial port won't be shutdown.
+                */
+               printk("rs_close: bad serial port count; tty->count is 1, "
+                      "info->count is %d\n", info->count);
+               info->count = 1;
+       }
+       if (--info->count < 0) {
+               printk("rs_close: bad serial port count for ttys%d: %d\n",
+                      info->line, info->count);
+               info->count = 0;
+       }
+       if (info->count) {
+               restore_flags(flags);
+               return;
+       }
+       info->flags |= ZILOG_CLOSING;
+       /*
+        * Save the termios structure, since this port may have
+        * separate termios for callout and dialin.
+        */
+       if (info->flags & ZILOG_NORMAL_ACTIVE)
+               info->normal_termios = *tty->termios;
+       if (info->flags & ZILOG_CALLOUT_ACTIVE)
+               info->callout_termios = *tty->termios;
+       /*
+        * Now we wait for the transmit buffer to clear; and we notify
+        * the line discipline to only process XON/XOFF characters.
+        */
+       tty->closing = 1;
+       if (info->closing_wait != ZILOG_CLOSING_WAIT_NONE)
+               tty_wait_until_sent(tty, info->closing_wait);
+       /*
+        * At this point we stop accepting input.  To do this, we
+        * disable the receiver and receive interrupts.
+        */
+       /** if (!info->iscons) ... **/
+       info->curregs[3] &= ~RxENABLE;
+       info->pendregs[3] = info->curregs[3];
+       write_zsreg(info->zs_channel, 3, info->curregs[3]);
+       info->curregs[1] &= ~(0x18);    /* disable any rx ints */
+       info->pendregs[1] = info->curregs[1];
+       write_zsreg(info->zs_channel, 1, info->curregs[1]);
+       ZS_CLEARFIFO(info->zs_channel);
+       if (info->flags & ZILOG_INITIALIZED) {
+               /*
+                * Before we drop DTR, make sure the SCC transmitter
+                * has completely drained.
+                */
+               timeout = jiffies+HZ;
+               while ((read_zsreg(info->zs_channel, 1) & ALL_SNT) == 0) {
+                       current->state = TASK_INTERRUPTIBLE;
+                       current->timeout = jiffies + info->timeout;
+                       schedule();
+                       if (jiffies > timeout)
+                               break;
+               }
+       }
+
+       shutdown(info);
+       if (tty->driver.flush_buffer)
+               tty->driver.flush_buffer(tty);
+       if (tty->ldisc.flush_buffer)
+               tty->ldisc.flush_buffer(tty);
+       tty->closing = 0;
+       info->event = 0;
+       info->tty = 0;
+       if (tty->ldisc.num != ldiscs[N_TTY].num) {
+               if (tty->ldisc.close)
+                       (tty->ldisc.close)(tty);
+               tty->ldisc = ldiscs[N_TTY];
+               tty->termios->c_line = N_TTY;
+               if (tty->ldisc.open)
+                       (tty->ldisc.open)(tty);
+       }
+       if (info->blocked_open) {
+               if (info->close_delay) {
+                       current->state = TASK_INTERRUPTIBLE;
+                       current->timeout = jiffies + info->close_delay;
+                       schedule();
+               }
+               wake_up_interruptible(&info->open_wait);
+       }
+       info->flags &= ~(ZILOG_NORMAL_ACTIVE|ZILOG_CALLOUT_ACTIVE|
+                        ZILOG_CLOSING);
+       wake_up_interruptible(&info->close_wait);
+       restore_flags(flags);
+}
+
+/*
+ * rs_hangup() --- called by tty_hangup() when a hangup is signaled.
+ */
+void rs_hangup(struct tty_struct *tty)
+{
+       struct mac_serial * info = (struct mac_serial *)tty->driver_data;
+
+       if (serial_paranoia_check(info, tty->device, "rs_hangup"))
+               return;
+
+       rs_flush_buffer(tty);
+       shutdown(info);
+       info->event = 0;
+       info->count = 0;
+       info->flags &= ~(ZILOG_NORMAL_ACTIVE|ZILOG_CALLOUT_ACTIVE);
+       info->tty = 0;
+       wake_up_interruptible(&info->open_wait);
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_open() and friends
+ * ------------------------------------------------------------
+ */
+static int block_til_ready(struct tty_struct *tty, struct file * filp,
+                          struct mac_serial *info)
+{
+       struct wait_queue wait = { current, NULL };
+       int             retval;
+       int             do_clocal = 0;
+
+       /*
+        * If the device is in the middle of being closed, then block
+        * until it's done, and then try again.
+        */
+       if (info->flags & ZILOG_CLOSING) {
+               interruptible_sleep_on(&info->close_wait);
+#ifdef SERIAL_DO_RESTART
+               if (info->flags & ZILOG_HUP_NOTIFY)
+                       return -EAGAIN;
+               else
+                       return -ERESTARTSYS;
+#else
+               return -EAGAIN;
+#endif
+       }
+
+       /*
+        * If this is a callout device, then just make sure the normal
+        * device isn't being used.
+        */
+       if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) {
+               if (info->flags & ZILOG_NORMAL_ACTIVE)
+                       return -EBUSY;
+               if ((info->flags & ZILOG_CALLOUT_ACTIVE) &&
+                   (info->flags & ZILOG_SESSION_LOCKOUT) &&
+                   (info->session != current->session))
+                   return -EBUSY;
+               if ((info->flags & ZILOG_CALLOUT_ACTIVE) &&
+                   (info->flags & ZILOG_PGRP_LOCKOUT) &&
+                   (info->pgrp != current->pgrp))
+                   return -EBUSY;
+               info->flags |= ZILOG_CALLOUT_ACTIVE;
+               return 0;
+       }
+
+       /*
+        * If non-blocking mode is set, or the port is not enabled,
+        * then make the check up front and then exit.
+        */
+       if ((filp->f_flags & O_NONBLOCK) ||
+           (tty->flags & (1 << TTY_IO_ERROR))) {
+               if (info->flags & ZILOG_CALLOUT_ACTIVE)
+                       return -EBUSY;
+               info->flags |= ZILOG_NORMAL_ACTIVE;
+               return 0;
+       }
+
+       if (info->flags & ZILOG_CALLOUT_ACTIVE) {
+               if (info->normal_termios.c_cflag & CLOCAL)
+                       do_clocal = 1;
+       } else {
+               if (tty->termios->c_cflag & CLOCAL)
+                       do_clocal = 1;
+       }
+
+       /*
+        * Block waiting for the carrier detect and the line to become
+        * free (i.e., not in use by the callout).  While we are in
+        * this loop, info->count is dropped by one, so that
+        * rs_close() knows when to free things.  We restore it upon
+        * exit, either normal or abnormal.
+        */
+       retval = 0;
+       add_wait_queue(&info->open_wait, &wait);
+#ifdef SERIAL_DEBUG_OPEN
+       printk("block_til_ready before block: ttys%d, count = %d\n",
+              info->line, info->count);
+#endif
+       info->count--;
+       info->blocked_open++;
+       while (1) {
+               cli();
+               if (!(info->flags & ZILOG_CALLOUT_ACTIVE))
+                       zs_rtsdtr(info, 1);
+               sti();
+               current->state = TASK_INTERRUPTIBLE;
+               if (tty_hung_up_p(filp) ||
+                   !(info->flags & ZILOG_INITIALIZED)) {
+#ifdef SERIAL_DO_RESTART
+                       if (info->flags & ZILOG_HUP_NOTIFY)
+                               retval = -EAGAIN;
+                       else
+                               retval = -ERESTARTSYS;
+#else
+                       retval = -EAGAIN;
+#endif
+                       break;
+               }
+               if (!(info->flags & ZILOG_CALLOUT_ACTIVE) &&
+                   !(info->flags & ZILOG_CLOSING) && do_clocal)
+                       break;
+               if (current->signal & ~current->blocked) {
+                       retval = -ERESTARTSYS;
+                       break;
+               }
+#ifdef SERIAL_DEBUG_OPEN
+               printk("block_til_ready blocking: ttys%d, count = %d\n",
+                      info->line, info->count);
+#endif
+               schedule();
+       }
+       current->state = TASK_RUNNING;
+       remove_wait_queue(&info->open_wait, &wait);
+       if (!tty_hung_up_p(filp))
+               info->count++;
+       info->blocked_open--;
+#ifdef SERIAL_DEBUG_OPEN
+       printk("block_til_ready after blocking: ttys%d, count = %d\n",
+              info->line, info->count);
+#endif
+       if (retval)
+               return retval;
+       info->flags |= ZILOG_NORMAL_ACTIVE;
+       return 0;
+}
+
+/*
+ * This routine is called whenever a serial port is opened.  It
+ * enables interrupts for a serial port, linking in its ZILOG structure into
+ * the IRQ chain.   It also performs the serial-specific
+ * initialization for the tty structure.
+ */
+int rs_open(struct tty_struct *tty, struct file * filp)
+{
+       struct mac_serial       *info;
+       int                     retval, line;
+
+       line = MINOR(tty->device) - tty->driver.minor_start;
+       if ((line < 0) || (line >= zs_channels_found))
+               return -ENODEV;
+       info = zs_soft + line;
+
+       /* Is the kgdb running over this line? */
+       if (info->kgdb_channel)
+               return -ENODEV;
+       if (serial_paranoia_check(info, tty->device, "rs_open"))
+               return -ENODEV;
+#ifdef SERIAL_DEBUG_OPEN
+       printk("rs_open %s%d, count = %d\n", tty->driver.name, info->line,
+              info->count);
+#endif
+
+       info->count++;
+       tty->driver_data = info;
+       info->tty = tty;
+
+       /*
+        * Start up serial port
+        */
+       retval = startup(info);
+       if (retval)
+               return retval;
+
+       retval = block_til_ready(tty, filp, info);
+       if (retval) {
+#ifdef SERIAL_DEBUG_OPEN
+               printk("rs_open returning after block_til_ready with %d\n",
+                      retval);
+#endif
+               return retval;
+       }
+
+       if ((info->count == 1) && (info->flags & ZILOG_SPLIT_TERMIOS)) {
+               if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
+                       *tty->termios = info->normal_termios;
+               else
+                       *tty->termios = info->callout_termios;
+               change_speed(info);
+       }
+
+       info->session = current->session;
+       info->pgrp = current->pgrp;
+
+#ifdef SERIAL_DEBUG_OPEN
+       printk("rs_open ttys%d successful...", info->line);
+#endif
+       return 0;
+}
+
+/* Finally, routines used to initialize the serial driver. */
+
+#ifdef CONFIG_MAC
+
+/*
+ *     MacII hardcoded - will be in the hwtab
+ */
+
+static void show_serial_version(void)
+{
+       printk("Mac68K Z8530 serial driver version 1.00\n");
+}
+
+static void probe_sccs(void)
+{
+       int n;
+#define ZS_CONTROL     0x50F04000
+#define ZS_DATA                (ZS_CONTROL+4)
+#define ZS_IRQ         5
+#define ZS_MOVE                3
+       for(n=0;n<2;n++)
+       {
+               zs_channels[n].control = (volatile unsigned char *)
+                               ZS_CONTROL+ZS_MOVE*n;
+               zs_channels[n].data = (volatile unsigned char *)ZS_DATA+ZS_MOVE*n;
+               zs_soft[n].zs_channel = &zs_channels[n];
+               zs_soft[n].irq = ZS_IRQ;
+#if 0
+               if (request_irq(ch->intrs[0], rs_interrupt, 0,
+                               "SCC", &zs_soft[n]))
+                       panic("macserial: can't get irq %d",
+                             ch->intrs[0]);
+#endif
+               if (n & 1)
+                       zs_soft[n].zs_chan_a = &zs_channels[n-1];
+               else
+                       zs_soft[n].zs_chan_a = &zs_channels[n];
+       }
+       if(request_irq(ZS_IRQ, rs_interrupt, 0, "SCC", &zs_soft[n]))
+               panic("macserial: can't get irq %d", ZS_IRQ);
+}
+
+#else
+
+/*
+ *     PowerMAC - query the PROM
+ */
+
+static void show_serial_version(void)
+{
+       printk("PowerMac Z8530 serial driver version 1.00\n");
+}
+
+/* Ask the PROM how many Z8530s we have and initialize their zs_channels */
+static void
+probe_sccs()
+{
+       struct device_node *dev, *ch;
+       struct mac_serial **pp;
+       int n;
+
+       n = 0;
+       pp = &zs_chain;
+       for (dev = find_devices("escc"); dev != 0; dev = dev->next) {
+               if (n >= NUM_CHANNELS) {
+                       printk("Sorry, can't use %s: no more channels\n",
+                              dev->full_name);
+                       continue;
+               }
+               for (ch = dev->child; ch != 0; ch = ch->sibling) {
+                       if (ch->n_addrs < 1 || ch ->n_intrs < 1) {
+                               printk("Can't use %s: %d addrs %d intrs\n",
+                                     ch->full_name, ch->n_addrs, ch->n_intrs);
+                               continue;
+                       }
+                       zs_channels[n].control = (volatile unsigned char *)
+                               ch->addrs[0].address;
+                       zs_channels[n].data = zs_channels[n].control
+                               + ch->addrs[0].size / 2;
+                       zs_soft[n].zs_channel = &zs_channels[n];
+                       zs_soft[n].irq = ch->intrs[0];
+                       if (request_irq(ch->intrs[0], rs_interrupt, 0,
+                                       "SCC", &zs_soft[n]))
+                               panic("macserial: can't get irq %d",
+                                     ch->intrs[0]);
+                       /* XXX this assumes the prom puts chan A before B */
+                       if (n & 1)
+                               zs_soft[n].zs_chan_a = &zs_channels[n-1];
+                       else
+                               zs_soft[n].zs_chan_a = &zs_channels[n];
+
+                       *pp = &zs_soft[n];
+                       pp = &zs_soft[n].zs_next;
+                       ++n;
+               }
+       }
+       *pp = 0;
+       zs_channels_found = n;
+}
+
+#endif
+
+extern void register_console(void (*proc)(const char *));
+
+static inline void
+rs_cons_check(struct mac_serial *ss, int channel)
+{
+       int i, o, io;
+       static consout_registered = 0;
+       static msg_printed = 0;
+
+       i = o = io = 0;
+
+       /* Is this one of the serial console lines? */
+       if ((zs_cons_chanout != channel) &&
+           (zs_cons_chanin != channel))
+               return;
+       zs_conschan = ss->zs_channel;
+       zs_consinfo = ss;
+
+       /* Register the console output putchar, if necessary */
+       if (zs_cons_chanout == channel) {
+               o = 1;
+               /* double whee.. */
+               if (!consout_registered) {
+                       register_console(zs_console_print);
+                       consout_registered = 1;
+               }
+       }
+
+       if (zs_cons_chanin == channel) {
+               i = 1;
+       }
+       if (o && i)
+               io = 1;
+       if (ss->zs_baud != 9600)
+               panic("Console baud rate weirdness");
+
+       /* Set flag variable for this port so that it cannot be
+        * opened for other uses by accident.
+        */
+       ss->is_cons = 1;
+
+       if (io) {
+               if(!msg_printed) {
+                       printk("zs%d: console I/O\n", ((channel>>1)&1));
+                       msg_printed = 1;
+               }
+       } else {
+               printk("zs%d: console %s\n", ((channel>>1)&1),
+                      (i==1 ? "input" : (o==1 ? "output" : "WEIRD")));
+       }
+}
+
+volatile int test_done;
+
+/* rs_init inits the driver */
+int rs_init(void)
+{
+       int channel, i;
+       unsigned long flags;
+       struct mac_serial *info;
+
+       /* Setup base handler, and timer table. */
+       init_bh(SERIAL_BH, do_serial_bh);
+       timer_table[RS_TIMER].fn = rs_timer;
+       timer_table[RS_TIMER].expires = 0;
+
+       /* Find out how many Z8530 SCCs we have */
+       if (zs_chain == 0)
+               probe_sccs();
+
+       show_serial_version();
+
+       /* Initialize the tty_driver structure */
+       /* Not all of this is exactly right for us. */
+
+       memset(&serial_driver, 0, sizeof(struct tty_driver));
+       serial_driver.magic = TTY_DRIVER_MAGIC;
+       serial_driver.name = "ttyS";
+       serial_driver.major = TTY_MAJOR;
+       serial_driver.minor_start = 64;
+       serial_driver.num = zs_channels_found;
+       serial_driver.type = TTY_DRIVER_TYPE_SERIAL;
+       serial_driver.subtype = SERIAL_TYPE_NORMAL;
+       serial_driver.init_termios = tty_std_termios;
+
+       serial_driver.init_termios.c_cflag =
+               B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+       serial_driver.flags = TTY_DRIVER_REAL_RAW;
+       serial_driver.refcount = &serial_refcount;
+       serial_driver.table = serial_table;
+       serial_driver.termios = serial_termios;
+       serial_driver.termios_locked = serial_termios_locked;
+
+       serial_driver.open = rs_open;
+       serial_driver.close = rs_close;
+       serial_driver.write = rs_write;
+       serial_driver.flush_chars = rs_flush_chars;
+       serial_driver.write_room = rs_write_room;
+       serial_driver.chars_in_buffer = rs_chars_in_buffer;
+       serial_driver.flush_buffer = rs_flush_buffer;
+       serial_driver.ioctl = rs_ioctl;
+       serial_driver.throttle = rs_throttle;
+       serial_driver.unthrottle = rs_unthrottle;
+       serial_driver.set_termios = rs_set_termios;
+       serial_driver.stop = rs_stop;
+       serial_driver.start = rs_start;
+       serial_driver.hangup = rs_hangup;
+
+       /*
+        * The callout device is just like normal device except for
+        * major number and the subtype code.
+        */
+       callout_driver = serial_driver;
+       callout_driver.name = "cua";
+       callout_driver.major = TTYAUX_MAJOR;
+       callout_driver.subtype = SERIAL_TYPE_CALLOUT;
+
+       if (tty_register_driver(&serial_driver))
+               panic("Couldn't register serial driver\n");
+       if (tty_register_driver(&callout_driver))
+               panic("Couldn't register callout driver\n");
+
+       save_flags(flags); cli();
+
+       for (channel = 0; channel < zs_channels_found; ++channel) {
+               zs_soft[channel].clk_divisor = 16;
+               zs_soft[channel].zs_baud = get_zsbaud(&zs_soft[channel]);
+
+               /* If console serial line, then enable interrupts. */
+               if (zs_soft[channel].is_cons) {
+                       write_zsreg(zs_soft[channel].zs_channel, R1,
+                                   (EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB));
+                       write_zsreg(zs_soft[channel].zs_channel, R9,
+                                   (NV | MIE));
+                       write_zsreg(zs_soft[channel].zs_channel, R10, (NRZ));
+                       write_zsreg(zs_soft[channel].zs_channel, R3,
+                                   (Rx8 | RxENABLE));
+                       write_zsreg(zs_soft[channel].zs_channel, R5,
+                                   (Tx8 | TxENAB));
+               }
+               /* If this is the kgdb line, enable interrupts because we
+                * now want to receive the 'control-c' character from the
+                * client attached to us asynchronously.
+                */
+               if (zs_soft[channel].kgdb_channel)
+                       kgdb_chaninit(&zs_soft[channel], 1,
+                                     zs_soft[channel].zs_baud);
+       }
+
+       for (info = zs_chain, i = 0; info; info = info->zs_next, i++)
+       {
+               info->magic = SERIAL_MAGIC;
+               info->port = (int) info->zs_channel->control;
+               info->line = i;
+               info->tty = 0;
+               info->custom_divisor = 16;
+               info->close_delay = 50;
+               info->closing_wait = 3000;
+               info->x_char = 0;
+               info->event = 0;
+               info->count = 0;
+               info->blocked_open = 0;
+               info->tqueue.routine = do_softint;
+               info->tqueue.data = info;
+               info->tqueue_hangup.routine = do_serial_hangup;
+               info->tqueue_hangup.data = info;
+               info->callout_termios =callout_driver.init_termios;
+               info->normal_termios = serial_driver.init_termios;
+               info->open_wait = 0;
+               info->close_wait = 0;
+               printk("tty%02d at 0x%08x (irq = %d)", info->line,
+                      info->port, info->irq);
+#ifdef CONFIG_MAC
+               printk(" is a Z8530 ESCC\n");
+#else
+               printk(" is a Z8530 SCC\n");
+#endif
+
+       }
+
+       restore_flags(flags);
+
+       return 0;
+}
+
+/*
+ * register_serial and unregister_serial allows for serial ports to be
+ * configured at run-time, to support PCMCIA modems.
+ */
+/* PowerMac: Unused at this time, just here to make things link. */
+int register_serial(struct serial_struct *req)
+{
+       return -1;
+}
+
+void unregister_serial(int line)
+{
+       return;
+}
+
+/* Hooks for running a serial console.  con_init() calls this if the
+ * console is being run over one of the serial ports.
+ * 'channel' is decoded as 0=modem 1=printer, 'chip' is ignored.
+ */
+void
+rs_cons_hook(int chip, int out, int channel)
+{
+       if (zs_chain == 0)
+               probe_sccs();
+       zs_soft[channel].clk_divisor = 16;
+       zs_soft[channel].zs_baud = get_zsbaud(&zs_soft[channel]);
+       rs_cons_check(&zs_soft[channel], channel);
+       if (out)
+               zs_cons_chanout = channel;
+       else
+               zs_cons_chanin = channel;
+
+}
+
+/* This is called at boot time to prime the kgdb serial debugging
+ * serial line.  The 'tty_num' argument is 0 for /dev/ttyS0 and 1
+ * for /dev/ttyS1 which is determined in setup_arch() from the
+ * boot command line flags.
+ */
+void
+rs_kgdb_hook(int tty_num)
+{
+       if (zs_chain == 0)
+               probe_sccs();
+       zs_kgdbchan = zs_soft[tty_num].zs_channel;
+       zs_soft[tty_num].clk_divisor = 16;
+       zs_soft[tty_num].zs_baud = get_zsbaud(&zs_soft[tty_num]);
+       zs_soft[tty_num].kgdb_channel = 1;     /* This runs kgdb */
+       zs_soft[tty_num ^ 1].kgdb_channel = 0; /* This does not */
+       /* Turn on transmitter/receiver at 8-bits/char */
+       kgdb_chaninit(&zs_soft[tty_num], 0, 9600);
+       ZS_CLEARERR(zs_kgdbchan);
+       ZS_CLEARFIFO(zs_kgdbchan);
+}
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/drivers/char/macserial.h linux-2.0.29/drivers/char/macserial.h
--- linux.vanilla/drivers/char/macserial.h      Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/drivers/char/macserial.h       Thu Apr 10 12:43:55 1997
@@ -0,0 +1,407 @@
+/*
+ * macserial.h: Definitions for the Macintosh Z8530 serial driver.
+ *
+ * Adapted from drivers/sbus/char/sunserial.h by Paul Mackerras.
+ *
+ * Copyright (C) 1996 Paul Mackerras ([email protected])
+ * Copyright (C) 1995 David S. Miller ([email protected])
+ */
+#ifndef _MACSERIAL_H
+#define _MACSERIAL_H
+
+#define NUM_ZSREGS    16
+
+struct serial_struct {
+       int     type;
+       int     line;
+       int     port;
+       int     irq;
+       int     flags;
+       int     xmit_fifo_size;
+       int     custom_divisor;
+       int     baud_base;
+       unsigned short  close_delay;
+       char    reserved_char[2];
+       int     hub6;
+       unsigned short  closing_wait; /* time to wait before closing */
+       unsigned short  closing_wait2; /* no longer used... */
+       int     reserved[4];
+};
+
+/*
+ * For the close wait times, 0 means wait forever for serial port to
+ * flush its output.  65535 means don't wait at all.
+ */
+#define ZILOG_CLOSING_WAIT_INF 0
+#define ZILOG_CLOSING_WAIT_NONE        65535
+
+/*
+ * Definitions for ZILOG_struct (and serial_struct) flags field
+ */
+#define ZILOG_HUP_NOTIFY 0x0001 /* Notify getty on hangups and closes
+                                  on the callout port */
+#define ZILOG_FOURPORT  0x0002 /* Set OU1, OUT2 per AST Fourport settings */
+#define ZILOG_SAK      0x0004  /* Secure Attention Key (Orange book) */
+#define ZILOG_SPLIT_TERMIOS 0x0008 /* Separate termios for dialin/callout */
+
+#define ZILOG_SPD_MASK 0x0030
+#define ZILOG_SPD_HI   0x0010  /* Use 56000 instead of 38400 bps */
+
+#define ZILOG_SPD_VHI  0x0020  /* Use 115200 instead of 38400 bps */
+#define ZILOG_SPD_CUST 0x0030  /* Use user-specified divisor */
+
+#define ZILOG_SKIP_TEST        0x0040 /* Skip UART test during autoconfiguration */
+#define ZILOG_AUTO_IRQ  0x0080 /* Do automatic IRQ during autoconfiguration */
+#define ZILOG_SESSION_LOCKOUT 0x0100 /* Lock out cua opens based on session */
+#define ZILOG_PGRP_LOCKOUT    0x0200 /* Lock out cua opens based on pgrp */
+#define ZILOG_CALLOUT_NOHUP   0x0400 /* Don't do hangups for cua device */
+
+#define ZILOG_FLAGS    0x0FFF  /* Possible legal ZILOG flags */
+#define ZILOG_USR_MASK 0x0430  /* Legal flags that non-privileged
+                                * users can set or reset */
+
+/* Internal flags used only by kernel/chr_drv/serial.c */
+#define ZILOG_INITIALIZED      0x80000000 /* Serial port was initialized */
+#define ZILOG_CALLOUT_ACTIVE   0x40000000 /* Call out device is active */
+#define ZILOG_NORMAL_ACTIVE    0x20000000 /* Normal device is active */
+#define ZILOG_BOOT_AUTOCONF    0x10000000 /* Autoconfigure port on bootup */
+#define ZILOG_CLOSING          0x08000000 /* Serial port is closing */
+#define ZILOG_CTS_FLOW         0x04000000 /* Do CTS flow control */
+#define ZILOG_CHECK_CD         0x02000000 /* i.e., CLOCAL */
+
+/* Software state per channel */
+
+#ifdef __KERNEL__
+/*
+ * This is our internal structure for each serial port's state.
+ *
+ * Many fields are paralleled by the structure used by the serial_struct
+ * structure.
+ *
+ * For definitions of the flags field, see tty.h
+ */
+
+struct mac_zschannel {
+       volatile unsigned char *control;
+       volatile unsigned char *data;
+};
+
+struct mac_serial {
+       struct mac_serial *zs_next;     /* For IRQ servicing chain */
+       struct mac_zschannel *zs_channel; /* Channel registers */
+       struct mac_zschannel *zs_chan_a;        /* A side registers */
+       unsigned char read_reg_zero;
+
+       char soft_carrier;  /* Use soft carrier on this channel */
+       char break_abort;   /* Is serial console in, so process brk/abrt */
+       char kgdb_channel;  /* Kgdb is running on this channel */
+       char is_cons;       /* Is this our console. */
+       unsigned char tx_active; /* character is being xmitted */
+       unsigned char tx_stopped; /* output is suspended */
+
+       /* We need to know the current clock divisor
+        * to read the bps rate the chip has currently
+        * loaded.
+        */
+       unsigned char clk_divisor;  /* May be 1, 16, 32, or 64 */
+       int zs_baud;
+
+       /* Current write register values */
+       unsigned char curregs[NUM_ZSREGS];
+
+       /* Values we need to set next opportunity */
+       unsigned char pendregs[NUM_ZSREGS];
+
+       char change_needed;
+
+       int                     magic;
+       int                     baud_base;
+       int                     port;
+       int                     irq;
+       int                     flags;          /* defined in tty.h */
+       int                     type;           /* UART type */
+       struct tty_struct       *tty;
+       int                     read_status_mask;
+       int                     ignore_status_mask;
+       int                     timeout;
+       int                     xmit_fifo_size;
+       int                     custom_divisor;
+       int                     x_char; /* xon/xoff character */
+       int                     close_delay;
+       unsigned short          closing_wait;
+       unsigned short          closing_wait2;
+       unsigned long           event;
+       unsigned long           last_active;
+       int                     line;
+       int                     count;      /* # of fd on device */
+       int                     blocked_open; /* # of blocked opens */
+       long                    session; /* Session of opening process */
+       long                    pgrp; /* pgrp of opening process */
+       unsigned char           *xmit_buf;
+       int                     xmit_head;
+       int                     xmit_tail;
+       int                     xmit_cnt;
+       struct tq_struct        tqueue;
+       struct tq_struct        tqueue_hangup;
+       struct termios          normal_termios;
+       struct termios          callout_termios;
+       struct wait_queue       *open_wait;
+       struct wait_queue       *close_wait;
+};
+
+
+#define SERIAL_MAGIC 0x5301
+
+/*
+ * The size of the serial xmit buffer is 1 page, or 4096 bytes
+ */
+#define SERIAL_XMIT_SIZE 4096
+
+/*
+ * Events are used to schedule things to happen at timer-interrupt
+ * time, instead of at rs interrupt time.
+ */
+#define RS_EVENT_WRITE_WAKEUP  0
+
+#endif /* __KERNEL__ */
+
+/* Conversion routines to/from brg time constants from/to bits
+ * per second.
+ */
+#define BRG_TO_BPS(brg, freq) ((freq) / 2 / ((brg) + 2))
+#define BPS_TO_BRG(bps, freq) ((((freq) + (bps)) / (2 * (bps))) - 2)
+
+/* The Zilog register set */
+
+#define        FLAG    0x7e
+
+/* Write Register 0 */
+#define        R0      0               /* Register selects */
+#define        R1      1
+#define        R2      2
+#define        R3      3
+#define        R4      4
+#define        R5      5
+#define        R6      6
+#define        R7      7
+#define        R8      8
+#define        R9      9
+#define        R10     10
+#define        R11     11
+#define        R12     12
+#define        R13     13
+#define        R14     14
+#define        R15     15
+
+#define        NULLCODE        0       /* Null Code */
+#define        POINT_HIGH      0x8     /* Select upper half of registers */
+#define        RES_EXT_INT     0x10    /* Reset Ext. Status Interrupts */
+#define        SEND_ABORT      0x18    /* HDLC Abort */
+#define        RES_RxINT_FC    0x20    /* Reset RxINT on First Character */
+#define        RES_Tx_P        0x28    /* Reset TxINT Pending */
+#define        ERR_RES         0x30    /* Error Reset */
+#define        RES_H_IUS       0x38    /* Reset highest IUS */
+
+#define        RES_Rx_CRC      0x40    /* Reset Rx CRC Checker */
+#define        RES_Tx_CRC      0x80    /* Reset Tx CRC Checker */
+#define        RES_EOM_L       0xC0    /* Reset EOM latch */
+
+/* Write Register 1 */
+
+#define        EXT_INT_ENAB    0x1     /* Ext Int Enable */
+#define        TxINT_ENAB      0x2     /* Tx Int Enable */
+#define        PAR_SPEC        0x4     /* Parity is special condition */
+
+#define        RxINT_DISAB     0       /* Rx Int Disable */
+#define        RxINT_FCERR     0x8     /* Rx Int on First Character Only or Error */
+#define        INT_ALL_Rx      0x10    /* Int on all Rx Characters or error */
+#define        INT_ERR_Rx      0x18    /* Int on error only */
+
+#define        WT_RDY_RT       0x20    /* Wait/Ready on R/T */
+#define        WT_FN_RDYFN     0x40    /* Wait/FN/Ready FN */
+#define        WT_RDY_ENAB     0x80    /* Wait/Ready Enable */
+
+/* Write Register #2 (Interrupt Vector) */
+
+/* Write Register 3 */
+
+#define        RxENABLE        0x1     /* Rx Enable */
+#define        SYNC_L_INH      0x2     /* Sync Character Load Inhibit */
+#define        ADD_SM          0x4     /* Address Search Mode (SDLC) */
+#define        RxCRC_ENAB      0x8     /* Rx CRC Enable */
+#define        ENT_HM          0x10    /* Enter Hunt Mode */
+#define        AUTO_ENAB       0x20    /* Auto Enables */
+#define        Rx5             0x0     /* Rx 5 Bits/Character */
+#define        Rx7             0x40    /* Rx 7 Bits/Character */
+#define        Rx6             0x80    /* Rx 6 Bits/Character */
+#define        Rx8             0xc0    /* Rx 8 Bits/Character */
+#define RxNBITS_MASK   0xc0
+
+/* Write Register 4 */
+
+#define        PAR_ENA         0x1     /* Parity Enable */
+#define        PAR_EVEN        0x2     /* Parity Even/Odd* */
+
+#define        SYNC_ENAB       0       /* Sync Modes Enable */
+#define        SB1             0x4     /* 1 stop bit/char */
+#define        SB15            0x8     /* 1.5 stop bits/char */
+#define        SB2             0xc     /* 2 stop bits/char */
+#define SB_MASK                0xc
+
+#define        MONSYNC         0       /* 8 Bit Sync character */
+#define        BISYNC          0x10    /* 16 bit sync character */
+#define        SDLC            0x20    /* SDLC Mode (01111110 Sync Flag) */
+#define        EXTSYNC         0x30    /* External Sync Mode */
+
+#define        X1CLK           0x0     /* x1 clock mode */
+#define        X16CLK          0x40    /* x16 clock mode */
+#define        X32CLK          0x80    /* x32 clock mode */
+#define        X64CLK          0xC0    /* x64 clock mode */
+#define XCLK_MASK      0xC0
+
+/* Write Register 5 */
+
+#define        TxCRC_ENAB      0x1     /* Tx CRC Enable */
+#define        RTS             0x2     /* RTS */
+#define        SDLC_CRC        0x4     /* SDLC/CRC-16 */
+#define        TxENAB          0x8     /* Tx Enable */
+#define        SND_BRK         0x10    /* Send Break */
+#define        Tx5             0x0     /* Tx 5 bits (or less)/character */
+#define        Tx7             0x20    /* Tx 7 bits/character */
+#define        Tx6             0x40    /* Tx 6 bits/character */
+#define        Tx8             0x60    /* Tx 8 bits/character */
+#define TxNBITS_MASK   0x60
+#define        DTR             0x80    /* DTR */
+
+/* Write Register 6 (Sync bits 0-7/SDLC Address Field) */
+
+/* Write Register 7 (Sync bits 8-15/SDLC 01111110) */
+
+/* Write Register 8 (transmit buffer) */
+
+/* Write Register 9 (Master interrupt control) */
+#define        VIS     1       /* Vector Includes Status */
+#define        NV      2       /* No Vector */
+#define        DLC     4       /* Disable Lower Chain */
+#define        MIE     8       /* Master Interrupt Enable */
+#define        STATHI  0x10    /* Status high */
+#define        NORESET 0       /* No reset on write to R9 */
+#define        CHRB    0x40    /* Reset channel B */
+#define        CHRA    0x80    /* Reset channel A */
+#define        FHWRES  0xc0    /* Force hardware reset */
+
+/* Write Register 10 (misc control bits) */
+#define        BIT6    1       /* 6 bit/8bit sync */
+#define        LOOPMODE 2      /* SDLC Loop mode */
+#define        ABUNDER 4       /* Abort/flag on SDLC xmit underrun */
+#define        MARKIDLE 8      /* Mark/flag on idle */
+#define        GAOP    0x10    /* Go active on poll */
+#define        NRZ     0       /* NRZ mode */
+#define        NRZI    0x20    /* NRZI mode */
+#define        FM1     0x40    /* FM1 (transition = 1) */
+#define        FM0     0x60    /* FM0 (transition = 0) */
+#define        CRCPS   0x80    /* CRC Preset I/O */
+
+/* Write Register 11 (Clock Mode control) */
+#define        TRxCXT  0       /* TRxC = Xtal output */
+#define        TRxCTC  1       /* TRxC = Transmit clock */
+#define        TRxCBR  2       /* TRxC = BR Generator Output */
+#define        TRxCDP  3       /* TRxC = DPLL output */
+#define        TRxCOI  4       /* TRxC O/I */
+#define        TCRTxCP 0       /* Transmit clock = RTxC pin */
+#define        TCTRxCP 8       /* Transmit clock = TRxC pin */
+#define        TCBR    0x10    /* Transmit clock = BR Generator output */
+#define        TCDPLL  0x18    /* Transmit clock = DPLL output */
+#define        RCRTxCP 0       /* Receive clock = RTxC pin */
+#define        RCTRxCP 0x20    /* Receive clock = TRxC pin */
+#define        RCBR    0x40    /* Receive clock = BR Generator output */
+#define        RCDPLL  0x60    /* Receive clock = DPLL output */
+#define        RTxCX   0x80    /* RTxC Xtal/No Xtal */
+
+/* Write Register 12 (lower byte of baud rate generator time constant) */
+
+/* Write Register 13 (upper byte of baud rate generator time constant) */
+
+/* Write Register 14 (Misc control bits) */
+#define        BRENABL 1       /* Baud rate generator enable */
+#define        BRSRC   2       /* Baud rate generator source */
+#define        DTRREQ  4       /* DTR/Request function */
+#define        AUTOECHO 8      /* Auto Echo */
+#define        LOOPBAK 0x10    /* Local loopback */
+#define        SEARCH  0x20    /* Enter search mode */
+#define        RMC     0x40    /* Reset missing clock */
+#define        DISDPLL 0x60    /* Disable DPLL */
+#define        SSBR    0x80    /* Set DPLL source = BR generator */
+#define        SSRTxC  0xa0    /* Set DPLL source = RTxC */
+#define        SFMM    0xc0    /* Set FM mode */
+#define        SNRZI   0xe0    /* Set NRZI mode */
+
+/* Write Register 15 (external/status interrupt control) */
+#define        ZCIE    2       /* Zero count IE */
+#define        DCDIE   8       /* DCD IE */
+#define        SYNCIE  0x10    /* Sync/hunt IE */
+#define        CTSIE   0x20    /* CTS IE */
+#define        TxUIE   0x40    /* Tx Underrun/EOM IE */
+#define        BRKIE   0x80    /* Break/Abort IE */
+
+
+/* Read Register 0 */
+#define        Rx_CH_AV        0x1     /* Rx Character Available */
+#define        ZCOUNT          0x2     /* Zero count */
+#define        Tx_BUF_EMP      0x4     /* Tx Buffer empty */
+#define        DCD             0x8     /* DCD */
+#define        SYNC_HUNT       0x10    /* Sync/hunt */
+#define        CTS             0x20    /* CTS */
+#define        TxEOM           0x40    /* Tx underrun */
+#define        BRK_ABRT        0x80    /* Break/Abort */
+
+/* Read Register 1 */
+#define        ALL_SNT         0x1     /* All sent */
+/* Residue Data for 8 Rx bits/char programmed */
+#define        RES3            0x8     /* 0/3 */
+#define        RES4            0x4     /* 0/4 */
+#define        RES5            0xc     /* 0/5 */
+#define        RES6            0x2     /* 0/6 */
+#define        RES7            0xa     /* 0/7 */
+#define        RES8            0x6     /* 0/8 */
+#define        RES18           0xe     /* 1/8 */
+#define        RES28           0x0     /* 2/8 */
+/* Special Rx Condition Interrupts */
+#define        PAR_ERR         0x10    /* Parity error */
+#define        Rx_OVR          0x20    /* Rx Overrun Error */
+#define        FRM_ERR         0x40    /* CRC/Framing Error */
+#define        END_FR          0x80    /* End of Frame (SDLC) */
+
+/* Read Register 2 (channel b only) - Interrupt vector */
+
+/* Read Register 3 (interrupt pending register) ch a only */
+#define        CHBEXT  0x1             /* Channel B Ext/Stat IP */
+#define        CHBTxIP 0x2             /* Channel B Tx IP */
+#define        CHBRxIP 0x4             /* Channel B Rx IP */
+#define        CHAEXT  0x8             /* Channel A Ext/Stat IP */
+#define        CHATxIP 0x10            /* Channel A Tx IP */
+#define        CHARxIP 0x20            /* Channel A Rx IP */
+
+/* Read Register 8 (receive data register) */
+
+/* Read Register 10  (misc status bits) */
+#define        ONLOOP  2               /* On loop */
+#define        LOOPSEND 0x10           /* Loop sending */
+#define        CLK2MIS 0x40            /* Two clocks missing */
+#define        CLK1MIS 0x80            /* One clock missing */
+
+/* Read Register 12 (lower byte of baud rate generator constant) */
+
+/* Read Register 13 (upper byte of baud rate generator constant) */
+
+/* Read Register 15 (value of WR 15) */
+
+/* Misc macros */
+#define ZS_CLEARERR(channel)    (write_zsreg(channel, 0, ERR_RES))
+#define ZS_CLEARFIFO(channel)   do { volatile unsigned char garbage; \
+                                    garbage = read_zsdata(channel); \
+                                    garbage = read_zsdata(channel); \
+                                    garbage = read_zsdata(channel); \
+                               } while(0)
+
+#endif /* !(_MACSERIAL_H) */
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/drivers/char/mem.c linux-2.0.29/drivers/char/mem.c
--- linux.vanilla/drivers/char/mem.c    Thu Nov 14 21:23:58 1996
+++ linux-2.0.29/drivers/char/mem.c     Tue Apr 15 18:54:15 1997
@@ -413,6 +413,9 @@
       gsp_init();
#endif
       tty_init();
+#ifdef CONFIG_MAC
+       nubus_probe_bus();
+#endif
#ifdef CONFIG_PRINTER
       lp_init();
#endif
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/drivers/char/misc.c linux-2.0.29/drivers/char/misc.c
--- linux.vanilla/drivers/char/misc.c   Sat Nov  9 14:07:30 1996
+++ linux-2.0.29/drivers/char/misc.c    Mon Apr 14 15:14:11 1997
@@ -198,6 +198,9 @@
               &proc_misc_read /* get_info */,
       });
#endif /* PROC_FS */
+#ifdef CONFIG_MAC
+       adbdev_init();
+#endif
#ifdef CONFIG_BUSMOUSE
       bus_mouse_init();
#endif
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/drivers/char/tty_io.c linux-2.0.29/drivers/char/tty_io.c
--- linux.vanilla/drivers/char/tty_io.c Sun Nov  3 20:15:41 1996
+++ linux-2.0.29/drivers/char/tty_io.c  Thu Apr 10 14:17:35 1997
@@ -1910,7 +1910,8 @@
#if defined(CONFIG_SERIAL) || defined(CONFIG_ATARI_MFPSER) || \
    defined(CONFIG_ATARI_SCC) || defined(CONFIG_ATARI_MIDI) || \
    defined(CONFIG_AMIGA_BUILTIN_SERIAL) || defined(CONFIG_GVIOEXT) || \
-    defined(CONFIG_MULTIFACE_III_TTY) || defined(CONFIG_USERIAL)
+    defined(CONFIG_MULTIFACE_III_TTY) || defined(CONFIG_USERIAL) || \
+    defined(CONFIG_MAC_SCC)
       rs_init();
#endif
#ifdef CONFIG_SCC
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/drivers/nubus/Makefile linux-2.0.29/drivers/nubus/Makefile
--- linux.vanilla/drivers/nubus/Makefile        Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/drivers/nubus/Makefile Tue Apr 15 16:50:47 1997
@@ -0,0 +1,15 @@
+#
+# Makefile for the nubus specific drivers.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definition is now inherited from the
+# parent makefile.
+#
+
+L_OBJS   := nubus.o
+L_TARGET := nubus.a
+
+include $(TOPDIR)/Rules.make
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/drivers/nubus/nubus.c linux-2.0.29/drivers/nubus/nubus.c
--- linux.vanilla/drivers/nubus/nubus.c Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/drivers/nubus/nubus.c  Wed Apr 16 16:57:52 1997
@@ -0,0 +1,230 @@
+/*
+ *     Macintosh Nubus Interface Code
+ */
+
+#include <linux/config.h>
+#include <linux/ptrace.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/string.h>
+
+#include <linux/nubus.h>
+
+#include <asm/system.h>
+#include <asm/page.h>
+
+static struct nubus_slot nubus_slots[16];
+
+/*
+ *     Please skip to the bottom of this file if you ate lunch recently
+ *                             -- Alan
+ */
+
+
+
+/* This function tests for the presence of an address, specially a
+ * hardware register address. It is called very early in the kernel
+ * initialization process, when the VBR register isn't set up yet. On
+ * an Atari, it still points to address 0, which is unmapped. So a bus
+ * error would cause another bus error while fetching the exception
+ * vector, and the CPU would do nothing at all. So we needed to set up
+ * a temporary VBR and a vector table for the duration of the test.
+ *
+ * See the atari/config.c code we nicked it from for more clues.
+ */
+
+static int hwreg_present( volatile void *regp )
+{
+    int        ret = 0;
+    long       save_sp, save_vbr;
+    long       tmp_vectors[3];
+    unsigned long flags;
+
+    save_flags(flags);
+    cli();
+
+    __asm__ __volatile__
+       (       "movec  %/vbr,%2\n\t"
+               "movel  #Lberr1,%4@(8)\n\t"
+                "movec %4,%/vbr\n\t"
+               "movel  %/sp,%1\n\t"
+               "moveq  #0,%0\n\t"
+               "tstb   %3@\n\t"
+               "nop\n\t"
+               "moveq  #1,%0\n"
+                "Lberr1:\n\t"
+               "movel  %1,%/sp\n\t"
+               "movec  %2,%/vbr"
+               : "=&d" (ret), "=&r" (save_sp), "=&r" (save_vbr)
+               : "a" (regp), "a" (tmp_vectors)
+                );
+    restore_flags(flags);
+    return( ret );
+}
+
+
+
+/*
+ *     Yes this sucks. The ROM can appear on arbitary bytes of the long
+ *     word. We are unamused.
+ */
+
+extern __inline__ int not_useful(void *p, int map)
+{
+       unsigned long pv=(unsigned long)p;
+       pv&=3;
+       if(map&(1<<pv))
+               return 0;
+       return 1;
+}
+
+static unsigned long nubus_get_rom(unsigned char **ptr, int len, int map)
+{
+       unsigned long v=0;
+       unsigned char *p=*ptr;  /* as v|=*((*ptr)++) upset someone */
+       while(len)
+       {
+               v<<=8;
+               while(not_useful(p,map))
+                       p++;
+               v|=*p++;
+               len--;
+       }
+       *ptr=p;
+       return v;
+}
+
+static void nubus_rewind(unsigned char **ptr, int len, int map)
+{
+       unsigned char *p=*ptr;
+       while(len)
+       {
+               do
+               {
+                       p--;
+               }
+               while(not_useful(p,map));
+               len--;
+       }
+       *ptr=p;
+}
+
+static void *nubus_slot_addr(int slot)
+{
+       return (void *)(0xF0000000|(slot<<24));
+}
+
+static void *nubus_rom_addr(int slot)
+{
+       /*
+        *      Returns the first byte after the card. We then walk
+        *      backwards to get the lane register and the config
+        */
+       return (void *)(0xF1000000+(slot<<24));
+}
+
+void nubus_probe_slot(int slot)
+{
+       unsigned char *rp;
+       unsigned char dp;
+       int lanes;
+       int i;
+       unsigned long dpat;
+
+       /*
+        *      Ok see whats cooking in the bytelanes
+        */
+
+       rp=nubus_rom_addr(slot);
+
+       for(i=4;i;i--)
+       {
+               rp--;
+
+               if(!hwreg_present(rp))
+                       continue;
+
+               dp=*rp;
+
+               if(dp==0)
+                       continue;
+
+               /*
+                *      Valid ?
+                */
+
+               if((((dp>>4)^dp)&0x0F)!=0x0F)
+                       continue;
+
+               if((dp&0x0F) >= 1<<i)
+                       continue;
+
+               /*
+                *      Looks promising
+                */
+
+               nubus_slots[i].slot_flags|=NUBUS_DEVICE_PRESENT;
+               lanes=dp;
+
+               /*
+                *      Time to dig deeper. Find the ROM base
+                *      and read it
+                */
+
+               rp=nubus_rom_addr(slot);
+
+               /*
+                *      Now to make this more fun the ROM is only visible
+                *      on its bytelanes - that is smeared across the address
+                *      space.
+                */
+
+               nubus_rewind(&rp,20,lanes);
+
+               nubus_slots[i].slot_directory   =       nubus_get_rom(&rp,4,lanes);
+               nubus_slots[i].slot_dlength     =       nubus_get_rom(&rp,4,lanes);
+               nubus_slots[i].slot_crc         =       nubus_get_rom(&rp,4,lanes);
+               nubus_slots[i].slot_rev         =       nubus_get_rom(&rp,1,lanes);
+               nubus_slots[i].slot_format      =       nubus_get_rom(&rp,1,lanes);
+
+               dpat=nubus_get_rom(&rp,4,lanes);
+
+               /*
+                *      Ok now check what we got
+                */
+
+               if(!(nubus_slots[i].slot_directory&0x00FF0000))
+                       printk("Dodgy doffset ??\n");
+               if(dpat!=0x5A932BC7)
+                       printk("Wrong test pattern %lx\n",dpat);
+
+               /*
+                *      I wonder how the CRC is meant to work -
+                *              any takers ?
+                */
+
+               printk("nubus%d: seems to have a toy in it\n",slot);
+
+               /*
+                *      Now parse the directories on the card
+                */
+
+
+
+               return;
+       }
+       printk("nubus%d: empty\n", slot);
+}
+
+
+void nubus_probe_bus(void)
+{
+       int i;
+       printk("Nubus system bus at %p\n", nubus_slot_addr(9));
+       for(i=9;i<15;i++)
+       {
+               nubus_probe_slot(i);
+       }
+}
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/fs/Config.in linux-2.0.29/fs/Config.in
--- linux.vanilla/fs/Config.in  Wed Dec  4 22:37:16 1996
+++ linux-2.0.29/fs/Config.in   Thu Mar 27 14:19:24 1997
@@ -45,6 +45,11 @@
if [ "$CONFIG_AFFS_FS" != "n" ]; then
  define_bool CONFIG_AMIGA_PARTITION y
fi
+tristate 'Macintosh HFS filesystem support' CONFIG_HFS_FS
+if [ "$CONFIG_HFS_FS" != "n" ]; then
+  define_bool CONFIG_MAC_PARTITION y
+fi
+
tristate 'UFS filesystem support (read only)' CONFIG_UFS_FS
if [ "$CONFIG_UFS_FS" != "n" ]; then
  bool 'BSD disklabel (FreeBSD partition tables) support' CONFIG_BSD_DISKLABEL
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/fs/Makefile linux-2.0.29/fs/Makefile
--- linux.vanilla/fs/Makefile   Sat Oct  5 14:25:50 1996
+++ linux-2.0.29/fs/Makefile    Thu Mar 27 14:20:09 1997
@@ -17,7 +17,7 @@

MOD_LIST_NAME := FS_MODULES
ALL_SUB_DIRS = minix ext ext2 fat msdos vfat proc isofs nfs xiafs umsdos \
-               hpfs sysv smbfs ncpfs ufs affs
+               hpfs sysv smbfs ncpfs ufs affs hfs

ifeq ($(CONFIG_QUOTA),y)
O_OBJS += dquot.o
@@ -146,6 +146,14 @@
else
  ifeq ($(CONFIG_UFS_FS),m)
  MOD_SUB_DIRS += ufs
+  endif
+endif
+
+ifeq ($(CONFIG_HFS_FS),y)
+SUB_DIRS += hfs
+else
+  ifeq ($(CONFIG_UFS_FS),m)
+  MOD_SUB_DIRS += hfs
  endif
endif

diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/fs/filesystems.c linux-2.0.29/fs/filesystems.c
--- linux.vanilla/fs/filesystems.c      Sat Oct  5 14:25:52 1996
+++ linux-2.0.29/fs/filesystems.c       Thu Mar 27 17:55:18 1997
@@ -24,6 +24,7 @@
#include <linux/ncp_fs.h>
#include <linux/affs_fs.h>
#include <linux/ufs_fs.h>
+#include <linux/hfs.h>
#include <linux/major.h>

extern void device_setup(void);
@@ -108,6 +109,9 @@

#ifdef CONFIG_UFS_FS
       init_ufs_fs();
+#endif
+#ifdef CONFIG_HFS_FS
+       init_hfs_fs();
#endif

       mount_root();
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/fs/hfs/BUG_INFO linux-2.0.29/fs/hfs/BUG_INFO
--- linux.vanilla/fs/hfs/BUG_INFO       Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/fs/hfs/BUG_INFO        Wed Apr  9 04:41:47 1997
@@ -0,0 +1,23 @@
+#!/bin/sh
+#
+# This script attempt to collect some of the
+# information I need for a complete bug report.
+#
+
+PATH=/sbin:/usr/sbin:/bin:/usr/bin
+export PATH
+
+echo -n "kernel version: "
+uname -r
+
+echo -n "hfs_fs version: "
+grep hfs_version version.c | cut '-d"' -f2
+
+echo -n "gcc version: "
+gcc --version
+
+echo -n "gcc output: "
+file hfs.o | cut -d: -f2-
+
+echo -n "insmod version: "
+insmod -V 2>/dev/null
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/fs/hfs/COPYING linux-2.0.29/fs/hfs/COPYING
--- linux.vanilla/fs/hfs/COPYING        Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/fs/hfs/COPYING Wed Apr  9 04:41:47 1997
@@ -0,0 +1,339 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                          675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19yy name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/fs/hfs/ChangeLog linux-2.0.29/fs/hfs/ChangeLog
--- linux.vanilla/fs/hfs/ChangeLog      Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/fs/hfs/ChangeLog       Wed Apr  9 04:41:47 1997
@@ -0,0 +1,2160 @@
+Wed Apr  9 03:39:05 1997  Paul H. Hargrove  <[email protected]>
+
+       * version.c, README.sgml:
+       Bump version to 0.8.3.
+
+Mon Apr  7 20:09:56 1997  Paul H. Hargrove  <[email protected]>
+
+       * part_tbl.c:
+       Fix to allow bootable CDROMs (which have blocksize != 512) to mount.
+
+       * super.c:
+       Check that blk_size[MAJOR(dev)] is non-NULL before dereferencing.
+
+Sat Apr  5 10:44:42 1997  Paul H. Hargrove  <[email protected]>
+
+       * hfs_btree.h, binsert.c, brec.c, bfind.c, bins_del.c, bdelete.c:
+       Make btree operations less likely to do
+       nasty things if the tree is corrupted.
+
+       * part_tbl.c, README.sgml:
+       Count partitions from 0 rather than from 1.
+
+Wed Apr  2 23:26:51 1997  Paul H. Hargrove  <[email protected]>
+
+       * bdelete.c:
+       Don't bother checking for oversized keys in hfs_bdelete().
+
+       * bdelete.c, bfind.c, binsert.c:
+       Verify key lengths against the maximum given for the tree.
+
+       * Makefile:
+       Check that /usr/include/linux/modversions.h exists before including it.
+       This allows compilation without CONFIG_MODVERSIONS enabled.
+
+Sat Mar 29 13:17:53 1997  Paul H. Hargrove  <[email protected]>
+
+       * linux/hfs_fs.h, super.c, file_hdr.c, hfs.h, extent.c, file_cap.c,
+         dir_dbl.c, dir_nat.c, dir.c, dir_cap.c, binsert.c, catalog.c,
+         bfind.c:
+       Make (struct hfs_bkey) and (struct hfs_brec) more "abstract".
+
+       * binsert.c:
+       Remove redundant test in hfs_binsert().
+
+Sat Mar 29 05:24:23 1997  Paul H. Hargrove  <[email protected]>
+
+       * version.c, README.sgml:
+       Fix formatting problems in README.sgml and bump version to 0.8.2.
+
+       * extent.c:
+       Fix bug that caused serious headaches with fragmented files.
+
+Fri Mar 28 00:23:18 1997  Paul H. Hargrove  <[email protected]>
+
+       * version.c, README.sgml:
+       Bump version to 0.8.1.
+
+       * btree.c, balloc.c:
+       Commit map nodes to buffers when new map nodes are added.
+
+Thu Mar 27 22:41:07 1997  Paul H. Hargrove  <[email protected]>
+
+       * Makefile:
+       Include linux/modversions.h from the gcc command line.
+
+       * mdb.c:
+       Was updating modified date twice in hfs_mdb_commit().
+
+       * linux/hfs_sysdep.h, linux/hfs_fs.h, linux/hfs_fs_i.h,
+         linux/hfs_fs_sb.h, sysdep.c, trans.c, super.c, hfs_sysdep.h, inode.c,
+         hfs_fs_i.h, hfs_fs_sb.h, hfs_fs.h, hfs.h, file_cap.c, file_hdr.c,
+         file.c, dir_nat.c, dir_cap.c, dir_dbl.c, Makefile, dir.c:
+       Rearrange headers in preparation for inclusion in the kernel.
+
+       * hfs_fs_sb.h, hfs_fs.h:
+       Add forward declarations so other code can include these headers.
+
+       * hfs_sysdep.h:
+       Include __constant_hton[ls]() for little-endian machines.
+
+       * hfs_fs.h, hfs_sysdep.h, hfs.h:
+       Move typedefs of hfs_{byte,word,lword}_t from hfs.h to hfs_sysdep.h.
+       Include hfs_sysdep.h from hfs_fs.h.
+
+       * trans.c, super.c, part_tbl.c, string.c, inode.c, mdb.c, hfs_fs_sb.h,
+         hfs_sysdep.h, hfs_fs.h, hfs.h, hfs_btree.h, file_cap.c, file_hdr.c,
+         file.c, dir_nat.c, extent.c, dir_dbl.c, dir.c, dir_cap.c, catalog.c,
+         btree.c, bnode.c, brec.c, bitmap.c, bitops.c, bins_del.c, binsert.c,
+         bdelete.c, bfind.c, balloc.c:
+       Big type system changes in preparation for kernel inclusion:
+       '[US](8|16|32)' -> 'hfs_[us](8|16|32)' (avoids name space pollution)
+       'hfs_name_t' -> 'struct hfs_name' (allows forward declaration)
+
+       * super.c, hfs_fs.h:
+       Add init_hfs_fs() to super.c for non-module compilation.
+
+Wed Mar 26 07:53:59 1997  Paul H. Hargrove  <[email protected]>
+
+       * version.c, README.sgml:
+       Bump version to 0.8.
+
+       * README.sgml:
+       Special compilation note for DEC Alpha.
+
+       * README.sgml:
+       Note status on non-Intel processors.
+
+       * hfs_fs.h:
+       Use long's for read() and write() on the Alpha.
+
+       * README.sgml:
+       Document the afpd mount option.
+
+       * inode.c:
+       Make files always writable for owner in afpd mode.
+
+Tue Mar 25 23:21:39 1997  Paul H. Hargrove  <[email protected]>
+
+       * part_tbl.c:
+       Clean up the error checking code a bit.
+
+Sat Mar 22 19:43:40 1997  Paul H. Hargrove  <[email protected]>
+
+       * part_tbl.c:
+       Fixed uninitialized variable in old-style partition code.
+
+       * bins_del.c, bdelete.c:
+       Fix extraneous "bad argument to shift_{left,right}" messages.
+
+       * bitops.c:
+       Note that these routines are now tested on Intel, PPC and Alpha.
+
+       * Makefile:
+       Add -fno-builtin the the CFLAGS.
+
+Fri Feb 14 10:50:14 1997  Paul H. Hargrove  <[email protected]>
+
+       * hfs_sysdep.h:
+       Don't include <asm/*.h> until after <linux/types.h>.
+
+       * catalog.c:
+       Use volume create date in hashfn() rather than casting pointer to int.
+
+       * hfs.h, mdb.c:
+       Maintaing volume create, modify and backup dates in struct hfs_mdb.
+
+       * hfs_fs.h:
+       Include the header for put_user BEFORE using it!
+
+       * string.c, hfs.h:
+       Make hfs_strhash() return an unsigned int.
+
+       * trans.c, version.c, super.c, mdb.c, part_tbl.c, string.c, inode.c,
+         hfs_sysdep.h, hfs_fs.h, hfs_fs_sb.h, hfs_btree.h, hfs.h, file_cap.c,
+         file_hdr.c, extent.c, dir_dbl.c, dir_nat.c, dir_cap.c, dir.c,
+         catalog.c, btree.c, bnode.c, brec.c, bitmap.c, binsert.c,
+         bins_del.c, bdelete.c, balloc.c, README.sgml, Makefile:
+       Updated copyright notices.
+
+       * trans.c, part_tbl.c, string.c, super.c, inode.c, mdb.c, hfs_fs.h,
+         hfs_fs_sb.h, hfs_sysdep.h, hfs_btree.h, hfs.h, file_cap.c,
+         file_hdr.c, dir_nat.c, extent.c, dir_cap.c, dir_dbl.c, catalog.c,
+         dir.c, brec.c, btree.c, bitmap.c, bnode.c, bdelete.c, bins_del.c,
+         binsert.c, Makefile, TODO, balloc.c:
+       First shot at portability to the DEC Alpha and non-gcc compilers.
+       This invloved a significant overhaul of the type system.
+
+Tue Feb  4 04:26:54 1997  Paul H. Hargrove  <[email protected]>
+
+       * version.c, README.sgml:
+       Bump version to "pre-0.8-4".
+
+       * dir_nat.c:
+       Allow creat() in Netatalk .AppleDouble directories.
+
+       * dir_dbl.c:
+       Make local functions static.
+
+       * dir_dbl.c:
+       Removed unnecessary 'extern' qualifiers from forward declarations.
+
+       * file_hdr.c, TODO:
+       Fixed the 30-year time warp with afpd.
+
+       * TODO, trans.c:
+       Don't mangle the name .AppleDesktop under fork=netatalk.
+
+Mon Feb  3 23:18:45 1997  Paul H. Hargrove  <[email protected]>
+
+       * inode.c:
+       Make header files always writable when the afpd mount option is given.
+       Otherwise it is impossible to unlock a locked file.
+
+       * TODO, inode.c:
+       Let afpd think chmod() always succeeds, so "New Folder" works right.
+
+       * super.c:
+       The 'afpd' mount option now makes 'fork=n,names=n' the default.
+
+       * TODO:
+       List the current known afpd-compatibility problems as bugs.
+
+       * file_hdr.c:
+       Make certain date changes through header files get written to disk.
+
+Sat Feb  1 02:24:12 1997  Paul H. Hargrove  <[email protected]>
+
+       * mdb.c:
+       Work around for Linux rounding device sizes to 1k increments.
+
+       * README.sgml:
+       Fixed a typo: "the a".
+
+Sat Dec 28 20:41:01 1996  Paul H. Hargrove  <[email protected]>
+
+       * TODO:
+       Add ioctl() interface as a "missing feature."
+
+       * dir_nat.c:
+       Finish implementing the afpd-compatibility
+       mode using the new 'afpd' mount option.
+
+       * hfs_fs_sb.h, super.c:
+       Add new 'afpd' mount option.
+
+       * file_cap.c:
+       Spelling fix.
+
+Wed Dec 11 23:16:08 1996  Paul H. Hargrove  <[email protected]>
+
+       * TODO, README.sgml:
+       Optimistically document the hybrid CD problem as fixed.
+
+       * part_tbl.c:
+       Fix the partition code so at least some of the hybrid
+       CDROMs that were previously rejected are now accepted.
+
+       * hfs.h:
+       Make fs_start a 32-bit integer rather than 16-bits.
+       The 16-bit value would overflow if a partition started
+       beyond the 32M mark (e.g. the Executor 2 Beta 1 CDROM).
+
+       * extent.c:
+       Fixed a typo in an error message.
+
+Tue Dec 10 14:43:46 1996  Paul H. Hargrove  <[email protected]>
+
+       * dir_nat.c:
+       Merge in the (still dormant) afpd-compatibility changes.
+
+       * inode.c:
+       Make the .AppleDouble directory writable (again).
+
+       * version.c, README.sgml:
+       Bump version up to "pre-0.8-3".
+
+       * hfs_fs.h, file_cap.c, file_hdr.c:
+       Move AFP constants to hfs_fs.h and prefix them with "HFS_".
+
+       * dir_nat.c, inode.c:
+       Back-out changes that allowed writing to the .AppleDouble directory.
+
+       * Makefile:
+       Update rules for linuxdoc-sgml v1.5.
+
+       * extent.c:
+       Fixed serious bug in decode_extent() with handling of empty extents.
+
+       * file.c:
+       Rewrote hfs_getblk().
+       It will no longer hang if hfs_extent_map() is buggy.
+       Also halves the worst-case number of calls to hfs_extent_map().
+
+       * extent.c:
+       Fixed serious bug in decode_extent() with handling of empty extents.
+
+       * hfs_fs.h:
+       Small change so the PPC (and maybe other architectures?)
+       pick up the prototypes for the user-space access functions.
+
+       * super.c, file_cap.c, file_hdr.c, hfs_fs.h, file.c:
+       Updated for new user-space memory interface.
+
+Sun Dec  8 11:49:36 1996  Paul H. Hargrove  <[email protected]>
+
+       * dir_nat.c:
+       Add special code for unlink(), and rename() in the .AppleDouble
+       directory and rmdir() of the .AppleDouble directory.
+
+       * inode.c:
+       Make the .AppleDouble directory writable.
+
+       * file_hdr.c:
+       Use AFP flags in version 1 headers (for Netatalk compatibility).
+
+       * trans.c:
+       Fixed bug with long names causing kernel Oops.
+
+Mon Oct  7 06:05:01 1996  Paul H. Hargrove  <[email protected]>
+
+       * hfs_fs.h, file_cap.c, file_hdr.c, hfs.h, extent.c, file.c, dir.c:
+       Fix types for various read/write/truncate computations.
+       Also allows compilation with 2.1.x kernels.
+
+Thu Sep 19 10:28:43 1996  Paul H. Hargrove  <[email protected]>
+
+       * README.sgml, version.c:
+       Bump version up to "pre-0.8-2".
+
+       * TODO:
+       Reformat the To Do list introducing prioritized categories.
+
+       * file_hdr.c, file.c:
+       Move comments about mmap() for headers from file.c to file_hdr.c.
+       Also revise the reasoning for not yet having it implemented.
+
+       * dir_nat.c, dir_cap.c, dir_dbl.c:
+       Remove 'hfs_' prefix from names of some purely local functions.
+
+       * dir_dbl.c, TODO:
+       Under AppleDouble make create(), mkdir(), mknod(), unlink(), rename()
+       and rename() check against header files when arguments start with '%'.
+
+       * super.c, hfs_fs_sb.h, hfs_fs.h, dir_dbl.c, dir_nat.c, dir_cap.c,
+         dir.c, README.sgml:
+       Fix problem that prevented creating %RootInfo or .rootinfo in all
+       directories in addition to preventing deletion from the root directory.
+
+       * TODO:
+       Remove writable header files from the To Do list.
+
+       * README.sgml:
+       Add extensive discussion of writing to HFS filesystems and
+       the format of the special files.
+
+       * file_hdr.c:
+       Generate the 'homefs' field for version 1 header files.
+
+Wed Sep 18 23:07:45 1996  Paul H. Hargrove  <[email protected]>
+
+       * hfs_fs.h, file_cap.c:
+       Comment the definition of (struct hfs_cap_info).
+
+       * version.c, README.sgml:
+       Bump version up to "pre-0.8-1" and update the "How can I write?" FAQ.
+
+       * file_hdr.c:
+       Implement hdr_write() and hdr_truncate()!!
+
+       * hfs_fs_i.h, inode.c:
+       Make hdr_layout per-inode (not per-file) so hdr_truncate() will work.
+
+       * file.c, hfs.h, catalog.c, extent.c, balloc.c:
+       hfs_extent_adj() now uses fork->lsize to determine the target file size.
+
+Sun Sep 15 07:55:24 1996  Paul H. Hargrove  <[email protected]>
+
+       * README.sgml, trans.c:
+       Prevent creation of files & directories with '\0' or ':' in their names.
+
+       * string.c, hfs_fs.h, hfs.h, dir_dbl.c, dir_nat.c, dir_cap.c:
+       With case=lower could have run off end of string.
+
+Tue Sep 10 12:05:47 1996  Paul H. Hargrove  <[email protected]>
+
+       * inode.c:
+       Small clean up of HFS_FIL_LOCK handling.
+
+       * inode.c:
+       Fix notify_change() not to accidentally make metadata executable.
+
+       * hfs_fs.h:
+       AppleSingle files should have HFS_ITYPE_NORM.
+
+       * inode.c:
+       Return to old behavior where MdDat = i_mtime.
+
+       * dir_dbl.c:
+       Fix serious bug in hfs_dbl_readdir() that would lock-up access to a
+       directory if one tried to write to a directory they had previously read.
+
+       * file.c:
+       Fix hfs_do_write() to adjust the fork's 'lsize' if it changed.
+
+       * inode.c, file_cap.c:
+       Allow truncate() to be called even on metadata.
+       Any size changes will last only until the next iput() of the inode.
+       Truncating a header file doesn't yet truncate the resource fork.
+
+       * inode.c:
+       Allow chmod() on a directory if it doesn't actually change i_mode.
+
+       * hfs_fs.h, trans.c, super.c:
+       Rename hfs_cap2mac() to hfs_colon2mac().
+       Rename hfs_apl2mac() to hfs_prcnt2mac().
+
+       * file_hdr.c:
+       Move header construction out of hdr_read() to create hdr_build_meta().
+
+       * hfs.h:
+       Add byte-order independent conversions: U32->U16, U32->U8 and U16->U8.
+
+       * file.c, file_cap.c, hfs_fs.h:
+       Rename fix_perms() to hfs_file_fix_mode() and
+       move it from from file_cap.c to file.c.
+
+       * README.sgml, super.c:
+       Make the default for the names mount option vary with the fork option.
+
+       * file_cap.c:
+       The umask was applied incorrectly in fix_perms().
+
+Mon Sep  9 13:11:28 1996  Paul H. Hargrove  <[email protected]>
+
+       * README.sgml:
+       Note that it compiles on m68k machines, but needs more testing.
+
+       * hfs_sysdep.h, Makefile:
+       Changes to compile unmodified on m68k (and possibly other machines).
+
+       * dir_cap.c:
+       hfs_cap_readdir() was mistakenly producing .rootinfo entries for
+       the .finderinfo and .resource subdirectories of the root directory.
+
+       * inode.c:
+       A directory's i_size was too small by 1 under CAP, so hfs_cap_readdir()
+       would omit the last directory entry.  i_nlink was also too large by 1.
+
+Sun Sep  8 12:56:06 1996  Paul H. Hargrove  <[email protected]>
+
+       * file_hdr.c:
+       Rewrite hdr_read() to be more efficient and to deal correctly with
+       descriptors having lengths that differ from the actual size of the data.
+
+       * file_cap.c:
+       Add write support for CAP finderinfo files!!
+
+       * super.c, inode.c, hfs_fs.h, hfs_fs_i.h, hfs_fs_sb.h, file_dbl.c,
+         file_nat.c, file_hdr.c, file.c, file_cap.c, Makefile, dir.c:
+       Generate metadata (header files and CAP finderinfo files) on-the-fly.
+       The files file_{dbl,nat}.c are merged into file_hdr.c as a result.
+
+Sat Sep  7 08:09:24 1996  Paul H. Hargrove  <[email protected]>
+
+       * README.sgml:
+       Fix silly grammatical error.
+
+Fri Sep  6 09:17:12 1996  Paul H. Hargrove  <[email protected]>
+
+       * hfs_fs_sb.h, super.c:
+       No need to cast type of s_reserved.
+
+       * file_dbl.c, file_nat.c, dir_dbl.c, dir_nat.c, file_cap.c, dir_cap.c:
+       Add the missing NULL readpage and writepage entries to the inode_ops.
+
+       * file_dbl.c, file_nat.c, file.c, file_cap.c:
+       Cleanup error checking for read() and write().
+
+Thu Sep  5 05:29:53 1996  Paul H. Hargrove  <[email protected]>
+
+       * version.c, README.sgml:
+       Bump version up to "0.7.2".
+       User-visible changes from 0.7.0:
+       + Corrected CAP finderinfo file format.
+       + Support for more features of CAP finderinfo files.
+       + No longer requires gcc 2.7.0 or newer.
+       + Now implements mknod() system call.
+
+       * hfs_fs.h, dir_nat.c, file_cap.c, file_nat.c, README.sgml, dir_cap.c:
+       Include the CAP and Netatalk copyright notices.
+
+       * hfs_fs.h, file_cap.c:
+       Repair and improve CAP support.
+
+       * catalog.c:
+       Oops! The BkDat for new files and directories was in 1972 when
+       it should have been in 1904 (not that it matters that much).
+
+       * inode.c:
+       The HFS MdDat should be the larger of the i_mtime and i_ctime.
+
+       * README.sgml:
+       Change 'm_time' to 'i_mtime'.
+
+Wed Sep  4 13:27:35 1996  Paul H. Hargrove  <[email protected]>
+
+       * version.c, README.sgml:
+       Bump version up to "0.7.1".
+       User-visible changes from 0.7.0:
+       + Minor bug in CAP finderinfo file format fixed.
+       + No longer requires gcc 2.7.0 or newer.
+       + Now implements mknod() system call.
+
+       * README.sgml:
+       Removed note about needing gcc 2.7.0 or newer.
+
+       * file.c:
+       Optimize hfs_do_read() based on the fact that HFS has no holes in files.
+       Minor code formatting changes.
+
+       * hfs.h, hfs_sysdep.h, mdb.c, extent.c, file.c, btree.c, catalog.c,
+         balloc.c, bnode.c:
+       Reorganize memory management routines.
+       hfs_malloc() and hfs_free() are the main routines.
+       The macros FREE() and MALLOC() are gone.
+       HFS_NEW() and HFS_DELETE() are new 'shorthand' macros.
+
+       * btree.c:
+       Fix broken debugging code.
+
+       * super.c, hfs.h, mdb.c, part_tbl.c, Makefile:
+       Separate partition table handling into its own file.
+
+       * dir.c:
+       Spelling fixes.
+
+       * sysdep.c:
+       Oops!  Error check got sense reversed while editing.
+
+       * mdb.c, sysdep.c, hfs.h, hfs_btree.h, hfs_sysdep.h, btree.c, extent.c,
+         bfind.c, bnode.c, balloc.c:
+       Make hfs_buffer a pointer to a buffer_head, rather than a buffer_head.
+
+       * hfs_fs.h, dir_cap.c, dir_dbl.c, dir_nat.c, dir.c:
+       Add a mknod() entry to the inode_operations for normal directories.
+       All it is good for is letting root create regular files.
+
+       * file_dbl.c, file_nat.c, file.c, file_cap.c, dir_cap.c, dir_dbl.c,
+         dir_nat.c:
+       Add the missing NULL entries to the end of the file_operations.
+
+       * super.c, hfs_btree.h, hfs_fs.h, mdb.c, extent.c, hfs.h, catalog.c:
+       Make the remainder of the (untested) changes
+       to allow compilation with gcc 2.6.3.
+
+       * hfs_fs.h:
+       Fix hfs_fs.h to work with gcc 2.6.3.
+
+       * hfs_fs.h:
+       (struct hfs_cap_info) should never have been 'packed'.
+
+       * BUG_INFO:
+       Use -V for getting version of module utilities.
+
+       * super.c, sysdep.c, trans.c, hfs_fs_sb.h, inode.c, hfs_fs.h,
+         hfs_fs_i.h, file_cap.c, file_dbl.c, file_nat.c, dir_dbl.c,
+         dir_nat.c, file.c, dir.c, dir_cap.c:
+       Fix up hfs_fs{,_i,_sb}.h in preparation for inclusion in kernel.
+
+Tue Sep  3 23:58:03 1996  Paul H. Hargrove  <[email protected]>
+
+       * hfs.h:
+       Change eventual destination to linux/fs/hfs rather than include/linux.
+
+       * super.c, inode.c, mdb.c, hfs_btree.h, hfs_fs.h, hfs_sysdep.h,
+         file_dbl.c, file_nat.c, hfs.h, dir_nat.c, extent.c, dir_dbl.c,
+         catalog.c, dir_cap.c, brec.c, btree.c, binsert.c, bnode.c, bdelete.c,
+         bfind.c, bins_del.c, balloc.c:
+       Replace all the swap{16,32}() stuff w/ ntohl() and friends.
+
+Fri Aug 30 09:51:23 1996  Paul H. Hargrove  <[email protected]>
+
+       * version.c, README.sgml:
+       Rewrite installation instructions and bump version up to "0.7.0".
+
+       * Makefile:
+       Remove the INCDIR variable; we now rely on the
+       user to have the correct links in /usr/include.
+
+Mon Aug 26 12:25:41 1996  Paul H. Hargrove  <[email protected]>
+
+       * version.c, README.sgml:
+       Reformat the documentation and bump version up to "pre-0.7-9".
+       Hopefully this will become version 0.7 in a few days.
+
+Thu Aug 22 08:00:44 1996  Paul H. Hargrove  <[email protected]>
+
+       * README.sgml, version.c:
+       Bump version up to "pre-0.7-8".
+
+       * file_nat.c, file_dbl.c:
+       AppleDouble headers had resource fork size in wrong byte order.
+
+Wed Aug 21 05:22:28 1996  Paul H. Hargrove  <[email protected]>
+
+       * version.c, README.sgml:
+       Bump version up to "pre-0.7-7".
+
+       * bnode.c:
+       Fixed a long-standing bug in hfs_bnode_lock().
+       This bug occasionally caused lock-up under heavy load.
+
+Tue Aug 20 09:15:10 1996  Paul H. Hargrove  <[email protected]>
+
+       * README.sgml, version.c:
+       Bump version up to "pre-0.7-6".
+
+       * catalog.c:
+       Fix a deadlock problem in catalog readers/writers locking.
+
+       * bins_del.c:
+       hfs_bnode_update_key() was still corrupting the header node sometimes.
+
+       * catalog.c, dir.c:
+       Fix problem with extending the catalog B-tree hanging hfs_cat_commit().
+       Fix a race that could delete a non-empty directory.
+
+Sun Aug 18 23:16:43 1996  Paul H. Hargrove  <[email protected]>
+
+       * version.c, README.sgml:
+       Bump version to "pre-0.7-5" for test release.
+
+       * dir_cap.c, README.sgml:
+       Change ".:rootinfo:" to ".rootinfo".
+
+       * hfs_fs.h, dir_cap.c, dir_dbl.c, dir_nat.c:
+       Mangle the names as first step in hfs_{cap,dbl,nat}_lookup().
+       Use the new hfs_streq() to catch mixed case matches to the special
+       files and directories in hfs_{cap,dbl,nat}_lookup().
+       Store reserved names only once.
+
+       * dir.c, hfs.h, string.c:
+       Implement hfs_streq() which tests for string equality more
+       rapidly than hfs_strcmp() by checking for equal length first,
+       and use it when checking for reserved names.
+
+       * inode.c, TODO, dir_cap.c, dir_dbl.c, README.sgml:
+       Provide the metadata for the root directory for the CAP and AppleDouble
+       schemes in the files ".:rootinfo:" and "%RootInfo", respectively.
+
+       * TODO, super.c:
+       Add (untested) support for the old Mac Plus style of partition map.
+
+       * bdelete.c, TODO:
+       Note the possibility of bdelete() to hanging on a corrupted B-tree.
+
+       * TODO:
+       Add items corresponding to some of the 'XXX' comments in the sources.
+
+       * dir_dbl.c, dir_cap.c:
+       Update comments, removing ref. to a comment that once existed in inode.c
+
+       * catalog.c:
+       Remove some redundant locking and error checks
+       that had been previously marked as questionable.
+
+Sat Aug 17 08:06:56 1996  Paul H. Hargrove  <[email protected]>
+
+       * binsert.c, bfind.c, bins_del.c, balloc.c, bdelete.c:
+       Edited some comments for correctness.
+
+       * README.sgml, version.c:
+       Bump version up to "pre-0.7-4" in preparation for snapshot release.
+
+       * Makefile:
+       Have 'make dep' delete the *.o and *.s files.
+
+       * catalog.c, hfs.h, TODO, bfind.c:
+       Move looping from hfs_cat_next() into hfs_bsucc(),
+       where it can be done an entire node at a time.
+
+Fri Aug 16 05:02:59 1996  Paul H. Hargrove  <[email protected]>
+
+       * TODO:
+       Add AppleShare support to the list of goals.
+
+       * trans.c, super.c, hfs_fs.h, README.sgml:
+       Add a "names=netatalk" mount option, since
+       Netatalk quotes initial periods and CAP doesn't.
+
+       * Makefile:
+       Oops! Had removed the 'include .depend' from Makefile.
+
+       * inode.c, hfs_fs.h, file_nat.c, file_dbl.c, file.c, dir_nat.c,
+         dir_dbl.c, dir_cap.c, dir.c, README.sgml:
+       Update for 2.0.1 and newer kernels.
+
+       * Makefile:
+       Get rid of ifeq stuff and use a .tmpdepend file to make sure
+       a failed 'make depend' doesn't allow a 'make hfs.o'.
+
+Wed Aug 14 01:03:01 1996  Paul H. Hargrove  <[email protected]>
+
+       * version.c, README.sgml:
+       Bump version up to "pre-0.7-3" in preparation for snapshot release.
+
+       * btree.c, extent.c, bnode.c:
+       Fix up some debugging code.
+
+Tue Aug 13 12:42:12 1996  Paul H. Hargrove  <[email protected]>
+
+       * version.c, README.sgml:
+       Bump revision to "pre-0.7-2".
+
+       * super.c, sysdep.c, mdb.c, file_nat.c, inode.c, file_cap.c,
+         file_dbl.c, file.c, extent.c, dir.c, catalog.c, btree.c, bnode.c,
+         balloc.c:
+       Added the remaining missing function comments.
+
+       * Makefile, README.sgml:
+       Simplify the default make rule to build the dependency file AND hfs.o.
+       Change the installation instructions to reflect the change.
+
+       * hfs.h:
+       Added missing structure comments.
+
+       * bdelete.c:
+       Merge bdelete_brec() back into hfs_bdelete().
+       Add missing function comments.
+
+
+       * extent.c:
+       Insignificant code change removing an unneeded indirection.
+
+       * btree.c, hfs_btree.h, balloc.c, bnode.c:
+       Add a 'sys_mdb' field to (struct hfs_btree).
+
+       * extent.c, hfs_sysdep.h, sysdep.c, bnode.c, balloc.c, bfind.c,
+         Makefile:
+       Move hfs_buffer_read() from hfs_sysdep.h to sysdep.c so it can use
+       the symbol HFS_SECTOR_SIZE rather than the manifest constant 512.
+       Have hfs_buffer_read() print an error message,
+       and remove redundant errors from the callers.
+
+       * hfs_sysdep.h, mdb.c, super.c, file.c, hfs.h, hfs_btree.h, catalog.c,
+         extent.c, btree.c, balloc.c, bfind.c, bnode.c:
+       Get rid of the type hfs_device and the fields of that type,
+       using the type hfs_sysmdb and the 'sys_mdb' field in its place.
+
+       * Makefile:
+       Fix definition of HDRS variable.
+
+       * README.sgml, version.c:
+       Bump version up to "pre-0.7-1".
+
+       * Makefile:
+       Separate sources and headers into three groups:
+       B-tree code, HFS code and Linux code.
+
+       * bitmap.c, bitops.c, hfs.h, hfs_sysdep.h, balloc.c:
+       Implemented portable set of bit operations in hfs_sysdep.h
+
+       * mdb.c, hfs_sysdep.h, hfs_btree.h, extent.c, btree.c, bitmap.c,
+         bnode.c, balloc.c:
+       Implement a portable set of buffer operations in hfs_sysdep.h
+
+       * TODO:
+       Remove note about separating header files into two parts.
+
+       * catalog.c:
+       Remove call to hfs_mdb_dirty(), since the hfs_brec_relse() does it.
+
+       * hfs.h, extent.c, file.c:
+       Move hfs_getblk() from extent.c to file.c, since that is now the
+       only file that actually uses it.
+
+       * balloc.c:
+       Replace use of hfs_getblk() in balloc.c with a local function
+       (get_new_node()) that doesn't retry, since B-trees can't shrink.
+
+       * hfs.h, hfs_btree.h, hfs_sysdep.h, mdb.c, extent.c:
+       Make hfs_buffer a typedef.
+
+       * inode.c, hfs.h, hfs_sysdep.h, dir.c:
+       Change hfs_sysentry to a typedef.
+       Rename 'sysentry' field of (struct hfs_cat_entry) to 'sys_entry'.
+
+       * super.c, mdb.c, catalog.c:
+       Rename hfs_cat_sync() to hfs_cat_commit() and call it
+       from hfs_mdb_commit() rather than from hfs_write_super().
+
+       * catalog.c, file.c:
+       Minimize the calls to hfs_mdb_dirty().   Now called when:
+       1) A buffer holding a volume bitmap block is dirtied.
+       2) A dirty B-tree node is written back to the buffers.
+       3) A dirty catalog entry is written back to the buffers.
+
+       * hfs_sysdep.h, hfs.h:
+       Make hfs_sysmdb a typedef.
+
+Sun Aug 11 08:46:10 1996  Paul H. Hargrove  <[email protected]>
+
+       * hfs_sysdep.h, extent.c, hfs.h:
+       Replace hfs_mdb_{lock,unlock} with more portable
+       scheme using a wait queue in the MDB.
+
+       * hfs.h, hfs_btree.h, hfs_sysdep.h, bnode.c, catalog.c, binsert.c:
+       Make hfs_wait_queue a typedef'd pointer to a (struct wait_queue).
+       Rename hfs_wait_on() to hfs_sleep_on().
+
+       * catalog.c, hfs_sysdep.h, super.c, bfind.c, bnode.c, balloc.c:
+       Implemented hfs_dev_name() in hfs_sysdep.h
+       as a portable call to produce a device name.
+
+       * super.c, hfs.h, mdb.c:
+       Rename hfs_mdb_read() to hfs_mdb_get(), and don't take a
+       'sys_mdb' argument.  That's the callers responsibility.
+
+       * sysdep.c, Makefile:
+       Remove the pointless file sysdep.c
+
+       * README.sgml:
+       Clean up the "System Requirements" section.
+
+Sat Aug 10 22:41:24 1996  Paul H. Hargrove  <[email protected]>
+
+       * sysdep.h, sysdep.c, super.c, hfs_sysdep.h, mdb.c, string.c,
+         hfs_fs.h, hfs_fs_i.h, hfs_fs_sb.h, hfs_btree_private.h, hfs_btree.h,
+         file_cap.c, file_dbl.c, file_nat.c, hfs.h, file.c, dir_nat.c,
+         extent.c, dir.c, dir_cap.c, dir_dbl.c, catalog.c, bnode.c, brec.c,
+         btree.c, binsert.c, bitmap.c, bitops.c, bfind.c, bins_del.c,
+         Makefile, balloc.c, bdelete.c:
+       Includes the hfs.h that was missing from the previous check in.
+       MAJOR include-file cleanup:
+               hfs_btree.h merged into hfs.h
+               hfs_btree_private.h renamed hfs_btree.h
+               sysdep.h renamed hfs_sysdep.h
+       Fixed some minor portability fixes shown up by the header split.
+
+       * README.sgml:
+       Add instructions for a dealing with a missing linux/version.h
+
+       * hfs_fs.h, mdb.c, string.c, catalog.c, extent.c, btree.c, bitmap.c,
+         bitops.c, bnode.c, brec.c, bins_del.c, binsert.c, bdelete.c, bfind.c,
+         balloc.c:
+       Major split of hfs_fs.h into Linux-specific
+       part (hfs_fs.h) and HFS-specific part (hfs.h).
+
+       * file.c, extent.c:
+       Move hfs_getblk() from file.c to extent.c
+
+       * sysdep.h, super.c, mdb.c, hfs_fs_sb.h, hfs_fs.h, file.c, extent.c,
+         catalog.c, bnode.c, bitmap.c:
+       Make the field 's_mdb' in (struct hfs_sb_info) a pointer to
+       the MDB, rather than the actual MDB.  This allowed the definition
+       of (struct hfs_mdb) to be moved from hfs_fs_sb.h to hfs_fs.h.
+
+       * ccache.c, hfs_fs.h, Makefile, catalog.c:
+       Merged ccache.c and catalog.c into the latter.
+       Moved definition of (struct hfs_cat_rec) into catalog.c
+
+       * extent.c:
+       Oops!  Last set of changes didn't compile but they're OK now.
+
+       * hfs_btree.h, hfs_fs.h, mdb.c, ccache.c, extent.c, btree.c:
+       Move the definition of (struct hfs_raw_extent) inside
+       extent.c and treat it as simple array of U16's elsewhere.
+
+       * hfs_fs.h, dir_dbl.c, dir_nat.c, ccache.c, catalog.c, dir_cap.c:
+       Make hfs_cat_next() return the CNID and cdrType of the entry.
+       Now catalog.c and ccache.c are the only files which
+       depend on the structure of a catalog record on disk.
+
+       * dir.c, hfs_fs.h, catalog.c:
+       Replace hfs_cat_new_{file,dir}() with hfs_cat_{create,mkdir}()
+       which are wrappers for what used to be hfs_cat_create().
+
+       * hfs_fs.h, mdb.c, super.c, Makefile:
+       Split super.c into super.c (Linux stuff) and mdb.c (MDB stuff).
+
+       * super.c, hfs_fs_sb.h:
+       Add the MDB field 'drAtrb' to (struct hfs_mdb) as the field 'attrib'.
+
+       * hfs_fs_sb.h, super.c:
+       Split hfs_read_super() into hfs_read_super() and hfs_mdb_read().
+
+       * super.c, hfs_fs_sb.h:
+       Remove the unneeded 'hs' field from (struct hfs_mdb).
+
+       * TODO:
+       Remove item about hfs_notify_change() needing to update metadata.
+
+       * inode.c, hfs_fs.h, hfs_fs_sb.h, file_cap.c, file_dbl.c, file_nat.c,
+         file.c, dir.c:
+       Add a flags argument to hfs_{cap,dbl,nat}_buildmeta() so that
+       it only builds the parts that are currently out-of-date.
+       Call hfs_{cap,dbl,nat}_buildmeta() through hfs_update_meta()
+       in hfs_notify_change() and hfs_rename() to update the metadata.
+
+       * dir.c:
+       Make test for normal dir in update_dirs_{plus,minus}() more explicit.
+
+       * inode.c, file_cap.c, file_dbl.c, file_nat.c, dir_dbl.c, dir_nat.c,
+         file.c, README.sgml, dir_cap.c:
+       Resolve the "meta-data" vs. "metadata" rivalry in favor of the latter.
+
+       * btree.c:
+       Simplify some debugging code.
+
+       * hfs_btree_private.h, bnode.c, btree.c, balloc.c:
+       Put the in-core copy of the header node IN the
+       B-tree structure rather than just a pointer to it.
+
+       * hfs_btree_private.h, btree.c, bnode.c:
+       Have hfs_btree_commit() call hfs_bnode_commit()
+       to commit the header and root nodes.
+
+       * hfs_fs.h, super.c, hfs_btree_private.h, btree.c, hfs_btree.h,
+         balloc.c:
+       Change hfs_commit_mdb() to hfs_mdb_commit().
+       Make hfs_mdb_commit() call hfs_btree_commit().
+       Move code to update B-tree size and extent
+       from hfs_btree_extend() to hfs_btree_commit().
+       Make hfs_btree_extend() call hfs_mdb_commit().
+
+       * super.c:
+       Change hfs_commit_super() to hfs_commit_mdb().
+
+       * btree.c, bnode.c, bfind.c:
+       Fixed up broken debugging code and error messages.
+
+       * super.c, hfs_btree_private.h, btree.c, hfs_btree.h, bdelete.c,
+         binsert.c, balloc.c:
+       Now use write-back caching of B-tree header fields.
+
+       * hfs_fs.h:
+       Get rid of the add{16,32}() inlines as they are no longer used.
+
+       * hfs_btree_private.h, binsert.c, btree.c, bdelete.c, bfind.c, balloc.c:
+       All the needed fields of the B-tree header are
+       now cached for reading, but not yet writing.
+
+       * TODO:
+       Remove "Implement write count" from TODO list.
+
+       * file.c, super.c, bnode.c:
+       Implement write count.
+
+       * catalog.c:
+       Fix directory entry counting in hfs_cat_move().
+
+       * balloc.c:
+       Simplify hfs_btree_extend(), since the allocation
+       request will get rounded up to the clumpsize.
+
+       * extent.c:
+       Honor clumpsize when allocating blocks to files.
+
+       * file_cap.c, file_dbl.c, file_nat.c, super.c, dir.c, file.c,
+         ccache.c, catalog.c, balloc.c:
+       Mark 44 functions in need of commenting.
+
+       * hfs_fs_sb.h, super.c, extent.c, hfs_fs.h, ccache.c, btree.c, balloc.c:
+       Record clumpsize in allocation blocks rather than 512-byte blocks.
+
+       * sysdep.h, super.c, TODO, balloc.c, hfs_fs_sb.h:
+       Now updates the backup MDB when a B-tree grows.
+
+       * extent.c:
+       hfs_extent_free() had test against NULL backward.
+       The result is that access to a file with extents in the extents
+       B-tree would result in an infinite loop in hfs_cat_put().
+
+       * hfs_fs_sb.h, super.c, hfs_fs.h:
+       Reorganize partition map code to get size of partition
+       in preparation for dealing with the alternate MDB.
+
+Fri Aug  9 03:25:13 1996  Paul H. Hargrove  <[email protected]>
+
+       * Makefile:
+       Add make rules for README.{ps,info}
+
+       * README, README.sgml, DOC, FAQ, Makefile, .cvsignore,
+       Merge CHANGES into ChangeLog.
+       Merge DOC, FAQ and README into README.sgml.
+       Add make rules for building README.{txt,dvi}
+
+       * BUG_INFO, Makefile:
+       Added a BUG_INFO script which attempts to collect some useful
+       information which I'd like to see in every bug report I receive.
+
+       * Makefile, version.c:
+       Added version.c which contains a version string.
+
+Thu Aug  8 21:48:24 1996  Paul H. Hargrove  <[email protected]>
+
+       * trans.c:
+       Fix Latin-1 -> Macintosh filename mapping to change colons to pipes.
+
+       * trans.c:
+       Fixed Mac->Latin-1 translation to behave as documented for the
+       extended 8-bit characters without corresponding Latin-1 characters.
+
+       * inode.c, super.c, file.c, hfs_fs_i.h, hfs_fs_sb.h, DOC:
+       Added a conv={binary,text,auto} mount option similar to that of the
+       msdos, hpfs and iso9660 filesystems, but applying only to data forks.
+       As compared to those filesystems, HFS has the advantage that only a
+       single CR need be converted to a NL, rather than a CR/NL sequence, so
+       it is quite safe to seek in the file.
+       Additionally the 'Type' field is far more reliable indicator of text
+       files than a file extension.
+
+       * super.c:
+       Simplified parsing of mount options.
+
+       * super.c:
+       Oops!  The part=<n> mount option was being parsed in octal!
+
+       * TODO:
+       Remove "case=lower" from the list of goals.
+
+       * super.c, hfs_fs.h, hfs_fs_sb.h, string.c, dir_dbl.c, dir_nat.c,
+         dir_cap.c, DOC:
+       Resurrect the case={asis,lower} mount option.
+
+       * dir.c:
+       Simpler test for "normal" directory in update_dirs_{plus,minus}().
+
+       * hfs_fs_sb.h, super.c, dir.c, hfs_fs.h, catalog.c, DOC:
+       Add mount options to specify what Type and Creator will be used for
+       new files and change the default from NULLs to "????".
+
+Wed Aug  7 11:32:22 1996  Paul H. Hargrove  <[email protected]>
+
+       * catalog.c:
+       In hfs_cat_next() use entry->cnid rather than the key of the initial
+       brec to identify the end of the directory.
+
+       * README:
+       Update for pre-0.7 version.
+
+       * hfs_fs.h:
+       Create versioned module if CONFIG_MODVERSIONS is set in linux/config.h
+
+       * TODO:
+       Note need for special steps for unaligned accesses on some machines.
+
+       * FAQ:
+       Added Q0: What is HFS?
+       Added Q7: Does hfs_fs work w/ 400k and 800k diskettes?
+       Brought Q6 (about writability) up to date.
+       Made a few other answers more verbose.
+
+Tue Aug  6 00:58:46 1996  Paul H. Hargrove  <[email protected]>
+
+       * Makefile:
+       Changed 'snapshot' rule to include cvs tag command.
+
+       * hfs_fs.h, dir_cap.c, dir_dbl.c, dir_nat.c, catalog.c, ccache.c:
+       Implemented readers half of dir locking scheme so readdir() should
+       produce consistent results and count_dir_entries() is not race prone.
+
+       * catalog.c:
+       hfs_cat_move() was calling hfs_cat_decache() after changing
+       the key rather than before, corrupting the hash lists.
+
+Mon Aug  5 14:03:46 1996  Paul H. Hargrove  <[email protected]>
+
+       * hfs_fs.h, catalog.c:
+       Implemented the writers half of a locking scheme for directories.
+
+       * inode.c:
+       Fixed a serious bug in hfs_notify_change() that would allow a chmod()
+       on directory meta-data and would cause the directory inode (if it was
+       in memory at the time) to change into a file inode.
+
+       * inode.c:
+       Fixed a problem with write permissions on directory meta-data.
+
+       * dir_dbl.c, dir_nat.c, dir_cap.c:
+       hfs_{cap,dbl,nat}_readdir() now return the correct value in the 'd_ino'
+       field of the dirent for all cases, something I think has always been
+       done incorrectly until now.
+
+       * dir_nat.c, inode.c, dir_cap.c:
+       In hfs_{cap,nat}_lookup() take advantage of the
+       'file_type' field of (struct hfs_inode_info).
+
+       * TODO:
+       Removed two accomplished goals (rename() and improved readdir()).
+
+       * inode.c, dir_dbl.c, dir_nat.c, hfs_fs_i.h, dir.c, dir_cap.c:
+       Rewrite hfs_{cap,dbl,nat}_readdir() to take advantage of hfs_cat_next().
+       They now use a uniform 'i_size' for all inodes for a given directory.
+       This simplifies update_dirs_{plus,minus}() and eliminates the need for
+       the 'file_size' and 'dir_link' fields of (struct hfs_inode_info).
+       For the CAP and Netatalk schemes the meta-data directories are now the
+       last entries rather than coming just after '.' and '..'.  This is in
+       preparation for the day when we can write to the files in those
+       directories, and ensures that when using 'tar' to copy HFS filesystems
+       the file or directory will be created before the meta-data is written.
+       Otherwise we could be stuck writing meta-data and not knowing if it is
+       for a file or a directory!
+
+       * ccache.c:
+       Updated count_dir_entries() for new hfs_cat_next().
+
+       * hfs_fs.h, catalog.c:
+       hfs_cat{nth,next}() no longer take a 'types' argument,
+       so they now return all entries.
+       hfs_cat_next() now uses the ParID of the key to detect
+       the end of the directory.
+       hfs_cat_nth() now accepts n=0 as a valid input, requesting the thread.
+
+       * trans.c, string.c, super.c, dir_nat.c, hfs_fs.h, dir.c, dir_cap.c,
+         dir_dbl.c, catalog.c:
+       Rename (struct hfs_cname) to the more appropriate (struct hfs_pstr).
+
+       * hfs_fs.h, hfs_btree.h:
+       Move some constants from hfs_fs.h to hfs_btree.h
+
+       * bdelete.c, hfs_btree.h:
+       Remove hfs_bdelete_brec() from public B-tree interface.
+
+       * hfs_btree_private.h, hfs_fs.h, btree.c, hfs_btree.h, bnode.c, brec.c,
+         bfind.c, bins_del.c, binsert.c, balloc.c, bdelete.c, Makefile:
+       Split B-tree stuff into public and private parts:
+               brec.c split into bfind.c and brec.c
+               hfs_btree.h split into hfs_btree.h and hfs_btree_private.c
+
+       * inode.c:
+       The tests and sets of the HFS_FIL_LOCK bit where all reversed!
+
+       * hfs_fs.h, ccache.c:
+       Redo some ccache stuff, removing the 'error' field from
+       (struct hfs_cat_entry) and ensuring that hfs_cat_put()
+       will not sleep on an uninitialized entry.
+
+Sun Aug  4 23:43:28 1996  Paul H. Hargrove  <[email protected]>
+
+       * sysdep.h:
+       Change swap{16,32}() back to macros since hton[ls]() are functions.
+
+       * hfs_fs.h, ccache.c:
+       Use only lowest order byte of parent CNID in hashing a catalog key.
+
+       * bdelete.c:
+       The "improved" bdelete() was TOO paranoid looking for missing parents.
+
+       * ccache.c:
+       Get rid of pointless swap16const(0).
+
+       * hfs_fs.h, inode.c, extent.c, ccache.c, dir_cap.c, dir_nat.c,
+         binsert.c, catalog.c:
+       Store cnid and associated constants in big-endian byte order.
+       This reduces the number of byte-order swaps required.
+
+       * sysdep.h:
+       Make swap32() and swap16() inline functions.
+
+       * dir_nat.c, dir_cap.c, dir_dbl.c:
+       Added hfs_rename() to the inode_operations for normal directories.
+
+       * dir.c, hfs_fs.h:
+       Added hfs_rename() and cleaned up hfs_{create,mkdir,unlink,rmdir}().
+
+       * catalog.c:
+       Added the missing check for moving a directory into itself.
+
+       * catalog.c, ccache.c, hfs_fs.h:
+       Implement a nearly ideal hfs_cat_move().
+       It still needs to prevent moving a directory into itself.
+       The functions hfs_cat_{create,delete,move}() still need work with
+       respect to their atomicity (especially vs. readdir).
+
+       * bdelete.c:
+       Fixed a serious bug in hfs_bdelete_brec() that would yield a corrupted
+       b-tree when the first record in a bnode was deleted.
+       Made bdelete() more aggressive when checking for missing parents.
+
+Sat Aug  3 06:11:50 1996  Paul H. Hargrove  <[email protected]>
+
+       * btree.c, super.c:
+       Fixed a problem that caused a kernel oops when no HFS filesystem
+       is found.
+
+Wed Jul 24 13:06:12 1996  Paul H. Hargrove  <[email protected]>
+
+       * catalog.c:
+       Remove race in hfs_cat_create() that could overflow directory valence.
+
+       * catalog.c:
+       Fix hfs_cat_create() so the parent directory doesn't get deleted
+       out from under it.  Otherwise we could have created files and
+       directories in deleted directories.
+
+       * hfs_fs.h, dir_cap.c, dir_dbl.c, dir_nat.c, catalog.c, ccache.c:
+       Redo hfs_cat_{next,nth}() in terms of which entry types to
+       allow, rather than which to skip.
+
+       * catalog.c:
+       The function hfs_cat_create() would fail to hfs_cat_put(entry) if
+       the 'record' argument was invalid or if the 'result' argument was NULL.
+
+       * dir.c:
+       The functions hfs_{create,mkdir,unlink,rmdir} all failed to
+       call iput() when their arguments conflicted with a reserved name.
+
+       * catalog.c, hfs_fs_sb.h:
+       Start over on rename().  Still unfinished.
+       Fix silly bug in hfs_cat_create() that made it always fail.
+
+       * ccache.c:
+       Fix byte-order bug in write_entry().
+
+Tue Jul 23 12:12:58 1996  Paul H. Hargrove  <[email protected]>
+
+       * dir_dbl.c, dir_nat.c, hfs_fs.h, dir.c, dir_cap.c:
+       Remove the macros KEY() and PARENT() since the key is now easy
+       to access through the catalog entry.
+       Replace the macros NAME{IN,OUT}() with inline functions
+       hfs_name{in,out}() to gain type checking of arguments.
+
+       * catalog.c:
+       Remove the macro TYPE().
+
+       * inode.c, file_dbl.c, file_nat.c, file.c, file_cap.c:
+       Remove the #define's of the unused macro KEY().
+
+       * hfs_fs.h, dir_cap.c, dir_dbl.c, dir_nat.c, catalog.c, dir.c:
+       Replace hfs_lookup_parent() in dir.c with hfs_cat_parent() in catalog.c.
+       This new function performs locking to protect against rename() changing
+               the parent during I/O.
+       It is also intended for use with files as well as directories.
+       Change hfs_{cap,dbl,nat}_lookup() to use the new function.
+
+       * dir.c, hfs_fs.h, catalog.c:
+       Remerge hfs_cat_{create,mkdir}() into hfs_cat_create() and resurrect
+               hfs_cat_new_{file,dir}().
+       Fix hfs_cat_{create,delete} to use the improved catalog cache for
+               locking in place of directory-level create/delete locks.
+       Fix hfs_{create,mkdir}() to use the new hfs_cat_create().
+
+       * hfs_fs.h, ccache.c:
+       Rewrite parts to remove need for specialized create/delete locking.
+       Use new case-independent hash function.
+       Fix bug in hfs_cat_get() that would read an entry w/o locking it.
+       Call hfs_relinquish() before retrying a deleted entry in hfs_cat_get.
+       If there is a read error, then don't retry in hfs_cat_get().
+       Remove unused 'version' field from (struct hfs_cat_entry).
+
+       * sysdep.h:
+       Add hfs_relinquish(), a system-independent alias for schedule().
+
+       * hfs_fs.h, string.c:
+       Add hfs_strhash(), a simplistic case-independent hash function.
+
+       * hfs_fs.h, inode.c:
+       Make hfs_iget() an inline function.
+
+       * TODO:
+       Add a few goals and removed those that have been achieved.
+
+       * Makefile:
+       Add ccache.c to list of source files.
+       Add rule for *.s files and include them in the 'clean' rule.
+
+Wed Jul 17 17:22:45 1996  Paul H. Hargrove  <[email protected]>
+
+       * sysdep.h, trans.c, string.c, super.c, hfs_fs_i.h, hfs_fs_sb.h,
+         inode.c, hfs_btree.h, hfs_fs.h, file_dbl.c, file_nat.c, extent.c,
+         file.c, file_cap.c, dir_dbl.c, dir_nat.c, ccache.c, dir.c,
+         dir_cap.c, btree.c, catalog.c, bnode.c, brec.c, balloc.c:
+       Total rewrite of the inode-handling stuff to be centered around
+       a catalog entry cache (ccache.c).  This results not only in a far
+       more sensible way of doing things, but also removed many race
+       conditions. (The source and object code both got smaller too!)
+       Many small "undocumented features" were also fixed.
+       Replace HFS_CNAME with (struct hfs_cname).
+       rename() has been temporarily abandoned.
+
+Thu Jul 11 01:14:38 1996  Paul H. Hargrove  <[email protected]>
+
+       * dir.c:
+       As written hfs_lookup_parent() had two overlapping read requests
+       in the catalog tree.  This could have led to deadlock.
+
+Wed Jul 10 09:27:00 1996  Paul H. Hargrove  <[email protected]>
+
+       * catalog.c, hfs_fs.h, bdelete.c:
+       More work on getting rename() fleshed out.  Still not done.
+       Before I can finish it looks like I'll need to build a
+       mechanism for exclusive access to the catalog tree.  There
+       just doesn't seem to be any other way to get proper POSIX
+       semantics without a bunch of race conditions elsewhere.
+
+       * hfs_fs.h, inode.c, dir_cap.c, dir_dbl.c, dir_nat.c, catalog.c:
+       More work on the still incomplete rename() code.
+       Merge hfs_cat_add_{dir,file}() into hfs_cat_create().
+       Add file-thread support to hfs_cat_{create,delete,rename}.
+
+Tue Jul  9 09:43:15 1996  Paul H. Hargrove  <[email protected]>
+
+       * inode.c, dir_dbl.c, dir_nat.c, extent.c, dir_cap.c:
+       The indirect (struct hfs_file) was causing blocks not to be freed
+       when files where deleted, and an omission in hfs_put_inode() was
+       preventing the inode from getting freed.  Both are now fixed.
+
+       * hfs_fs.h, dir_dbl.c, dir_nat.c, hfs_btree.h, catalog.c, dir_cap.c,
+         bdelete.c:
+       Made unlink() and rmdir() more race resistant and did some more
+       work on the still incomplete code for rename().
+
+       * btree.c, bnode.c:
+       There was a serious race condition in the bnode cache, so
+       hfs_bnode_find() is now modeled after Linus's inode cache.
+
+Mon Jul  8 10:33:38 1996  Paul H. Hargrove  <[email protected]>
+
+       * hfs_fs_i.h, inode.c, file_cap.c, file_dbl.c, file_nat.c, dir_dbl.c,
+         dir_nat.c, file.c, dir.c, dir_cap.c:
+       More changes to layout of (struct hfs_inode_info).
+
+       * super.c, inode_cap.c, inode_dbl.c, inode_nat.c, inode.c, hfs_fs_i.h,
+         hfs_fs_sb.h, file_nat.c, hfs_fs.h, file.c, file_cap.c, file_dbl.c,
+         Makefile, catalog.c:
+       Implemented new layout for (struct hfs_inode_info) resulting in the
+       elimination of lots of duplicated code for hfs_*_write_inode(),
+       hfs_*_put_inode() and *_open() functions.
+       Merged inode_*.c files back into inode.c.
+       Not fully tested.
+
+       * TODO:
+       Add a few more of my goals to the list.
+
+       * README:
+       Documentation updates.
+
+       * inode_nat.c, inode_cap.c, inode_dbl.c, inode.c, hfs_fs.h, hfs_fs_i.h,
+         file.c, file_cap.c, file_dbl.c, file_nat.c, catalog.c:
+       (struct hfs_file) and metadata are read when file is opened or
+       truncated and are released by iput().
+
+Sun Jul  7 23:55:43 1996  Paul H. Hargrove  <[email protected]>
+
+       * inode_nat.c, inode_cap.c, inode_dbl.c, inode.c, dir_nat.c, hfs_fs.h,
+         hfs_fs_i.h, dir_cap.c, dir_dbl.c, catalog.c, dir.c:
+       (struct hfs_dir) is now inside (struct hfs_inode_info) once again.
+
+       * inode_nat.c, super.c, inode_cap.c, inode_dbl.c, inode.c, file_nat.c,
+         hfs_btree.h, hfs_fs.h, extent.c, file_cap.c, file_dbl.c, dir_nat.c,
+         dir_cap.c, dir_dbl.c, btree.c, catalog.c, dir.c, bpath.c, brec.c,
+         bins_del.c, binsert.c, bnode.c, bfind.c, balloc.c, bdelete.c,
+         Makefile:
+       Remerged (struct hfs_bpath) and (struct hfs_brec), merging the
+       files bfind.c and bpath.c as a resurrected brec.c.
+
+Sat Jul  6 21:47:05 1996  Paul H. Hargrove  <[email protected]>
+
+       * inode_cap.c, inode_dbl.c, inode_nat.c, inode.c, hfs_fs.h, hfs_fs_i.h,
+         file_cap.c, file_dbl.c, file_nat.c, hfs_btree.h, dir_nat.c, extent.c,
+         dir.c, dir_cap.c, dir_dbl.c, btree.c, catalog.c, bfind.c, bpath.c,
+         binsert.c, bdelete.c:
+       Renamed (struct hfs_brec_key) to (struct hfs_bkey).
+
+Tue May 28 07:53:24 1996  Paul H. Hargrove  <[email protected]>
+
+       * inode_cap.c, catalog.c:
+       Spelling fixes.
+
+       * inode_nat.c, super.c, inode_cap.c, inode_dbl.c, inode.c, hfs_fs.h,
+         hfs_fs_i.h, hfs_fs_sb.h, file.c, file_dbl.c, file_nat.c, dir_dbl.c,
+         dir_nat.c, extent.c, dir.c, dir_cap.c, catalog.c:
+       Structures got too big, so I had to add a layer of indirection
+       to (struct hfs_inode_info).
+       This means we must clear_inode() in inode_put().
+
+Mon May 27 01:32:42 1996  Paul H. Hargrove  <[email protected]>
+
+       * catalog.c, file_cap.c:
+       Some sizeof() stuff now uses variable not type.
+
+       * hfs_fs.h:
+       Make HFS_I() and HFS_SB() inline to gain type checking.
+
+Sun May 26 13:34:17 1996  Paul H. Hargrove  <[email protected]>
+
+       * dir_nat.c:
+       Oops.  Had left some debugging printk()s in place.
+
+       * file_dbl.c, file_nat.c, file_cap.c:
+       Cleaned up variable names for consistency.
+
+       * hfs_fs_sb.h:
+       Add a couple 'const's to function typedefs.
+
+       * hfs_fs.h:
+       Add and update function prototypes.
+       Cleaned up type names.
+       Fix debugging malloc code.
+       Add hfs_iget_by_name() as an inline function.
+
+       * sysdep.h:
+       Remove extra semicolon from macro definitions.
+
+       * super.c:
+       Use new hfs_iget_by_name() to get root inode.
+
+       * extent.c:
+       Cleaned up some variable naming for consistency.
+
+       * catalog.c:
+       Added (untested) code for hfs_cat_move_file().
+
+       * catalog.c:
+       Fix one missed call to hfs_cat_build_key().
+       Make hfs_cat_add_{file,dir}() take a cat_entry as an argument.
+       Add hfs_cat_new_{file,dir}() to generate new cat_entry's.
+
+       * dir_dbl.c, dir_nat.c, dir.c, dir_cap.c:
+       Cleaned up type and variable names.
+       Updated calls to hfs_cat_build_key() and NAMEOUT()
+       Use new hfs_iget_by_*() calls.
+
+       * inode_cap.c, inode_dbl.c, inode_nat.c:
+       Cleaned up type and variable names.
+
+       * inode.c:
+       Update calls to hfs_cat_build_key().
+       Cleaned up type and variable names.
+       Implemented a hierarchy of hfs_iget_by*() calls.
+
+       * catalog.c:
+       Change hfs_cat_build_key() to take a HFS_CNAME as input.
+
+       * btree.c:
+       Initialize lsize and psize fields of file.
+
+       * trans.c:
+       Now passes type HFS_CNAME and has name/len in "normal" order.
+
+Tue May 21 07:02:34 1996  Paul H. Hargrove  <[email protected]>
+
+       * bnode.c:
+       Attempt to read invalid bnode would have led to an infinite loop under
+       certain circumstances.  One way to cause this was with an invalid
+       partition table which points beyond the end of the device.
+
+Sat May 11 12:38:42 1996  Paul H. Hargrove  <[email protected]>
+
+       * sysdep.h, sysdep.c, inode_dbl.c, inode_nat.c, super.c, inode_cap.c,
+         inode.c, hfs_fs.h, hfs_fs_i.h, hfs_fs_sb.h, file_dbl.c, file_nat.c,
+         hfs_btree.h, extent.c, file.c, file_cap.c, dir_nat.c, dir.c,
+         dir_cap.c, dir_dbl.c, btree.c, catalog.c, bitmap.c, bitops.c,
+         bnode.c, bfind.c, bins_del.c, binsert.c, balloc.c, bdelete.c:
+       Another big wave of portability-oriented changes.
+
+Tue May  7 11:28:35 1996  Paul H. Hargrove  <[email protected]>
+
+       * super.c, sysdep.c, sysdep.h, inode_cap.c, inode_dbl.c, inode_nat.c,
+         hfs_fs_i.h, inode.c, file_nat.c, hfs_btree.h, hfs_fs.h, file.c,
+         file_cap.c, file_dbl.c, dir_nat.c, extent.c, dir_cap.c, dir_dbl.c,
+         btree.c, catalog.c, dir.c, bnode.c, bpath.c, binsert.c, bitmap.c,
+         bitops.c, bdelete.c, bfind.c, bins_del.c, Makefile, balloc.c:
+       Start a big move to abstract all the Linux-specific stuff
+       out of the lower levels.  Created sysdep.[ch] to hold it.
+
+       * FAQ, TODO:
+       Bring some documentation up-to-date.
+
+Fri May  3 20:15:29 1996  Paul H. Hargrove  <[email protected]>
+
+       * super.c, inode_dbl.c, inode_nat.c, inode.c, inode_cap.c, extent.c,
+         hfs_fs.h, hfs_fs_i.h, dir_dbl.c, dir_nat.c, catalog.c, dir.c,
+         dir_cap.c, bpath.c, btree.c, binsert.c, bnode.c:
+       "FID reform": 'fid' became 'cnid' (Catalog Node ID), and is now
+       a field in (struct hfs_file).  The new name is more consistent
+       with Apple's documentation.  The presence of 'cnid' in (struct
+       hfs_file) help move more of the code toward OS-independence.
+
+       * inode_nat.c, super.c, trans.c, inode.c, inode_cap.c, inode_dbl.c,
+         hfs_fs.h, file_cap.c, file_dbl.c, file_nat.c, dir_nat.c, extent.c,
+         file.c, dir.c, dir_cap.c, dir_dbl.c, btree.c, catalog.c, bnode.c,
+         bpath.c, bins_del.c, binsert.c, bitmap.c, bitops.c, bdelete.c,
+         bfind.c, balloc.c:
+       A lot of changes in what headers are included and in what order.
+
+Sat Apr 27 12:28:54 1996  Paul H. Hargrove  <[email protected]>
+
+       * FAQ:
+       Updated for current writability status.
+
+       * .cvsignore:
+       Added ChangeLog.
+
+       * file_dbl.c, file_nat.c, file_cap.c, file.c, dir_dbl.c, dir_nat.c,
+         dir_cap.c:
+       Added the default fsync() to all file_operations structures.
+
+       * dir_nat.c, hfs_fs.h, dir.c, dir_cap.c, dir_dbl.c:
+       Add rmdir() for normal directories.
+
+       * binsert.c:
+       I had messed up insertion so that is would sometime fail to
+       split the root, but its OK now.
+
+       * dir.c:
+       hfs_do_unlink() decremented directory counts rather than file counts.
+
+Wed Apr 24 13:20:08 1996  Paul H. Hargrove  <[email protected]>
+
+       * hfs_fs.h, bnode.c, hfs_btree.h:
+       Fixed a couple more type size assumptions.
+
+       * hfs_fs.h, balloc.c, bitmap.c, bitops.c:
+       "Portable" bitmap handling was wrong for just about everything but
+       the i386 and the "inverse big-endian" bit ordering that I thought
+       the m68k port was using.  It seems the m68k port is now using standard
+       big-endian bit-numbering conventions.
+       This code is now correct for the standard big- and little-endian bit
+       orderings. (which should cover all Linux systems?)
+       Also no longer assumes sizeof(long) == 4, though that might still be
+       a problem in other parts of the code.
+
+Tue Apr 23 19:19:27 1996  Paul H. Hargrove  <[email protected]>
+
+       * FAQ:
+       Bring uptodate for this snapshot.
+
+       * Makefile:
+       Add FAQ to $(MISC)
+
+       * README, TODO:
+       Documentation updates.
+
+       * bdelete.c:
+       Spelling fixes.
+
+       * dir_cap.c:
+       In unlink() don't force metadata into memory if not present.
+
+       * bdelete.c:
+       Some function comments and some clean up.
+
+       * bins_del.c:
+       Added missing function comment for hfs_bnode_update_key().
+
+       * binsert.c, bitmap.c:
+       Spelling and grammar corrections to comments.
+
+       * hfs_btree.h, hfs_fs.h, bins_del.c, binsert.c, Makefile, bdelete.c:
+       Clean up of hfs_bdelete(), splitting bins_del.c into three files:
+               bins_del.c, binsert.c and bdelete.c
+
+       * bpath.c, bins_del.c:
+       hfs_bdelete() is now working "correctly", but needs some cleaning up.
+
+Mon Apr 22 05:35:41 1996  Paul H. Hargrove  <[email protected]>
+
+       * hfs_fs.h, bpath.c, hfs_btree.h, bins_del.c, bnode.c, balloc.c,
+         bfind.c:
+       Rewrite bnode handling, heading toward a more write-behind approach.
+       Have done away with HFS_LOCK_BLIND.
+
+       * inode_dbl.c, inode_nat.c, extent.c, hfs_fs_i.h, inode_cap.c:
+       Was trying to truncate resource fork of directories!
+
+Sun Apr 21 08:15:43 1996  Paul H. Hargrove  <[email protected]>
+
+       * balloc.c:
+       Updated to use truncate() to grow full trees.
+
+       * extent.c, hfs_fs.h, file.c, inode.c:
+       Added truncate() for normal files.
+
+       * bins_del.c:
+       hfs_bdelete() fixes for handling removal of root.
+
+       * inode_cap.c, inode_dbl.c, inode_nat.c:
+       Release storage for deleted files in hfs_*_put_inode().
+
+       * bitmap.c:
+       Make len=0 valid for hfs_{set,clear}_vbm_bits().
+
+       * super.c, inode.c, hfs_fs_i.h, hfs_fs_sb.h, btree.c, balloc.c:
+       Changed from clumpsize to clumpblks.
+
+       * inode_nat.c, hfs_fs.h, inode_cap.c, inode_dbl.c, btree.c, extent.c,
+         balloc.c:
+       Some extent-related changes in preparation for truncate() support.
+
+Sat Apr 20 10:59:13 1996  Paul H. Hargrove  <[email protected]>
+
+       * inode_nat.c, hfs_fs_i.h, inode.c, inode_cap.c, inode_dbl.c,
+         dir_nat.c, hfs_fs.h, dir.c, dir_cap.c, dir_dbl.c:
+       Removed dir.valence from hfs inode.
+       Added unlink(), but still need truncate() and some more support
+       in hfs_*_put_inode() to free the disk space used by deleted files.
+
+       * bnode.c:
+       Check for NULL bnode in hfs_bnode_relse().
+
+       * bins_del.c:
+       Fixed a byte-order problem in bdelete_nonempty().
+
+       * hfs_fs.h, bnode.c, bpath.c, hfs_btree.h, balloc.c, bins_del.c:
+       First attempt at hfs_bdelete().
+
+       * dir.c:
+       The Finder would display strange things if it couldn't set frView.
+       Therefore initialize frView field for new directories.
+
+       * file_cap.c, file_dbl.c, file_nat.c, hfs_fs.h:
+       Define User/Finder info fields of catalog entry in more detail.
+
+       * hfs_fs.h:
+       HFS_BFIND_DELETE should require exact match.
+
+       * dir.c:
+       Set "record in use" bit of filFlags for new files.
+
+       * inode.c:
+       Was doing the wrong thing with i_ctime.
+
+       * dir_nat.c, dir_cap.c, dir_dbl.c:
+       Added some missing updates to the inode in hfs_*_{create,mkdir}().
+
+Sun Apr 14 00:10:52 1996  Paul H. Hargrove  <[email protected]>
+
+       * hfs_fs.h, file_dbl.c, file_nat.c, file.c:
+       Work around the ever-changing type of f_reada.
+
+Sat Apr 13 00:43:41 1996  Paul H. Hargrove  <[email protected]>
+
+       * bpath.c, bfind.c:
+       Spelling corrections in comments.
+
+       * bins_del.c:
+       ifdef out shift_left() until it is actually used.
+
+       * hfs_btree.h, hfs_fs.h, bins_del.c, bpath.c, bfind.c:
+       Cleaned up code related to 'flags' argument to hfs_bpath_find().
+
+Fri Apr 12 23:30:01 1996  Paul H. Hargrove  <[email protected]>
+
+       * bpath.c:
+       Updated comments.
+       Rewrote hfs_bpath_init() and hfs_bpath_next().
+
+       * hfs_btree.h:
+       Updated prototype for hfs_bpath_init().
+
+       * bins_del.c:
+       Updated call to hfs_bpath_init().
+
+       * inode.c, inode_cap.c, inode_dbl.c, inode_nat.c, extent.c, file_cap.c,
+         file_dbl.c, file_nat.c, dir_cap.c, dir_dbl.c, dir_nat.c, catalog.c,
+         dir.c:
+       Renamed hfs_brec_relse() to hfs_brelse().
+
+       * hfs_fs.h, hfs_btree.h:
+       Updated prototypes to reflect new names in bpath.c
+
+       * bins_del.c:
+       Updated calls to functions in bpath.c
+       Updated comments.
+
+       * Makefile:
+       Renamed brec.c to bpath.c
+
+       * bfind.c:
+       Updated calls to functions in bpath.c
+       Added hfs_brelse() which was previously hfs_brec_relse() in brec.c
+
+       * bpath.c:
+       brec.c renamed to bpath.c
+       Functions renamed to reflect their current actions.
+       Comments are still out of date.
+       hfs_brec_relse() renamed to hfs_brelse() and moved to bfind.c
+
+       * brec.c:
+       brec.c renamed to bpath.c
+
+Wed Apr 10 07:20:28 1996  Paul H. Hargrove  <[email protected]>
+
+       * hfs_fs.h, extent.c, hfs_btree.h, brec.c, dir.c, bfind.c,
+         bins_del.c:
+       Backed-out changes to hfs_binsert() that added the ability to
+       return the new record, since it will probably not ever be needed.
+
+       * extent.c:
+       Since 1.3.45 truncate() has locked the file, so there is no need
+       for all the things I've been doing to hfs_file_extend() & new_extent().
+       Those two functions have been cleaned up a bit (similar to older forms).
+
+       * extent.c:
+       hfs_file_extend() now more "robust", but new_extent() is still
+       not fully "concurrency safe."
+
+Tue Apr  9 09:01:18 1996  Paul H. Hargrove  <[email protected]>
+
+       * bins_del.c:
+       Made split() inline.
+
+       * inode.c, dir_nat.c, hfs_fs.h, dir_cap.c:
+       Added hfs_itry() to get in-core inodes.
+
+       * inode_dbl.c, inode_nat.c, hfs_fs.h, inode.c, inode_cap.c, file_dbl.c,
+         file_nat.c, hfs_btree.h, extent.c, file_cap.c, dir_cap.c, dir_dbl.c,
+         dir_nat.c, brec.c, catalog.c, dir.c, bins_del.c, bnode.c,
+         bfind.c:
+       Rewrite of all the (struct hfs_brec) stuff.
+
+Mon Apr  8 21:50:01 1996  Paul H. Hargrove  <[email protected]>
+
+       * btree.c, extent.c, bnode.c:
+       Fixed format strings in a few debugging printk()'s.
+
+       * brec.c, hfs_fs.h:
+       Removed hfs_brec_relse_one().
+
+       * hfs_fs.h, bnode.c, brec.c, hfs_btree.h, bfind.c, bins_del.c, balloc.c:
+       (struct hfs_bnode_ref)s are now returned by value rather than reference
+       and they are in (struct hfs_brec) rather than pointed to.  Cuts down on
+       a lot of kmalloc() and kfree() traffic.
+
+       * hfs_fs.h, dir.c, extent.c, bins_del.c:
+       Modified hfs_binsert() to be able to return the new record.
+
+       * bins_del.c, hfs_btree.h:
+       Added shift_left(), still untested.
+
+       * bins_del.c:
+       new_root() was missing its comment.
+
+       * super.c, trans.c, hfs_fs_i.h, inode.c, inode_dbl.c, inode_nat.c,
+         file_nat.c, hfs_btree.h, hfs_fs.h, file.c, file_dbl.c, dir_dbl.c,
+         dir_nat.c, extent.c, dir.c, dir_cap.c, bitops.c, bnode.c, brec.c,
+         bfind.c, bins_del.c, bitmap.c, balloc.c:
+       Fixed lines over 80 characters and tabified files.
+
+       * bins_del.c:
+       Fixed line(s) over 80 columns.
+
+       * trans.c, inode_nat.c, string.c, super.c, inode.c, inode_cap.c,
+         inode_dbl.c, hfs_fs_i.h, hfs_fs_sb.h, hfs_btree.h, hfs_fs.h, file.c,
+         file_cap.c, file_dbl.c, file_nat.c, dir_dbl.c, extent.c, btree.c,
+         dir_cap.c, bitops.c, bnode.c, brec.c, bfind.c, bins_del.c, bitmap.c,
+         DOC, README, TODO, balloc.c, CHANGES:
+       About 150 spelling corrections.
+
+Sun Apr  7 23:14:28 1996  Paul H. Hargrove  <[email protected]>
+
+       * dir_cap.c, dir_dbl.c, dir_nat.c, dir.c:
+       Cleaned-up check for special names in mkdir().
+
+       * extent.c:
+       More verbose error message.
+
+       * inode_dbl.c, inode_nat.c, hfs_fs_i.h, inode.c, inode_cap.c, dir.c,
+         hfs_fs.h:
+       Limit directories to 32767 entries, since Mac uses 16-bit integer.
+
+Fri Apr  5 07:27:57 1996  Paul H. Hargrove  <[email protected]>
+
+       * FAQ:
+       Initial version.
+
+       * dir_dbl.c, dir_nat.c, bins_del.c, dir.c, dir_cap.c:
+       Added missing function comments.
+
+Wed Apr  3 06:38:36 1996  Paul H. Hargrove  <[email protected]>
+
+       * brec.c:
+       Cleaned-up code for brec->flags.
+
+       * extent.c:
+       Added function comments.
+
+       * bins_del.c:
+       Added function comments.
+       hfs_binsert() was incrementing record count even on failure.
+
+Mon Apr  1 08:35:51 1996  Paul H. Hargrove  <[email protected]>
+
+       * extent.c:
+       Rewrote find_ext() and new_extent() for new hfs_btree_extend().
+       Moved hfs_btree_extend() to balloc.c
+       Fixed potential kernel OOPS in new_extent().
+
+       * brec.c:
+       Fixed potential kernel OOPS in hfs_brec_get_root().
+       Removed hfs_brec_find_first().
+       Fixed return value of hfs_brec_find().
+
+       * bins_del.c:
+       Updated call to hfs_btree_extend().
+
+       * balloc.c:
+       Merged hfs_bnode_add() and hfs_btree_extend() into the later.
+       Commented init_mapnode().
+
+       * bfind.c:
+       Removed hfs_bfind_first().
+
+       * hfs_fs.h, hfs_btree.h:
+       Updated prototypes.
+
+Sat Mar 30 22:56:47 1996  Paul H. Hargrove  <[email protected]>
+
+       * CHANGES, README, TODO:
+       Updated documentation in preparation for 0.6 release.
+
+       * inode.c, hfs_fs.h:
+       Got rid of HFS_FAKE_EXEC in favor of noexec mount option.
+
+       * inode.c, super.c, DOC, hfs_fs_sb.h:
+       Added "quiet" mount option, like the fat filesystem.
+
+       * inode.c, dir_cap.c, dir_nat.c:
+       Pseudo-directories are read-only (at least for now).
+
+       * hfs_fs.h, dir_dbl.c, dir_nat.c, dir.c, dir_cap.c:
+       mkdir() updated to check against reserved names, but the
+       AppleDouble scheme still has problems with names starting with '%'.
+
+       * dir_dbl.c, dir_nat.c, hfs_fs.h, dir.c, dir_cap.c:
+       Added mkdir(). (It only took 2 tries to get it right!!)
+       Only works in "normal" directories and doesn't yet stop
+       one from creating dirs with the reserved names.
+
+       * brec.c, extent.c, bins_del.c:
+       Now have a way to get an EEXIST back from hfs_binsert().
+
+       * btree.c, inode.c, hfs_fs_i.h, file.c, bfind.c, bnode.c, balloc.c:
+       Added 'dev' field to struct hfs_file.
+
+       * hfs_fs_i.h, inode.c, btree.c, extent.c, file.c, bnode.c, brec.c,
+         balloc.c:
+       Removed duplicated fields from struct hfs_file since
+       even B*-trees now have that information in the inode.
+
+       * extent.c:
+       zero_blocks() neglected allocation block size in computing start.
+
+Fri Mar 29 16:04:37 1996  Paul H. Hargrove  <[email protected]>
+
+       * super.c:
+       hfs_statfs(): f_files and f_ffree fields are now -1, which is
+       documented as the value for "undefined" fields in struct statfs.
+
+       * trans.c, inode_nat.c, string.c, super.c, inode_dbl.c, inode_cap.c,
+         inode.c, file_nat.c, file_dbl.c, file_cap.c, file.c, dir_dbl.c,
+         extent.c, dir_cap.c, catalog.c, btree.c, brec.c, bnode.c, bitops.c,
+         bitmap.c, bins_del.c, balloc.c:
+       Stylistic editing: {} for all 'for', 'while' and 'if' blocks.
+       I hope I didn't screw-up anything.
+
+       * hfs_fs.h, dir.c, dir_cap.c, dir_dbl.c, dir_nat.c:
+       Added creation of normal files to all three fork schemes!
+       Strange things may happen when trying to create "non-normal" files.
+
+       * brec.c:
+       Cleaned up some debugging code.
+
+       * hfs_fs_i.h:
+       File and directory counts could have overflown 16-bit integer.
+
+       * hfs_btree.h:
+       Added HFS_BREC_RIGHT to help fix insertion problem.
+
+       * extent.c:
+       Various fixes to hfs_{file,btree}_extend().
+
+       * catalog.c:
+       Made hfs_build_cat_key() more "correct".
+
+       * btree.c:
+       Added and fixed debugging code.
+
+       * brec.c:
+       Fixed overflow detection.
+       Added some debugging code.
+
+       * bnode.c:
+       Dirtied some buffers in places that might have been missed.
+       Fixed some debugging code that had broken.
+
+       * bitops.c:
+       hfs_count_free_bits() was running off end of bitmap.
+
+       * bins_del.c:
+       Fixed various bugs, mostly related to variable-length keys.
+
+       * balloc.c:
+       Had forgotten to set a bit in new mapnodes.
+       Node counts were overflowing 16-bit integers.
+
+       * bitmap.c:
+       Oops! clear/set did opposite operation on full words.
+
+Wed Mar 27 10:59:07 1996  Paul H. Hargrove  <[email protected]>
+
+       * hfs_fs_i.h:
+       Updated struct hfs_extent for concurrent access.
+       Also caused a slight modification to struct hfs_file.
+
+       * hfs_fs.h, hfs_btree.h:
+       Added/updated prototypes.
+
+       * balloc.c:
+       hfs_bnode_alloc() finished but still untested.
+
+       * bins_del.c:
+       Fixed up deadlock avoidance in hfs_binsert() again.
+       Perhaps I even got it right this time.
+
+       * extent.c:
+       hfs_file_extend() now safe under concurrent operations?
+
+       * file.c:
+       hfs_getblk() now safe under concurrent operations?
+
+Tue Mar 26 23:26:35 1996  Paul H. Hargrove  <[email protected]>
+
+       * btree.c:
+       Added call to hfs_extent_trim() to fix memory leak.
+
+       * extent.c:
+       Oops, had left a "#define static" in from debugging.
+
+       * bins_del.c:
+       hfs_binsert() rewritten to avoid deadlock when extending
+       the extents B*-tree.
+
+       * btree.c:
+       Moved hfs_btree_extend() to extent.c
+
+       * inode_nat.c, inode_cap.c, inode_dbl.c:
+       hfs_*_put_inode() rewritten to call hfs_extent_trim().
+
+       * extent.c:
+       Big rewrite for new struct hfs_extent:
+               Now keep linked list of extents.
+               Cache is now a pointer to a list element.
+               Now have 'end' field to aid decode_extent().
+       New functions:
+               hfs_extent_trim(): frees linked list.
+               hfs_btree_extend(): for extending B*-trees.
+       Improved debugging output.
+
+       * balloc.c:
+       Added hfs_bnode_add() (incomplete and uncommented).
+
+       * btree.c:
+       Moved some work from hfs_btree_extend() to hfs_bnode_add().
+
+       * bfind.c:
+       Added hfs_bfind_first() as wrapper for hfs_brec_find_first().
+
+       * brec.c:
+       Added hfs_brec_find_first() to search first leaf node.
+
+       * bins_del.c:
+       Added error returns to hfs_binsert() and binsert().
+
+       * bins_del.c:
+       Check to see that we really need ancestors before starting.
+       Check that hfs_btree_alloc() gave us enough nodes.
+       binsert() uses info precomputed by hfs_binsert().
+
+Mon Mar 25 11:33:53 1996  Paul H. Hargrove  <[email protected]>
+
+       * bnode.c:
+       Collected together the error returns in hfs_bnode_lock().
+
+       * Makefile:
+       Added ChangeLog to $(MISC).
+
+Wed Mar 20 19:41:45 1996  Paul H. Hargrove  <[email protected]>
+
+       * super.c, hfs_fs.h, file.c, dir_dbl.c, dir_nat.c, dir.c, dir_cap.c:
+       Removed support for kernels older than about 1.3.70
+       Most of that support had been broken recently anyway.
+
+       * super.c:
+       Fixed so DEBUG_MEM works w/o DEBUG_ALL.
+       Updated call to hfs_btree_init().
+
+       * hfs_fs.h:
+       Updated/added prototypes.
+
+       * hfs_btree.h:
+       HFS_BFIND_CHAIN removed.
+       struct hfs_brec gets new 'flags' field with bits:
+               HFS_BREC_{FIRST,OVERFLOW,UNDERFLOW,UNINITIALIZED}
+       Removed bitmap size constants.
+       Changes to struct hfs_btree:
+               'file' and 'cache' now structs rather than pointers.
+               Added 'reserved' field (used during insertion).
+               Added pointers to size and extent in MDB.
+
+       * file.c:
+       Made hfs_getblk() public.
+       Removed (fil->inode == NULL) special cases.
+
+       * extent.c:
+       {find,update}_ext() are no longer inline.
+       new_extent() fails when called for the extents tree;
+               previously it would hanging calling hfs_binsert().
+       extend_file():
+               renamed to hfs_file_extend() and made public.
+               fixed to work for B*-trees.
+               zeros-out blocks as they are allocated.
+               fixed bugs for (allocation block) != (physical block).
+
+       * btree.c:
+       hfs_btree_{init,free}() modified for changes to struct:
+               'file' and 'cache' moved back into structure
+               file.inode initialized to reduce special cases
+       hfs_btree_init() gets pointer to size in MDB instead of size.
+       Added hfs_btree_extend() (incomplete and uncommented).
+
+       * bnode.c:
+       hfs_bnode_{alloc,free}() moved to separate file.
+       Removed 'const' from some function arguments
+               due to change in struct hfs_btree.
+       hfs_bnode_lock(): added WRITE/RESRV->READ transition.
+
+       * brec.c:
+       hfs_brec_get_{root,child}() now take a 'keep_mask' argument
+               indicating when to keep ancestor nodes, and store
+               information about why ancestors were kept.
+       HFS_BFIND_CHAIN eliminated in favor of HFS_BFIND_{INSERT,DELETE}
+               which are now implemented using 'keep_mask'.
+       Added hfs_brec_relse_one() that doesn't release ancestors.
+
+       * bins_del.c:
+       Lots of rewrites to cleanup insertion.
+       Now tries to extend tree before insertion starts.
+       binsert() iterative rather than recursive.
+       No point in keeping track as it is still not "stable".
+
+       * balloc.c:
+       New file: started with hfs_bnode_{free,alloc}()
+       Added hfs_bnode_init() to initialize a newly allocated bnode.
+       hfs_bnode_free():
+               Renamed hfs_bnode_bitop().
+               Can set or clear a specified bit.
+               Gets bitmap sizes from nodes directly.
+       hfs_bnode_alloc():
+               Returns actual node, calling hfs_bnode_init().
+               Gets bitmap sizes from nodes directly.
+
+       * bfind.c:
+       Removed obsolete comment from hfs_bsucc()
+       Removed 'const' from tree arg of hfs_bfind()
+               due to changes in struct hfs_btree.
+
+       * Makefile:
+       Added new file: balloc.c
+
+Sat Mar  9 22:03:53 1996  Paul H. Hargrove  <[email protected]>
+
+       * Start of detailed CVS logging.
+
+Mar 09, 1996: snapshot-09Mar96 [email protected] (Paul H. Hargrove)
+       NOT AN OFFICIAL RELEASE
+       Fixed up debugging code that was broken by split of btree.c
+       Added debugging kmalloc/kfree
+       Fixed memory leak in hfs_bnode_relse()
+
+Mar 08, 1996: snapshot-08Mar96 [email protected] (Paul H. Hargrove)
+       NOT AN OFFICIAL RELEASE
+       now reset blocksize on device when done.
+       hfs_binsert done (except for the full tree case).
+       btree.c split up into manageable pieces (need to sort out hfs_btree.h)
+
+Feb 26, 1996: snapshot-26Feb96 [email protected] (Paul H. Hargrove)
+       NOT AN OFFICIAL RELEASE
+       Some writability.
+       Bug with multiple opens of meta data fixed.
+       Netatalk support no longer considered experimental.
+
+Virtually everything has changed, so I've lost track here.
+
+Nov 16, 1995: snapshot-16Nov95 [email protected] (Paul H. Hargrove)
+       NOT AN OFFICIAL RELEASE
+       Still more comments.
+       btree.c back to 80 columns.  will do same to other files soon.
+       Starting with btree.c have begun to put file contents into some
+               sort of standard order.
+       Moved metadata reading to VFS open() routine and now free it in
+               the VFS release() routine.  Much cleaner than the old way.
+       Unified hfs_iget by shifting scheme-dependent code into a function
+               pointer in the superblock.  This could/should be shifted to
+               a VFS read_inode() routine if that can be done cleanly.
+       Probably lots of other changes; I've lost track.
+
+Nov 05, 1995: version 0.5.3 [email protected] (Paul H. Hargrove)
+       NOT AN OFFICIAL RELEASE
+       1.2.x compatibility removed
+       Added lots of comments to btree.c and cleanup some code.  The result
+               is that the source file doubled in size while the object
+               file dropped in size by 20%.
+       Added some comments to super.c and dir.c as well.
+       Cleaned up some stuff in dir.c adding some additional error checking
+               and moving closer to using a unified hfs_iget by migrating
+               common code into lookup_parent().
+       Changed btree.c to use a separate bnode cache per filesystem.
+       Renamed a bunch of the bnode functions in btree.c
+
+Jun 29, 1995: version 0.5.2 [email protected] (Paul H. Hargrove)
+       BUG FIX and 1.3.x-compatibility release.
+       Will compile under 1.2.x or 1.3.x by changing one line in Makefile.
+       Started adding magic numbers to structures for "safety".
+       Don't strip internal symbols when linking or loading, as this made
+               good bug reports rather difficult.
+       Fixed a bug that could cause the fs to lock-up after trying to open
+               a non-existent file.
+       Fixed a bug that allowed files to appear truncated, when in fact it
+               is still not possible to truncate a file.
+       Added more/better comments to header files.
+       Deal with volume and b-tree bitmaps in preparation for writing.
+       Fixed readdir() to deal properly with the case where the directory
+               changes while writing to user-space. (which can't yet
+               actually happen, until directories are writable).
+
+Jun 23, 1995: version 0.5.1 [email protected] (Paul H. Hargrove)
+       BUG FIX RELEASE
+       Removed two debugging messages that didn't belong.
+       Fixed a typo that prevented modified inodes from being written to disk.
+       Added a missing line which prevented rmmod'ing sometimes.
+       Added a missing line which caused errors when modifying .finderinfo or
+               .resource under the CAP system.
+       Added a notify_change() to keep mode bits sensible, and to cause
+               changes to an inode to affect the data fork and resource fork
+               of a file together.
+
+Jun 22, 1995: version 0.5 [email protected] (Paul H. Hargrove)
+       Fixed a bug that was giving wrong values for i_blocks
+       Partly writable (can only 'touch' existing files, so far)
+       Removed case= mount option.  It will be back eventually.
+       Can now deal with CDROMs (and hard disks?), many thanks to
+               Holger Schemel for this work.
+       Latin-1 filename conversion also due to Holger Schemel.
+       Rewritten btree operations.
+
+Feb 28, 1995: version 0.4 [email protected] (Paul H. Hargrove)
+       Requires Linux >= 1.1.94: depends on changes made to asm/byteorder.h
+       Now using string comparison code donated by ARDI (see string.c)
+       Code reorganized to use data structures more like ARDI's.
+       More code reorganization to abstract the btree operations.
+       Added the fork= mount option.
+       Added AppleDouble support.  Executor, from ARDI, can now run programs
+               from HFS filesystems mounted w/ the HFS module.
+
+Jan 28, 1995: version 0.3 [email protected] (Paul H. Hargrove)
+       Major code reorganization.
+       Known for certain to work ONLY on floppies.
+       Started caching extents, so got faster on long file reads.
+       Now compiles separate from kernel tree.
+       Supports 5 filename conversion methods.
+       Supports forks, using the method from CAP.
+       All external symbols now start with HFS_ or hfs_
+
+Jan 12, 1995: version 0.2 [email protected] (Paul H. Hargrove)
+       Should now work on all HFS volumes, but still only tested on floppies.
+       Got smaller and faster with some code reorganization.
+       Since Linus moved htons() and friends to an asm file, should now be
+        truly endian-independent, but still only tested on Intel machines.
+       Requires Linux >= 1.1.77, since Linus moved htons().
+
+Jan 05, 1995: version 0.1 [email protected] (Paul H. Hargrove)
+       First release.
+       1.44Mb floppies only
+       no resource forks
+       trivial name mangling only
+       read only
+       for Linux >= 1.1.75
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/fs/hfs/Makefile linux-2.0.29/fs/hfs/Makefile
--- linux.vanilla/fs/hfs/Makefile       Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/fs/hfs/Makefile        Thu Apr 10 12:39:44 1997
@@ -0,0 +1,14 @@
+#
+# Makefile for the linux ufs-filesystem routines.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+O_TARGET := hfs.o
+O_OBJS   := balloc.o bdelete.o bfind.o bins_del.o binsert.o bitmap.o bitops.o bnode.o brec.o btree.o catalog.o dir.o dir_cap.o dir_dbl.o dir_nat.o extent.o file.o file_cap.o file_hdr.o inode.o mdb.o part_tbl.o string.o super.o sysdep.o trans.o version.o
+M_OBJS   := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
Binary files linux.vanilla/fs/hfs/README.dvi and linux-2.0.29/fs/hfs/README.dvi differ
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/fs/hfs/README.sgml linux-2.0.29/fs/hfs/README.sgml
--- linux.vanilla/fs/hfs/README.sgml    Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/fs/hfs/README.sgml     Wed Apr  9 04:41:47 1997
@@ -0,0 +1,1255 @@
+<!doctype linuxdoc system> <!-- -*- sgml -*- -->
+<article>
+<!-- Title information -->
+<title>Macintosh HFS Filesystem for Linux
+<author>Paul H. Hargrove, <tt><htmlurl url="mailto:[email protected]"
+name="[email protected]"></tt>
+<date>version 0.8.3, 08 Apr 1997
+<abstract>
+This document describes version 0.8.3 of <tt>hfs_fs</tt>,
+a Linux kernel loadable module implementing the Macintosh HFS filesystem.
+The most current versions of this document and the software are kept at
+<url url="http://www-sccm.Stanford.EDU/&tilde;hargrove/HFS/"
+name="The HFS for Linux Page">.
+</abstract>
+<!-- Table of contents -->
+<toc>
+<!-- Begin the document -->
+<sect>Introduction
+<p>
+This package is a Macintosh HFS filesystem module for Linux.  It allows
+you to read and write Macintosh HFS filesystems
+on floppy disks, CDROMs, hard drives, ZIP drives, etc.
+It is <em>not</em> an AppleShare client.
+<p>
+If you use this software, please send me a note telling of your success
+or failure with it.  Your feedback lets me know that this project is not
+a waste of my time.
+<p>
+This code is still experimental, so backup anything important before you
+start playing.  I'd like you to know that I've never lost any files while
+using this software, or I would not release it.  However, a ``better
+safe than sorry'' attitude is probably best.
+<p>
+If, for instance, the buffer cache were to become corrupted you could
+start losing things on other disks.  Because of this, if you get a
+General Protection Fault, or a kernel Oops, I <em>strongly</em> recommend
+that you reboot before writing any files.
+<!--  -->
+<sect>System Requirements
+<p>
+You will need the following to compile and use this release of <tt>hfs_fs</tt>:
+<itemize>
+       <item>
+       Kernel version 2.0.1 or newer
+       compiled with modules enabled (<tt>CONFIG_MODULES</tt>).
+       <!-- -->
+       <item>
+       The kernel sources (or at least the header files) available online.
+       <!-- -->
+       <item>
+       The module utilities package current for your kernel version and
+       an understanding of how to use it.  (The file
+       <tt>Documentation/modules.txt</tt> in the kernel source directory
+       provides a brief introduction.)
+       <!-- XXX: add a URL for the module utilities -->
+</itemize>
+<p>
+In theory the code is endian-independent, but is tested most extensively
+on Intel platforms.  It is also known to compile on m68k, PPC and Alpha
+machines, but is not as extensively tested on any of these platforms.
+<!--  -->
+<sect>Installation
+<p>
+The <tt>hfs_fs</tt> code is not yet part of the official kernel distribution.
+Therefore, it is compiled as a module and then loaded into the kernel
+using the module utilities.  Therefore, your kernel must be compiled
+with <tt>CONFIG_MODULES</tt> enabled.
+<!-- -->
+<sect1>Compiling the loadable module
+<p>
+To compile <tt>hfs.o</tt> you should only need to execute <tt>make</tt>
+in the <tt>hfs_fs</tt> source directory.
+<p>
+If <tt>gcc</tt> complains about not finding a large number of header
+files with names beginning with ``linux/'' then you probably don't have
+the kernel header files installed correctly.  Either
+<tt>/usr/include/linux</tt>, <tt>/usr/include/asm</tt> and
+<tt>/usr/include/scsi</tt> should be symbolic links to <tt>include/linux</tt>,
+<tt>include/asm</tt> and <tt>include/scsi</tt> in the
+kernel source tree for the kernel you wish to use <tt>hfs_fs</tt> with,
+or else they should be directories containing the header files for the
+kernel you wish to use <tt>hfs_fs</tt> with.
+<p>
+If <tt>gcc</tt> complains about not finding <tt>linux/version.h</tt>,
+then you will need to run <tt>make dep</tt> in the kernel source
+directory to build it.
+<p>
+If <tt>gcc</tt> complains about not finding the files
+<tt>linux/config.h</tt> or <tt>linux/autoconf.h</tt>, then you will
+need to run <tt>make config</tt> and <tt>make dep</tt> in the kernel
+source directory to build these two files and <tt>linux/version.h</tt>.
+<p>
+If you are compiling on a DEC Alpha and receive messages saying
+<tt>assignment from incompatible pointer type</tt> when compiling
+files <tt>dir_*.c</tt> and <tt>file_*.c</tt>, then you need to change
+a single line in the file <tt>hfs_fs.h</tt>.  Remove the text
+``<tt>&amp;&amp; !defined(__alpha__)</tt>'' from the end of line 217.
+<!-- -->
+<sect1>Installing the module in the modules directory (optional)
+<p>
+If you plan to use <tt>kerneld</tt> to automatically load the module
+or if you wish to use <tt>modprobe</tt> or <tt>insmod</tt> without
+supplying a complete path to <tt>hfs.o</tt>, then you will need to
+copy <tt>hfs.o</tt> into a directory where the module utilities
+expect to find it.
+<p>
+The proper directory may depend slightly on your configuration.
+However, <tt>/lib/modules/default/fs/</tt> is a common one for filesystem
+modules.  Once <tt>hfs.o</tt> is in the proper directory you should run
+<tt>depmod -a</tt> to update the dependency list used by
+<tt>kerneld</tt> and <tt>modprobe</tt>.
+<!-- -->
+<sect1>Loading the module into the running kernel
+<p>
+There are three ways to accomplish this:
+<enum>
+       <item>
+       If you are running <tt>kerneld</tt> and have installed <tt>hfs.o</tt>
+       in the modules directory then you don't need
+       to issue any commands; the module will be loaded when you
+       attempt to mount an HFS filesystem.
+       <!-- -->
+       <item>
+       If you are <em>not</em> running <tt>kerneld</tt> then you
+       can load <tt>hfs.o</tt> manually by running
+       <tt>modprobe hfs.o</tt>.  If you have not installed
+       <tt>hfs.o</tt> in one of the standard module directories,
+       then you will need provide a full path to the file
+       <tt>hfs.o</tt>.
+       <!-- -->
+       <item>
+       If you have been experiencing kernel crashes with
+       <tt>hfs_fs</tt>, then you should file a bug report
+       including the names of the functions which the EIP
+       and Stack Trace point into.
+       To help with this you can ask for relocation map for the
+       module when you load it.  To do this load the module with
+       <tt>insmod -m hfs.o >loadmap</tt>.  Again, you may need a
+       full path to the file <tt>hfs.o</tt> if you have not placed
+       it in one of the standard module directories.
+</enum>
+<!-- -->
+<sect1>Using the module with versioned symbols
+<p>
+All the interface between the module and the kernel take place through
+very stable (since the mid-1.3.x kernels) parts of the kernel.  If you
+enabled versioned symbols (<tt>CONFIG_MODVERSIONS</tt>) when you compiled
+your kernel you should often be able to compile this module once and
+then use it with many kernels newer than the one you compiled it for.
+<p>
+In any case, it is unlikely that this module will need changes with
+each new kernel patch; simple recompilation should usually suffice.
+<!--  -->
+<sect>Mounting HFS Filesystems
+<p>
+Once you have installed the module, you will be able to use
+<tt>hfs</tt> as a filesystem type option to <tt>mount</tt>.  For
+instance, to mount a Macintosh floppy disk on the directory
+<tt>/mnt</tt> using the default mount options you would execute
+<tt>mount -t hfs /dev/fd0 /mnt</tt>.
+<p>
+The remainder of this section describes the several mount options available
+to control how the HFS filesystem is mapped onto a Linux structure.  The
+values for the multiple-choice options (<tt>case</tt>, <tt>conv</tt>,
+<tt>fork</tt> and <tt>names</tt>) can be abbreviated by their first character.
+<!-- -->
+<sect1>afpd
+<p>
+If included in the options, then the behavior of <tt>hfs_fs</tt> is
+changed to make it fully read-write compatible with Netatalk's afpd.
+In this mode you should not use normal user-level tools to modify the
+filesystem, though reading from it is acceptable.  This is because the
+return codes from some system calls are changed to fool afpd.  These
+changes will confuse many user-level tools.  In particular
+<tt>rm -r</tt> will loop forever.
+<p>
+This option implies <tt>fork=netatalk</tt>, which in turn implies
+<tt>names=netatalk</tt>.  If either of these options are explicitly
+set to something else they will take precedence and will confuse afpd.
+The <tt>quiet</tt> option has no effect.  The <tt>case=</tt> option
+functions normally, but afpd usually does the same thing for you.  The
+<tt>conv=</tt> and <tt>part=</tt> options also function normally.
+<p>
+You will probably want to use the <tt>uid=</tt>, <tt>gid=</tt> and
+<tt>umask=</tt> mount options.  Note that because all the files on an
+HFS filesystem belong to a single user and group and have a single
+umask, the full AppleShare permission scheme will not work.
+<p>
+One additional limitation is that the Desktop database on the disk is
+stored in afpd's format and is separate from any existing database
+maintained by the Finder when the volume is used on a Macintosh.
+<p>
+This mode is known to be compatible with afpd from Netatalk versions
+1.4b1 and 1.4b2, and known to be incompatible with the afpd from
+version 1.3.3.  As of this writing Netatalk version 1.4 has not yet
+been released.  However, it is likely that <tt>hfs_fs</tt>'s afpd mode
+will be compatible with afpd from Netatalk version 1.4 when it is
+released.
+<!--  -->
+<sect1>case={asis, lower}
+<p>
+default value: <tt>asis</tt>
+<p>
+This option determines if Mac filenames are presented in their original
+case or in all lowercase.  Filename matching is not affected, so
+either way <tt>foo</tt> and <tt>Foo</tt> refer to the same file but
+<tt>ls</tt> will list <tt>Foo</tt> with <tt>case=asis</tt>, and
+<tt>foo</tt> with <tt>case=lower</tt>.
+(Same as for the HPFS filesystem.)
+       <descrip>
+       <tag><tt>asis</tt></tag>
+       Filenames are reported in the case they were created with.
+       <tag><tt>lower</tt></tag>
+       Filenames are reported in lowercase.
+       </descrip>
+<!--  -->
+<sect1>conv={auto, binary, text}
+<p>
+default value: <tt>binary</tt>
+<p>
+This option controls <tt>CR</tt>&lt;-&gt;<tt>NL</tt> conversion of Macintosh
+<em>data forks</em>.
+Any translation takes place only for files accessed with the <tt>read()</tt>
+and <tt>write()</tt> system calls (either directly or through
+the stdio functions).  Access through <tt>mmap()</tt> is unaffected.
+(Similar to the <tt>conv=</tt> option for the MS-DOS filesystem.)
+       <descrip>
+       <tag><tt>auto</tt></tag>
+       If the Finder's type for a file is <tt>TEXT</tt> or <tt>ttro</tt>,
+       then <tt>CR</tt> characters are converted to <tt>NL</tt> characters
+       when read, and <tt>NL</tt> characters are converted to <tt>CR</tt>
+       characters when written.
+       <p>
+       Please e-mail me if you know of any other types that
+       indicate pure text files.
+       <!--  -->
+       <tag><tt>binary</tt></tag>
+       No <tt>CR</tt>&lt;-&gt;<tt>NL</tt> conversion is done.
+       <!--  -->
+       <tag><tt>text</tt></tag>
+       In all data forks, regardless of the Finder's type for the file,
+       <tt>CR</tt> characters are converted to <tt>NL</tt> characters
+       when read, and <tt>NL</tt> characters are converted to <tt>CR</tt>
+       characters when written.
+       </descrip>
+<!--  -->
+<sect1>creator=cccc
+<p>
+default value: ``????''
+<p>
+Specifies the 4-character string specifying the Finder's Creator for new files.
+<!--  -->
+<sect1>fork={cap, double, netatalk}
+<p>
+default value: <tt>cap</tt>
+<p>
+This option determines how resource forks and the Finder's metadata are
+represented within the structure of the Linux filesystem.
+       <descrip>
+       <tag><tt>cap</tt></tag>
+       The scheme used by the Columbia AppleTalk Package's AUFS.
+       <p>
+       Associated with each directory are two special directories and a
+       metadata file.  The directory <tt>./bar</tt> is represented by:
+               <descrip>
+               <tag><tt>./bar</tt></tag>
+               The directory itself, containing subdirectories, the data
+               forks of files, and the following two special directories.
+               <!--  -->
+               <tag><tt>./bar/.resource</tt></tag>
+               A special directory holding resource forks of the
+               files in <tt>./bar</tt>.
+               <!--  -->
+               <tag><tt>./bar/.finderinfo</tt></tag>
+               A special directory holding metadata files for the files and
+               subdirectories in <tt>./bar</tt>.
+               <!--  -->
+               <tag><tt>./.finderinfo/bar</tt></tag>
+               The metadata file for the directory <tt>./bar</tt>.
+               </descrip>
+       <p>
+       The files in a directory are represented as three files:
+               <descrip>
+               <tag><tt>./foo</tt></tag>
+               The data fork of the file <tt>./foo</tt>.
+               <!--  -->
+               <tag><tt>./.resource/foo</tt></tag>
+               The resource fork of the file <tt>./foo</tt>.
+               <!--  -->
+               <tag><tt>./.finderinfo/foo</tt></tag>
+               The metadata file for the file <tt>./foo</tt>.
+               </descrip>
+       <p>
+       Additionally, the file <tt>.rootinfo</tt> in the root directory
+       of the HFS filesystem is a metadata file for the root directory.
+       <p>
+       Documentation on the format of file containing the Finder's metadata
+       is included in the Columbia AppleTalk Package.
+       <!-- XXX: Add URL for CAP homepage -->
+       <!--  -->
+       <tag><tt>double</tt></tag>
+       The ``AppleDouble'' format recommended by Apple.  (Apple's other
+       recommended format, ``AppleSingle'', is not yet implemented.)
+       <p>
+       Associated with each directory is an AppleDouble ``header file''.
+       The directory <tt>./bar</tt> is represented by:
+               <descrip>
+               <tag><tt>./bar</tt></tag>
+               The directory itself, containing subdirectories, the data
+               forks for files, and the header files for files and
+               subdirectories.
+               <!--  -->
+               <tag><tt>./&percnt;bar</tt></tag>
+               The header file for the directory <tt>./bar</tt>, containing
+               the Finder's metadata for the directory.
+               </descrip>
+       <p>
+       The files in a directory are represented as two files:
+               <descrip>
+               <tag><tt>./foo</tt></tag>
+               The data fork of the file <tt>./foo</tt>.
+               <!--  -->
+               <tag><tt>./&percnt;foo</tt></tag>
+               The header file for the file <tt>./foo</tt>, containing the
+               resource fork and the Finder's metadata for the file.
+               </descrip>
+       <p>
+       Additionally, the file <tt>&percnt;RootInfo</tt> in the root
+       directory of the HFS filesystem is a header file for the root
+       directory.  This is not quite the <tt>&percnt;RootInfo</tt>
+       file referred to in the AppleDouble specification.
+       <p>
+       The header files used in this scheme are version 2 AppleDouble
+       header files.  Their format is documented in
+       ``AppleSingle/AppleDouble Formats: Developer's Note (9/94)'',
+       available from from <url url="http://devworld.apple.com"
+       name="Apple's Developer Services Page">.
+       <!-- XXX: give full URL for the document -->
+       <p>
+       Note that the naming convention for the header file can cause
+       name conflicts.  For instance, using Apple's 7-bit ASCII name
+       conversion the name <tt>&percnt;Desktop</tt> could be interpreted
+       either as the header file for the file <tt>Desktop</tt> or as the
+       file with 0xDE as the hexadecimal representation of its first
+       character, and &dquot;sktop&dquot; as the remaining 5 characters.
+       The problem arises when both files exist, since only one will be
+       accessible.  The behavior of the
+       HFS module in the case of such a conflict is undefined, and
+       may change in future releases.
+       (If this causes problems for you, please don't report it as
+       a bug; I didn't design this ``standard'', Apple did.)
+       <!--  -->
+       <tag><tt>netatalk</tt></tag>
+       The scheme used by the Netatalk afpd.
+       <p>
+       Associated with each directory is a special directory and a
+       metadata file.  The directory <tt>./bar</tt> is represented by:
+               <descrip>
+               <tag><tt>./bar</tt></tag>
+               The directory itself, containing subdirectories, the data
+               forks of files, and the following special directory.
+               <!--  -->
+               <tag><tt>./bar/.AppleDouble</tt></tag>
+               A special directory holding AppleDouble header files
+               for <tt>./bar</tt> and the files it contains, but not for
+               the subdirectories it contains.
+               <!--  -->
+               <tag><tt>./bar/.AppleDouble/.Parent</tt></tag>
+               The header file for the directory <tt>./bar</tt>,
+               containing the Finder's metadata for the directory.
+               </descrip>
+       <p>
+       The files in a directory are represented as two files:
+               <descrip>
+               <tag><tt>./foo</tt></tag>
+               The data fork of the file <tt>./foo</tt>.
+               <!--  -->
+               <tag><tt>./.AppleDouble/foo</tt></tag>
+               The header file for file <tt>./foo</tt>, containing the
+               resource fork and the Finder's metadata.
+               </descrip>
+       <p>
+       The header files used in this scheme are version 1 AppleDouble
+       header files.  Their format is documented in the ``Apple II
+       File Type Notes'' under the type
+       ``&dollar;E0.0002/&dollar;E0.0003-AppleDouble'', and in
+       Appendix B of the ``A/UX Toolbox: Macintosh ROM Interface'' manual.
+       <!-- XXX: give URL for the file type notes -->
+       <!-- XXX: Add URL for Netatalk homepage -->
+       </descrip>
+<!--  -->
+<sect1>gid=n
+<p>
+default value: gid of the mounting process
+<p>
+Specifies the group that owns all files and directories on the filesystem.
+(Same as for the MS-DOS and HPFS filesystems.)
+<!--  -->
+<sect1>names={7bit, 8bit, alpha, cap, latin, netatalk, trivial}
+<p>
+default value: varies as follows
+       <itemize>
+       <item> If the <tt>fork</tt> option is set to <tt>double</tt>,
+               then <tt>names</tt> defaults to <tt>alpha</tt>.
+       <!-- -->
+       <item> If the <tt>fork</tt> option is set to <tt>netatalk</tt>,
+               then <tt>names</tt> defaults to <tt>netatalk</tt>.
+       <!-- -->
+       <item>If the <tt>fork</tt> option is set to <tt>cap</tt>
+               (or has taken that value by default),
+               then <tt>names</tt> defaults to <tt>cap</tt>.
+       </itemize>
+<p>
+This option determines how to convert between valid Macintosh filenames
+and valid Linux filenames.
+The <tt>7bit</tt>, <tt>8bit</tt> and <tt>alpha</tt>
+options correspond to Apple's recommended conventions named
+``7-bit ASCII'', ``8-bit'' and ``7-bit alphanumeric''.
+<p>
+       <descrip>
+       <tag><tt>7bit</tt></tag>
+       When converting from Macintosh filenames to Linux filenames
+       the NULL (0x00), slash (/) and percent (&percnt;) characters
+       and the extended 8-bit characters (hexadecimal codes 0x80-0xff)
+       are replaced by a percent character (&percnt;) followed by the
+       two-digit hexadecimal code for the character.
+       <p>
+       When converting from Linux filenames to Macintosh filenames
+       the string &dquot;&percnt;YZ&dquot; is replaced by the character
+       with hexadecimal code 0xYZ.  If 0xYZ is not a valid
+       hexadecimal number or is the code for NULL or colon (:)
+       then the string &dquot;&percnt;YZ&dquot; is unchanged.  A colon
+       (:) is replaced by a pipe character (|).
+       <!--  -->
+       <tag><tt>8bit</tt></tag>
+       When converting from Macintosh filenames to Linux filenames
+       the NULL (0x00), slash (/) and percent (&percnt;) characters
+       are replaced by a percent character (&percnt;) followed by the
+       two-digit hexadecimal code for the character.
+       <p>
+       When converting from Linux filenames to Macintosh filenames
+       the string &dquot;&percnt;YZ&dquot; is replaced by the character
+       with hexadecimal code 0xYZ.  If 0xYZ is not a valid
+       hexadecimal number or is the code for NULL or colon (:)
+       then the string &dquot;&percnt;YZ&dquot; is unchanged.  A colon
+       (:) is replaced by a pipe character (|).
+       <!--  -->
+       <tag><tt>alpha</tt></tag>
+       When converting from Macintosh filenames to Linux filenames
+       only the alphanumeric characters (a-z, A-Z and 0-9), the underscore
+       (_) and the last period (.) in the filename are unchanged.
+       The remaining characters
+       are replaced by a percent character (&percnt;) followed by the
+       two-digit hexadecimal code for the character.
+       <p>
+       When converting from Linux filenames to Macintosh filenames
+       the string &dquot;&percnt;YZ&dquot; is replaced by the character
+       with hexadecimal code 0xYZ.  If 0xYZ is not a valid
+       hexadecimal number or is the code for NULL or colon (:)
+       then the string &dquot;&percnt;YZ&dquot; is unchanged.  A colon
+       (:) is replaced by a pipe character (|).
+       <!--  -->
+       <tag><tt>cap</tt></tag>
+       The convention used by the Columbia AppleTalk Package's AUFS.
+       <p>
+       When converting from Macintosh filenames to Linux filenames the
+       characters from space ( ) through tilde (&tilde;) (ASCII 32-126)
+       are unchanged,
+       with the exception of slash (/).  The slash (/) and all characters
+       outside the range 32-126 are replaced by a colon (:) followed by the
+       two-digit hexadecimal code for the character.
+       <p>
+       When converting from Linux filenames to Macintosh filenames
+       the string &dquot;:YZ&dquot; is replaced by the character
+       with hexadecimal code 0xYZ.  If 0xYZ is not a valid
+       hexadecimal number or is the code for NULL or colon (:)
+       then the colon is replaced by a pipe character (|).
+       <!--  -->
+       <tag><tt>latin</tt></tag>
+       When converting from Macintosh filenames to Linux filenames the
+       characters from space ( ) through tilde (&tilde;) (ASCII 32-126)
+       are unchanged,
+       with the exception of slash (/) and percent (&percnt;).  The
+       extended 8-bit Macintosh characters with equivalents in the Latin-1
+       character set are replaced by those equivalents.  The remaining
+       characters are replaced by a percent character (&percnt;) followed
+       by the two-digit hexadecimal code for the character.
+       <p>
+       When converting from Linux filenames to Macintosh filenames
+       the string &dquot;&percnt;YZ&dquot; is replaced by the character
+       with hexadecimal code 0xYZ.  If 0xYZ is not a valid
+       hexadecimal number or is the code for NULL or colon (:)
+       then the string &dquot;&percnt;YZ&dquot; is unchanged. The Latin-1
+       characters with equivalents in the extended 8-bit Macintosh
+       character set are replaced by those equivalents.
+       A colon (:) is replaced by a pipe character (|).
+       <p>
+       Thanks to Holger Schemel
+       (<tt><htmlurl url="mailto:[email protected]"
+       name="[email protected]"></tt>) for contributing this
+       conversion mode.
+       <!--  -->
+       <tag><tt>netatalk</tt></tag>
+       The convention used by the Netatalk afpd.
+       <p>
+       When converting from Macintosh filenames to Linux filenames the
+       characters from space ( ) through tilde (&tilde;) (ASCII 32-126)
+       are unchanged, with the exception of slash (/) and any initial
+       period (.).
+       The slash (/)  and any initial period (.) and all characters
+       outside the range 32-126 are replaced by a colon (:) followed by the
+       two-digit hexadecimal code for the character.
+       <p>
+       When converting from Linux filenames to Macintosh filenames
+       the string &dquot;:YZ&dquot; is replaced by the character
+       with hexadecimal code 0xYZ.  If 0xYZ is not a valid
+       hexadecimal number or is the code for NULL or colon (:)
+       then the colon is replaced by a pipe character (|).
+       <!--  -->
+       <tag><tt>trivial</tt></tag>
+       When converting from Macintosh filenames to Linux filenames
+       a slash character (/) is replaced by a colon (:).
+       <p>
+       When converting from Linux filenames to Macintosh filenames
+       a colon (:) is replaced by a slash character (/).
+       </descrip>
+<!--  -->
+<sect1>part=n
+<p>
+default value: 0
+<p>
+Specifies which HFS partition to mount from a Macintosh CDROM or
+hard drive.  Partitions are numbered from 0 and count only those
+identified in the partition table as containing HFS filesystems.
+<p>
+Note that in versions before 0.8.3 partitions were numbered from 1.
+<!--  -->
+<sect1>quiet
+<p>
+If included in the options, then <tt>chown</tt> and <tt>chmod</tt>
+operations will not return errors, but will instead fail silently.
+(Same as for the MS-DOS and HPFS filesystems.)
+<!--  -->
+<sect1>type=cccc
+<p>
+default value: ``????''
+<p>
+Specifies the 4-character string specifying the Finder's Type for new files.
+<!--  -->
+<sect1>uid=n
+<p>
+default value: uid of the mounting process
+<p>
+Specifies the user that owns all files and directories on the filesystem.
+(Same as for the MS-DOS and HPFS filesystems.)
+<!--  -->
+<sect1>umask=n
+<p>
+default value: umask of the mounting process
+<p>
+Specifies (in octal) the umask used for all files and directories.
+(Same as for the MS-DOS and HPFS filesystems.)
+<!--  -->
+<sect>Writing to HFS Filesystems
+<p>
+Each of the values of the <tt>fork</tt> mount option yields a
+different representation of the Macintosh-specific parts of a file
+within the structure of the Linux filesystem.  There are, therefore,
+slightly different steps involved in copying files if you want to
+preserve the resource forks and the Finder's metadata.
+<p>
+Regardless of the value of the <tt>fork</tt> mount option you can
+do virtually everything to the data fork of a file that you can
+to a file on any other filesystem.  The limitations are essentially
+the same as those imposed by the MS-DOS filesystem:
+       <itemize>
+       <item>You can't change the uid or gid of files.
+       <item>You can't set the set-uid, set-gid or sticky permission bits.
+       <item>You can't clear the execute permission bits.
+       </itemize>
+<p>
+Likewise you can do virtually everything to a directory that you can to
+a directory on another file system with the following exceptions:
+       <itemize>
+       <item>You can't create, delete or rename resource forks of files
+       or the Finder's metadata.  Note, however, that they are created
+       (with defaults values), deleted and renamed along with the
+       corresponding data fork or directory.
+       <item>You can't change permissions on directories.
+       <item>You can't change the uid or gid of directories.
+       <item>You can't create multiple links to files.
+       <item>You can't create symlinks, device files, sockets or FIFOs.
+       </itemize>
+<!--  -->
+<sect1>Writing with fork=cap
+<p>
+Unlike the other schemes for representing forked files, the CAP scheme
+presents the resource fork as an independent file; the resource fork
+of <tt>./foo</tt> is <tt>./.resource/foo</tt>.  Therefore, you can
+treat it as a normal file.  You can do anything to a resource fork
+that you can do to a data fork, except that you cannot enable execute
+permissions on a resource fork.  Therefore, resource forks are not
+suitable for holding Linux executables or shared libraries.
+<p>
+If you plan to use the resource fork on a Macintosh then you must obey
+the format of a valid resource fork.  This format is documented in
+Chapter 1 of Apple's <em>Inside Macintosh: More Macintosh
+Toolbox</em>.  <tt>hfs_fs</tt> knows nothing about this format and so
+can do nothing to enforce it.
+<p>
+The current support for reading and writing is sufficient to allow
+copying of entire directories with <tt>tar</tt>, as long as both the
+source and destination are mounted with <tt>fork=cap</tt>.
+<tt>tar</tt> may complain about being unable to change the
+<tt>uid</tt>, <tt>gid</tt> or <tt>mode</tt> of files.  This is normal
+and is an unavoidable side effect of the having a single <tt>uid</tt>,
+<tt>gid</tt> and <tt>umask</tt> for the entire filesystem.
+<p>
+It is impossible to create a resource fork or a Finder metadata file.
+However, they are created automatically when the data fork is created.
+Therefore, if you wish to copy a single file including both forks and
+the Finder's metadata then you must create the data fork first.  Then
+you can copy the resource fork and the Finder's metadata.  For
+instance to copy the file <tt>foo</tt> to <tt>dir/bar</tt> you should
+do the following:
+       <enum>
+       <item><tt>cp foo dir/bar</tt>
+       <item><tt>cp .resource/foo dir/.resource/bar</tt>
+       <item><tt>cp .finderinfo/foo dir/.finderinfo/bar</tt>
+       </enum>
+You may get ``Operation not permitted'' errors from <tt>cp</tt> when
+it tries to change the permissions on files.  These errors can safely
+be ignored.  This method will work even if the file <tt>dir/bar</tt>
+exists.
+<p>
+If you wish to move <tt>foo</tt> to <tt>dir/bar</tt> and <tt>foo</tt>
+and <tt>dir</tt> are on the same filesystem then you only need to
+execute <tt>mv foo dir/bar</tt> and the resource fork and the Finder's
+metadata will move too.  However, if <tt>foo</tt> and <tt>dir</tt> are
+on different filesystem then this will lose the resource fork and
+metadata.  Therefore, it is safest to always move files as follows:
+       <enum>
+       <item><tt>cp foo dir/bar</tt>
+       <item><tt>cp .resource/foo dir/.resource/bar</tt>
+       <item><tt>cp .finderinfo/foo dir/.finderinfo/bar</tt>
+       <item><tt>rm foo</tt>
+       </enum>
+You may get ``Operation not permitted'' errors from <tt>cp</tt> when
+it tries to change the permissions on files.  These errors can safely
+be ignored.  This method will work even if the file <tt>dir/bar</tt>
+exists.
+<p>
+Directories have no resource fork but you may wish to create a
+directory which has the same location and view on the Finder's screen
+as an existing one.  This can be done by copying the Finder metadata
+file.  To give the directory <tt>bar</tt> the same location, layout,
+creation date and modify date as <tt>foo</tt> you simply execute
+<tt>cp .finderinfo/foo .finderinfo/bar</tt>.
+<p>
+When copying an entire directory with <tt>cp -R</tt> you may also wish
+to copy the metadata for the directory:
+       <enum>
+       <item><tt>cp -R foo bar</tt>
+       <item><tt>cp .finderinfo/foo .finderinfo/bar</tt>
+       </enum>
+You may get ``Operation not permitted'' errors from <tt>cp</tt> when
+it tries to change the permissions on files.  These errors can safely
+be ignored.
+<!--  -->
+<sect1>Writing with fork=double
+<p>
+The current support for reading and writing header files is sufficient
+to allow copying of entire directories with <tt>tar</tt>, as long as
+both the source and destination are mounted with <tt>fork=double</tt>.
+<tt>tar</tt> may complain about being unable to change the
+<tt>uid</tt>, <tt>gid</tt> or <tt>mode</tt> of files.  This is normal
+and is an unavoidable side effect of the having a single <tt>uid</tt>,
+<tt>gid</tt> and <tt>umask</tt> for the entire filesystem.
+<p>
+It is impossible to create a header file.  However, they are created
+automatically when the data fork is created.  Therefore, if you wish
+to copy a single file including both forks and the Finder's metadata
+then you must create the data fork first.  Then you can copy the
+header file.  instance to copy the file <tt>foo</tt> to
+<tt>dir/bar</tt> you should do the following:
+       <enum>
+       <item><tt>cp foo dir/bar</tt>
+       <item><tt>cp &percnt;foo dir/&percnt;bar</tt>
+       </enum> You may get ``Operation not permitted'' errors from
+<tt>cp</tt> when it tries to change the permissions on files.  These
+errors can safely be ignored.  This method will work even if the file
+<tt>dir/bar</tt> exists.
+<p>
+If you wish to move <tt>foo</tt> to <tt>dir/bar</tt> and <tt>foo</tt>
+and <tt>dir</tt> are on the same filesystem then you only need to
+execute <tt>mv foo dir/bar</tt> and the header file will move too.
+However, if <tt>foo</tt> and <tt>dir</tt> are on different filesystem
+then this will lose the header file.  Therefore, it is safest to
+always move files as follows:
+       <enum>
+       <item><tt>cp foo dir/bar</tt>
+       <item><tt>cp &percnt;foo dir/&percnt;bar</tt>
+       <item><tt>rm foo</tt>
+       </enum>
+You may get ``Operation not permitted'' errors from <tt>cp</tt> when
+it tries to change the permissions on files.  These errors can safely
+be ignored.  This method will work even if the file <tt>dir/bar</tt>
+exists.
+<p>
+Directories have no resource fork but you may wish to create a
+directory which has the same location and view on the Finder's screen
+as an existing one.  This can be done by copying the corresponding
+header file.  To give the directory <tt>bar</tt> the same location,
+layout, creation date and modify date as <tt>foo</tt> simply execute
+<tt>cp &percnt;foo &percnt;bar</tt>.
+<p>
+When copying an entire directory with <tt>cp -R</tt> you may also wish
+to copy the header file for the directory as well:
+       <enum>
+       <item><tt>cp -R foo bar</tt>
+       <item><tt>cp &percnt;foo &percnt;bar</tt>
+       </enum>
+You may get ``Operation not permitted'' errors from <tt>cp</tt> when
+it tries to change the permissions on files.  These errors can safely
+be ignored.
+<!--  -->
+<sect1>Writing with fork=netatalk
+<p>
+The current support for reading and writing header files is sufficient
+to allow copying of entire directories with <tt>tar</tt>, as long as
+both the source and destination are mounted <tt>fork=netatalk</tt>.
+<tt>tar</tt> may complain about being unable to change the
+<tt>uid</tt>, <tt>gid</tt> or <tt>mode</tt> of files.  This is normal
+and is an unavoidable side effect of the having a single <tt>uid</tt>,
+<tt>gid</tt> and <tt>umask</tt> for the entire filesystem.
+<p>
+It is impossible to create a header file.  However, they are created
+automatically when the data fork is created.  Therefore, if you wish
+to copy a single file including both forks and the Finder's metadata
+then you must create the data fork first.  Then you can copy the
+header file.  instance to copy the file <tt>foo</tt> to
+<tt>dir/bar</tt> you should do the following:
+       <enum>
+       <item><tt>cp foo dir/bar</tt>
+       <item><tt>cp .AppleDouble/foo dir/.AppleDouble/bar</tt>
+       </enum>
+You may get ``Operation not permitted'' errors from <tt>cp</tt> when
+it tries to change the permissions on files.  These errors can safely
+be ignored.  This method will work even if the file <tt>dir/bar</tt>
+exists.
+<p>
+If you wish to move <tt>foo</tt> to <tt>dir/bar</tt> and <tt>foo</tt>
+and <tt>dir</tt> are on the same filesystem then you only need to
+execute <tt>mv foo dir/bar</tt> and the header file will move too.
+However, if <tt>foo</tt> and <tt>dir</tt> are on different filesystem
+then this will lose the header file.  Therefore, it is safest to
+always move files as follows:
+       <enum>
+       <item><tt>cp foo dir/bar</tt>
+       <item><tt>cp .AppleDouble/foo dir/.AppleDouble/bar</tt>
+       <item><tt>rm foo</tt>
+       </enum>
+You may get ``Operation not permitted'' errors from <tt>cp</tt> when
+it tries to change the permissions on files.  These errors can safely
+be ignored.  This method will work even if the file <tt>dir/bar</tt>
+exists.
+<p>
+Directories have no resource fork but you may wish to create a
+directory which has the same location and view on the Finder's screen
+as an existing one.  This can be done by copying the corresponding
+header file.  To give the directory <tt>bar</tt> the same location,
+layout, creation date and modify date as <tt>foo</tt> you simply
+execute <tt>cp foo/.AppleDouble/.Parent bar/.AppleDouble/.Parent</tt>.
+<p>
+Because the <tt>fork=netatalk</tt> scheme holds the header file for a
+directory within that directory, directories can safely be copied with
+<tt>cp -R foo bar</tt> with no loss of information.  However,
+you may get ``Operation not permitted'' errors from <tt>cp</tt> when
+it tries to change the permissions on files.  These errors can safely
+be ignored.
+<!--  -->
+<sect>A Guide to Special File Formats
+<p>
+Each of the values of the <tt>fork</tt> mount option yields different
+special files to represent the Macintosh-specific parts of a file
+within the structure of the Linux filesystem.  You can write to these
+special files to change things such as the Creator and Type of a file.
+However, to do so safely you must follow certain rules to avoid
+corrupting the data.  Additionally, there are certain fields in the
+special files that you can't change (writes to them will fail
+silently).
+<!-- -->
+<sect1>CAP .finderinfo Files
+<p>
+The Finder's metadata for the file <tt>./foo</tt> in held in the
+file <tt>./.finderinfo/foo</tt>.  The file has a fixed format defined
+in <tt>hfs_fs.h</tt> as follows:
+<tscreen><code>
+struct hfs_cap_info {
+        __u8    fi_fndr[32];            /* Finder's info */
+        __u16   fi_attr;                /* AFP attributes */
+        __u8    fi_magic1;              /* Magic number: */
+#define HFS_CAP_MAGIC1          0xFF
+        __u8    fi_version;             /* Version of this structure: */
+#define HFS_CAP_VERSION         0x10
+        __u8    fi_magic;               /* Another magic number: */
+#define HFS_CAP_MAGIC           0xDA
+        __u8    fi_bitmap;              /* Bitmap of which names are valid: */
+#define HFS_CAP_SHORTNAME       0x01
+#define HFS_CAP_LONGNAME        0x02
+        __u8    fi_shortfilename[12+1]; /* "short name" (unused) */
+        __u8    fi_macfilename[32+1];   /* Original (Macintosh) name */
+        __u8    fi_comln;               /* Length of comment (always 0) */
+        __u8    fi_comnt[200];          /* Finder comment (unused) */
+        /* optional:    used by aufs only if compiled with USE_MAC_DATES */
+        __u8    fi_datemagic;           /* Magic number for dates extension: */
+#define HFS_CAP_DMAGIC          0xDA
+        __u8    fi_datevalid;           /* Bitmap of which dates are valid: */
+#define HFS_CAP_MDATE           0x01
+#define HFS_CAP_CDATE           0x02
+        __u8    fi_ctime[4];            /* Creation date (in AFP format) */
+        __u8    fi_mtime[4];            /* Modify date (in AFP format) */
+        __u8    fi_utime[4];            /* Un*x time of last mtime change */
+};
+</code></tscreen>
+The type <tt>__u8</tt> is an unsigned character, and <tt>__u16</tt> is
+an unsigned 16-bit integer.
+<p>
+Currently only the fields <tt>fi_fndr</tt>, <tt>fi_attr</tt>,
+<tt>fi_ctime</tt> and <tt>fi_mtime</tt> can be changed.  Writes to the
+other fields are silently ignored.  However, you shouldn't write
+random bytes to the other fields, since they may be writable in the
+future.
+<p>
+The <tt>fi_fndr</tt> field is the ``Finder info'' and ``Extended
+Finder info'' for a file or directory.  These structures are described
+in various books on Macintosh programming.  The portion of the most
+interest is probably the first 8 bytes which, for a file, give the
+4-byte Type followed by the 4-byte Creator.
+<p>
+The <tt>fi_attr</tt> field is the AFP attributes of the file or
+directory.  While you can write any value to this field, only the
+``write-inhibit'' bit is significant.  Setting or clearing this bit
+will clear or set the write bits in the file's permissions.  When you
+read from this field anything you may have written is lost.  If the
+file has write permissions enabled then you will read zero from this
+field.  With write permission disabled you will read back <tt>0x01
+0xA0</tt>, which corresponds to setting the ``write-inhibit'',
+``rename-inhibit'' and ``delete-inhibit'' bits.
+<p>
+The <tt>fi_ctime</tt> and <tt>fi_mtime</tt> are the Macintosh created
+and modified time for the file or directory, and are 32-bit signed
+integers in network byteorder giving seconds from 00:00 GMT Jan. 1,
+2000.  <!-- Is this correct? -->
+<!-- -->
+<sect1>AppleDouble Header Files
+<p>
+Both the <tt>fork=double</tt> and <tt>fork=netatalk</tt> schemes for
+representing forked files use AppleDouble header files to contain the
+resource fork and the Finder's metadata together in a single file.
+<p>
+The AppleDouble format specifies a fixed-format header which describes
+which fields are contained in the remainder of the file, where they
+are located in the file and how long they are.  A full description of
+the version 1 format used when <tt>fork=netatalk</tt> is available
+from ??????.  The version 2 format used when <tt>fork=double</tt> is
+documented in ??????.  The discussion that follows assumes you have
+read and understood these documents, which may be difficult until I've
+replaced the ``??????''s above with something more informative :-).
+<p>
+Due to the variable structure of an AppleDouble header file you must
+not use buffered I/O when reading or writing them; you should only use
+the read() and write() system calls.  It is also important that you
+make some effort to coordinate processes that are reading and writing
+the same header file, since a reader will receive the wrong data if
+the location of a given entry has changed since it read the descriptor
+for the entry.  If a process tries to read the descriptor table while
+it is changing then it is possible to read totally meaningless data.
+<p>
+When a header file is opened it is initially presented with a default
+header layout.  You may write to the header and change the layout, but
+when all file descriptors for the file or directory have been closed
+the change in format is lost and subsequent opens will yield the
+default layout.  Changes to supported entries are made directly to the
+filesystem and are thus preserved when the file is closed and
+reopened.
+<p>
+<tt>hfs_fs</tt> currently uses a fixed-size table to hold the
+descriptors.  Therefore you are limited to <tt>HFS_HDR_MAX</tt>
+(currently 10) descriptors.  In the unlikely event that you try to
+write a header with more descriptors, a warning will be issued by the
+kernel, and extra descriptors will be ignored.  This should be
+considered a bug in <tt>hfs_fs</tt> and will hopefully change sooner
+rather than later.
+<p>
+The results of specifying overlapping entries is undefined and should
+not be relied upon to remain unchanged from one version of
+<tt>hfs_fs</tt> to the next.  There is no valid reason to define
+overlapping entries, so just don't do it!
+<p>
+Changes to the magic number and version fields are preserved until all
+file descriptors are closed, however the only significance given to
+them internally is that the 16 bytes following the version changes
+meaning according to the version.  For version 1 header files these 16
+bytes contain the string ``Macintosh'' followed by 7 spaces.  For any
+other value of the version field these 16 bytes are all zeros.  In
+either case writes to these 16 bytes are silently ignored.
+<p>
+Since the magic number and version are given no other significance
+internally, you are free to do many things that violate the official
+formats.  For instance you can create an entry for the data fork in a
+header file with an AppleDouble magic number or create ``File Info''
+(id=7) entries in version 2 header files and ``File Dates Info''
+(id=8) entries in version 1 header files.  However, future versions of
+<tt>hfs_fs</tt> may enforce the format more strictly.
+<p>
+Entry ids 1, 2, 7, 8, 9 and 10 (``Data Fork'', ``Resource Fork'',
+``File Info'', ``File Dates Info'', ``Finder Info'' and ``Macintosh
+File Info'') are fully supported, meaning that their contents may be
+read and written and that data written is preserved when the file is
+closed and reopened.  The data and resource forks are, of course,
+supported only for files, not directories.
+<p>
+Entry id 7 specifies some of the same data given by ids 8 and 10.  If
+you create a header file with an entry for id 7 and for ids 8 or 10,
+then the behavior with respect to their interaction is undefined.  A
+header that contains an entry for id 7 and for ids 8 or 10 is not
+valid as either a version 1 or a version 2 header file, so there is
+no reason to do this and future versions of <tt>hfs_fs</tt> may
+prevent it.
+<p>
+Entry id 3 (``Real Name'') is read-only, since it will change
+automatically when a file is renamed.  Writes to the corresponding
+entry are silently ignored.
+<p>
+All other entry ids are ignored.  You may create descriptors for them;
+in fact the default header layout when <tt>fork=netatalk</tt> includes
+a descriptor for id 4 (``Comment'').  However writes to the entries
+corresponding to the ignored ids fail silently and reads from the
+entries always return zeros.  However, you shouldn't write random
+bytes to unsupported entries, since they may be supported in the
+future.
+<p>
+All of the supported entry types except the data and resource forks
+have a fixed length.  If you give them a smaller length in the
+descriptor then you are unable to access part of the corresponding
+entry.  If you give them a larger length in the descriptor, then the
+corresponding entry is padded with zeros and writes to the extra space
+are silently ignored.
+<p>
+Writes to the length field of descriptors for the data and resource
+forks will cause the corresponding fork to grow (with zero padding) or
+shrink to the indicated length.
+<p>
+If you have an entry for the data fork then the descriptor's length
+field does not change automatically to reflect any modification of the
+data fork directly (the data does change however).  If the data fork
+is longer than the descriptor indicates, then a portion of it is
+inaccessible.  If the data fork is shorter than the descriptor
+indicates then reads will be padded with zeros.
+<p>
+Writes beyond the end of the data or resource fork that extend into
+empty space between entries or beyond the end of the file will extend
+the corresponding fork, automatically changing the length field of the
+corresponding descriptor.  Writes to any other space between entries
+are silently ignored and read of such spaces always return zeros.
+<p>
+Calling truncate() on a header file can change the length of either
+fork and such a change will automatically be reflected in the length
+field of the corresponding descriptor.  If truncate() shortens the
+file so that the entry for either fork would extend beyond the new end
+of the file then the fork is shortened to fit in the space that
+remains, or to zero bytes if the corresponding entry is now entirely
+beyond the end of the file.  If the last entry in a header file is the
+data or resource fork then a call to truncate() that extends the
+header file will extend the fork with zeros.  Note that this happens
+even if there was previously space between the end of the fork and the
+end of the file.
+<!--  -->
+<sect>Frequently Asked Questions (and their answers)
+<p>
+This is a quick and dirty attempt at answering some of the more common
+questions I receive about <tt>hfs_fs</tt>.  Additions are welcome.
+<!--  -->
+<sect1>What is HFS?
+<p>
+HFS stands for ``Hierarchical File System'' and is the filesystem used
+by the Mac Plus and all later Macintosh models.  Earlier Macintosh
+models used MFS (``Macintosh File System''), which is not supported
+by <tt>hfs_fs</tt>.
+<!--  -->
+<sect1>Does hfs_fs allow me to mount AppleShare volumes?
+<p>
+No, <tt>hfs_fs</tt> is for mounting local volumes only.  I am not
+aware of any software that allows Linux to act as an AppleShare client.
+<!--  -->
+<sect1>Why can I mount some HFS CDROMs but not others?
+<p>
+In the past there was a known incompatibility with some ``hybrid'' CDROMs
+that appear as HFS disks on Macs and as ISO9660 disks on other systems.
+I think I have fixed the problem.  So, if you encounter this particular
+problem or have problems with specific non-hybrid CDROMs please e-mail
+me with the title and manufacturer of the CD.
+<!--  -->
+<sect1>What does ``only 1024-char blocks implemented (512)'' mean?
+<p>
+This message comes from the kernel and indicates that an attempt was made
+to read a 512-byte block from a device that doesn't support 512-byte blocks.
+<tt>hfs_fs</tt> only works with 512-byte blocks, and therefore doesn't
+function with these devices.  Eventually <tt>hfs_fs</tt> will be able to
+use 1024-byte blocks when necessary.
+<!--  -->
+<sect1>Why do I get a message about a bad or unknown partition table?
+<p>
+The Linux kernel doesn't yet know anything about Macintosh partition
+tables, so it gives this warning when it can't find a partition table it
+recognizes.  The decoding of Mac partition tables is done by
+<tt>hfs_fs</tt>, so you should still be able to mount the disk.
+<!--  -->
+<sect1>Can I mount multiple HFS partitions from the same Macintosh disk?
+<p>
+Not simultaneously.  Because the Linux kernel doesn't understand the
+Macintosh partition table, <tt>hfs_fs</tt> must access the raw device.
+Therefore, the kernel thinks the entire drive is in use and prevents
+additional mounts on it.  However, you can use the <tt>part=n</tt>
+mount option to select which HFS partition to mount.
+<!--  -->
+<sect1>In what ways can I write to HFS volumes?
+<p>
+<tt>hfs_fs</tt> is as capable as the MS-DOS or VFAT filesystems,
+except that certain things can only be done with a file's data fork.
+<p>
+You <bf>can</bf>:
+<itemize>
+       <item>
+       Create, delete and rename directories and data forks of
+       files with the caveat that names are case insensitive
+       (so <tt>foo</tt> and <tt>Foo</tt> are the same file or
+       directory).
+       <!-- -->
+       <item>
+       Run Linux executables or shared libraries on an HFS disk if
+       they are stored in the data fork of a file.
+       <!-- -->
+       <item>
+       Read, write and truncate both forks of files and the
+       Finder's metadata of files and directories.
+       <!-- -->
+       <item>
+       Mmap data forks of files (and the resource
+       fork if the filesystem is mounted with the <tt>fork=cap</tt>
+       option).
+       <!-- -->
+       <item>
+       Toggle the 'w' permission bits (as a group) of data forks.
+       <!-- -->
+       <item>
+       Change the <tt>i_mtime</tt> of files and directories.
+</itemize>
+<p>
+You <bf>cannot</bf>:
+<itemize>
+       <item>
+       Create, delete or rename resource forks of files or the
+       Finder's metadata.  Note, however, that they are created
+       (with defaults values), deleted and renamed along with the
+       corresponding data fork or directory.
+       <!-- -->
+       <item>
+       Run Linux executables or shared libraries on an HFS disk
+       if they are stored in the resource fork of a file.
+       <!-- -->
+       <item>
+       Mmap the Finder's metadata (when <tt>fork=cap</tt>) or AppleDouble
+       header files (when <tt>fork=double</tt> or <tt>fork=netatalk</tt>).
+       <!-- -->
+       <item>
+       Change permissions on directories.
+       <!-- -->
+       <item>
+       Change the uid or gid of files or directories.
+       <!-- -->
+       <item>
+       Set the set-uid, set-gid or sticky permission bits.
+       <!-- -->
+       <item>
+       Create multiple links to files.
+       <!-- -->
+       <item>
+       Create symlinks, device files, sockets or FIFOs.
+</itemize>
+<!-- -->
+<sect1>Does hfs_fs work with 400k or 800k Macintosh diskettes?
+<p>
+Yes and no.  The software is fully capable of dealing with HFS
+disks of any size.  However, the 400k and 800k diskettes are written
+in a physical format that is incompatible with most non-Macintosh
+floppy drives.  Note also that almost all 400k Macintosh diskettes
+are MFS, not HFS.
+<!-- -->
+<sect1>How can I format an HFS volume?
+<p>
+Robert Leslie (<tt><htmlurl url="mailto:[email protected]"
+name="[email protected]"></tt>) has written a package for working
+with HFS volumes (like <tt>mtools</tt> plus a graphical interface).
+One program in the package is <tt>hformat</tt> which can format
+HFS volumes.  The latest version can be found on
+<url url="http://www.mars.org/home/rob/proj/hfs/"
+name="the HFS Utilities home page">.
+<!--  -->
+<sect>Reporting Bugs
+<p>
+If you'd like any problems you encounter fixed, you'll need to provide
+a detailed bug report.  However, you should check the FAQ first to be
+certain that your problem is not a known limitation of <tt>hfs_fs</tt>.
+If your bug doesn't appear in the FAQ then you should e-mail me at
+<tt><htmlurl url="mailto:[email protected]"
+name="[email protected]"></tt>.
+<!--  -->
+<sect1>What Goes in a Bug Report
+<p>
+When writing your bug report, include any facts you think might be
+relevant; I'd much rather have a bunch of extra facts than need
+to e-mail you to get the information.  At a minimum the following
+information should be included:
+<itemize>
+       <item>
+       The version of <tt>hfs_fs</tt> you are using.
+       <item>
+       The kernel version you are using.
+       <item>
+       The version of <tt>gcc</tt> you used to compile the module, including
+       whether it is producing ELF or a.out output.
+       <item>
+       The version of the module utilities used to load <tt>hfs.o</tt>.
+       <item>
+       Any unofficial kernel patches or loadable modules you are using.
+       <item>
+       The type of media you are working with
+       (floppy, CDROM, ZIP Drive, etc.).
+       <item>
+       The steps required to reproduce the bug, including mount options
+       used.  (If you can't reproduce the bug tell me everything you did
+       the one time it did occur, but be warned that non-reproducible bugs
+       can only rarely be fixed.)
+</itemize>
+<p>
+There is a script, <tt>BUG_INFO</tt>, in the <tt>hfs_fs</tt> source directory
+that will attempt to determine the first four items for you.
+<!-- -->
+<sect1>How to Report a Kernel Oops or GPF
+<p>
+If you encounter a bug that causes a kernel Oops or a General Protection
+Fault then you'll need to collect some information for the bug report
+before you reboot.
+It is important that you do this before
+rebooting, since the module is unlikely to be loaded at the same address
+after the reboot.
+<p>
+You should include all the information that the kernel prints to the console
+or to the system logs.  However, the <tt>EIP</tt> and <tt>Stack Trace</tt>
+are addresses in <em>your</em> kernel and mean nothing to me without
+more information.  Using your <tt>System.map</tt> file (or either
+<tt>ksymoops</tt> or <tt>klogd</tt>) determine which functions the
+<tt>EIP</tt> and <tt>Stack Trace</tt> are in.  If you do this by hand using
+your <tt>System.map</tt> file then the correct symbol is the one
+of type <tt>t</tt> or <tt>T</tt> with the largest address less than or
+equal to the one you are resolving.
+<p>
+If the Oops or GPF was in the <tt>hfs_fs</tt> code then the <tt>EIP</tt> and
+the top levels of the <tt>Stack Trace</tt> will be in a loadable module, rather
+than in the kernel proper.  So, their symbols will not be in the file
+<tt>System.map</tt>.
+Therefore, you will need to use <tt>/proc/ksyms</tt>, or a
+loadmap produced by passing the <tt>-m</tt> option to <tt>insmod</tt>, to
+locate those symbols.  Keep in mind that neither of these files is sorted.
+<!--  -->
+<sect>Legal Notices
+<sect1>This Document
+<p>
+This document is Copyright &copy; 1996, 1997 by Paul H. Hargrove.
+<p>
+Permission is granted to make and distribute verbatim copies of this
+document provided the copyright notice and this permission notice are
+preserved on all copies.
+<p>
+Permission is granted to copy and distribute modified versions of this
+document under the conditions for verbatim copies above, provided a
+notice clearly stating that the document is a modified version is also
+included in the modified document.
+<p>
+Permission is granted to copy and distribute translations of this
+document into another language, under the conditions specified above
+for modified versions.
+<p>
+Permission is granted to convert this document into another media
+under the conditions specified above for modified versions provided
+the requirement to acknowledge the source document is fulfilled by
+inclusion of an obvious reference to the source document in the new
+media. Where there is any doubt as to what defines ``obvious'' the
+copyright owner reserves the right to decide.
+<!--  -->
+<sect1>The Software
+<p>
+The <tt>hfs_fs</tt> software is Copyright &copy; 1994-1997 by Paul H. Hargrove.
+<p>
+This software is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+<p>
+This software is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+See the GNU General Public License for more details.
+<p>
+You should have received a copy of the GNU General Public License
+along with this software in the file ``COPYING''; if not, write to the
+Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+<!-- XXX: add a URL for the GPL? -->
+<!--  -->
+<sect2>The Columbia AppleTalk Package for UNIX
+<p>
+The source code distribution of the Columbia AppleTalk Package for
+UNIX, version 6.0, (CAP) was used as a <em>specification</em> of the
+location and format of files used by CAP's Aufs.  No code from CAP
+appears in <tt>hfs_fs</tt>.  <tt>hfs_fs</tt> is not a work ``derived''
+from CAP in the sense of intellectual property law.
+<!--  -->
+<sect2>Netatalk
+<p>
+The source code distributions of Netatalk, versions 1.3.3b2 and 1.4b2,
+were used as a <em>specification</em> of the location and format of
+files used by Netatalk's afpd.  No code from Netatalk appears in
+<tt>hfs_fs</tt>.  <tt>hfs_fs</tt> is not a work ``derived'' from
+Netatalk in the sense of intellectual property law.
+<!-- -->
+<sect1>Trademarks
+<p>
+<itemize>
+       <item>
+       ``Finder'' is a trademarks of Apple Computer, Inc.
+       <!-- -->
+       <item>
+       ``Apple'', ``AppleShare'', ``AppleTalk'' and ``Macintosh'' are
+       registered trademarks of Apple Computer, Inc.
+       <!-- -->
+       <item>
+       ``Microsoft'' and ``MS-DOS'' are registered trademarks
+       of Microsoft Corporation.
+       <!-- -->
+       <item>
+       All other trademarks are the property of their respective owners.
+</itemize>
+</article>
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/fs/hfs/README.txt linux-2.0.29/fs/hfs/README.txt
--- linux.vanilla/fs/hfs/README.txt     Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/fs/hfs/README.txt      Wed Apr  9 04:41:47 1997
@@ -0,0 +1,1259 @@
+  Macintosh HFS Filesystem for Linux
+  Paul H. Hargrove, [email protected]
+  version 0.8.3, 08 Apr 1997
+
+  This document describes version 0.8.3 of hfs_fs, a Linux kernel load-
+  able module implementing the Macintosh HFS filesystem.  The most cur-
+  rent versions of this document and the software are kept at The HFS
+  for Linux Page <http://www-sccm.Stanford.EDU/~hargrove/HFS/>.
+  ______________________________________________________________________
+
+  Table of Contents:
+
+  1.      Introduction
+
+  2.      System Requirements
+
+  3.      Installation
+
+  3.1.    Compiling the loadable module
+
+  3.2.    Installing the module in the modules directory (optional)
+
+  3.3.    Loading the module into the running kernel
+
+  3.4.    Using the module with versioned symbols
+
+  4.      Mounting HFS Filesystems
+
+  4.1.    afpd
+
+  4.2.    case={asis, lower}
+
+  4.3.    conv={auto, binary, text}
+
+  4.4.    creator=cccc
+
+  4.5.    fork={cap, double, netatalk}
+
+  4.6.    gid=n
+
+  4.7.    names={7bit, 8bit, alpha, cap, latin, netatalk, trivial}
+
+  4.8.    part=n
+
+  4.9.    quiet
+
+  4.10.   type=cccc
+
+  4.11.   uid=n
+
+  4.12.   umask=n
+
+  5.      Writing to HFS Filesystems
+
+  5.1.    Writing with fork=cap
+
+  5.2.    Writing with fork=double
+
+  5.3.    Writing with fork=netatalk
+
+  6.      A Guide to Special File Formats
+
+  6.1.    CAP .finderinfo Files
+
+  6.2.    AppleDouble Header Files
+
+  7.      Frequently Asked Questions (and their answers)
+
+  7.1.    What is HFS?
+
+  7.2.    Does hfs_fs allow me to mount AppleShare volumes?
+
+  7.3.    Why can I mount some HFS CDROMs but not others?
+
+  7.4.    What does ``only 1024-char blocks implemented (512)'' mean?
+
+  7.5.    Why do I get a message about a bad or unknown partition table?
+
+  7.6.    Can I mount multiple HFS partitions from the same Macintosh
+  disk?
+
+  7.7.    In what ways can I write to HFS volumes?
+
+  7.8.    Does hfs_fs work with 400k or 800k Macintosh diskettes?
+
+  7.9.    How can I format an HFS volume?
+
+  8.      Reporting Bugs
+
+  8.1.    What Goes in a Bug Report
+
+  8.2.    How to Report a Kernel Oops or GPF
+
+  9.      Legal Notices
+
+  9.1.    This Document
+
+  9.2.    The Software
+
+  9.2.1.  The Columbia AppleTalk Package for UNIX
+
+  9.2.2.  Netatalk
+
+  9.3.    Trademarks
+  ______________________________________________________________________
+
+  11..  IInnttrroodduuccttiioonn
+
+  This package is a Macintosh HFS filesystem module for Linux.  It
+  allows you to read and write Macintosh HFS filesystems on floppy
+  disks, CDROMs, hard drives, ZIP drives, etc.  It is _n_o_t an AppleShare
+  client.
+
+  If you use this software, please send me a note telling of your
+  success or failure with it.  Your feedback lets me know that this
+  project is not a waste of my time.
+
+  This code is still experimental, so backup anything important before
+  you start playing.  I'd like you to know that I've never lost any
+  files while using this software, or I would not release it.  However,
+  a ``better safe than sorry'' attitude is probably best.
+
+  If, for instance, the buffer cache were to become corrupted you could
+  start losing things on other disks.  Because of this, if you get a
+  General Protection Fault, or a kernel Oops, I _s_t_r_o_n_g_l_y recommend that
+  you reboot before writing any files.
+
+  22..  SSyysstteemm RReeqquuiirreemmeennttss
+
+  You will need the following to compile and use this release of hfs_fs:
+
+  +o  Kernel version 2.0.1 or newer compiled with modules enabled
+     (CONFIG_MODULES).
+
+  +o  The kernel sources (or at least the header files) available online.
+
+  +o  The module utilities package current for your kernel version and an
+     understanding of how to use it.  (The file
+     Documentation/modules.txt in the kernel source directory provides a
+     brief introduction.)
+
+  In theory the code is endian-independent, but is tested most
+  extensively on Intel platforms.  It is also known to compile on m68k,
+  PPC and Alpha machines, but is not as extensively tested on any of
+  these platforms.
+
+  33..  IInnssttaallllaattiioonn
+
+  The hfs_fs code is not yet part of the official kernel distribution.
+  Therefore, it is compiled as a module and then loaded into the kernel
+  using the module utilities.  Therefore, your kernel must be compiled
+  with CONFIG_MODULES enabled.
+
+  33..11..  CCoommppiilliinngg tthhee llooaaddaabbllee mmoodduullee
+
+  To compile hfs.o you should only need to execute make in the hfs_fs
+  source directory.
+
+  If gcc complains about not finding a large number of header files with
+  names beginning with ``linux/'' then you probably don't have the
+  kernel header files installed correctly.  Either /usr/include/linux,
+  /usr/include/asm and /usr/include/scsi should be symbolic links to
+  include/linux, include/asm and include/scsi in the kernel source tree
+  for the kernel you wish to use hfs_fs with, or else they should be
+  directories containing the header files for the kernel you wish to use
+  hfs_fs with.
+
+  If gcc complains about not finding linux/version.h, then you will need
+  to run make dep in the kernel source directory to build it.
+
+  If gcc complains about not finding the files linux/config.h or
+  linux/autoconf.h, then you will need to run make config and make dep
+  in the kernel source directory to build these two files and
+  linux/version.h.
+
+  If you are compiling on a DEC Alpha and receive messages saying
+  assignment from incompatible pointer type when compiling files dir_*.c
+  and file_*.c, then you need to change a single line in the file
+  hfs_fs.h.  Remove the text ``&& !defined(__alpha__)'' from the end of
+  line 217.
+
+  33..22..  IInnssttaalllliinngg tthhee mmoodduullee iinn tthhee mmoodduulleess ddiirreeccttoorryy ((ooppttiioonnaall))
+
+  If you plan to use kerneld to automatically load the module or if you
+  wish to use modprobe or insmod without supplying a complete path to
+  hfs.o, then you will need to copy hfs.o into a directory where the
+  module utilities expect to find it.
+
+  The proper directory may depend slightly on your configuration.
+  However, /lib/modules/default/fs/ is a common one for filesystem
+  modules.  Once hfs.o is in the proper directory you should run depmod
+  -a to update the dependency list used by kerneld and modprobe.
+
+  33..33..  LLooaaddiinngg tthhee mmoodduullee iinnttoo tthhee rruunnnniinngg kkeerrnneell
+
+  There are three ways to accomplish this:
+
+  1. If you are running kerneld and have installed hfs.o in the modules
+     directory then you don't need to issue any commands; the module
+     will be loaded when you attempt to mount an HFS filesystem.
+
+  2. If you are _n_o_t running kerneld then you can load hfs.o manually by
+     running modprobe hfs.o.  If you have not installed hfs.o in one of
+     the standard module directories, then you will need provide a full
+     path to the file hfs.o.
+
+  3. If you have been experiencing kernel crashes with hfs_fs, then you
+     should file a bug report including the names of the functions which
+     the EIP and Stack Trace point into.  To help with this you can ask
+     for relocation map for the module when you load it.  To do this
+     load the module with insmod -m hfs.o >loadmap.  Again, you may need
+     a full path to the file hfs.o if you have not placed it in one of
+     the standard module directories.
+
+  33..44..  UUssiinngg tthhee mmoodduullee wwiitthh vveerrssiioonneedd ssyymmbboollss
+
+  All the interface between the module and the kernel take place through
+  very stable (since the mid-1.3.x kernels) parts of the kernel.  If you
+  enabled versioned symbols (CONFIG_MODVERSIONS) when you compiled your
+  kernel you should often be able to compile this module once and then
+  use it with many kernels newer than the one you compiled it for.
+
+  In any case, it is unlikely that this module will need changes with
+  each new kernel patch; simple recompilation should usually suffice.
+
+  44..  MMoouunnttiinngg HHFFSS FFiilleessyysstteemmss
+
+  Once you have installed the module, you will be able to use hfs as a
+  filesystem type option to mount.  For instance, to mount a Macintosh
+  floppy disk on the directory /mnt using the default mount options you
+  would execute mount -t hfs /dev/fd0 /mnt.
+
+  The remainder of this section describes the several mount options
+  available to control how the HFS filesystem is mapped onto a Linux
+  structure.  The values for the multiple-choice options (case, conv,
+  fork and names) can be abbreviated by their first character.
+
+  44..11..  aaffppdd
+
+  If included in the options, then the behavior of hfs_fs is changed to
+  make it fully read-write compatible with Netatalk's afpd.  In this
+  mode you should not use normal user-level tools to modify the
+  filesystem, though reading from it is acceptable.  This is because the
+  return codes from some system calls are changed to fool afpd.  These
+  changes will confuse many user-level tools.  In particular rm -r will
+  loop forever.
+
+  This option implies fork=netatalk, which in turn implies
+  names=netatalk.  If either of these options are explicitly set to
+  something else they will take precedence and will confuse afpd.  The
+  quiet option has no effect.  The case= option functions normally, but
+  afpd usually does the same thing for you.  The conv= and part= options
+  also function normally.
+
+  You will probably want to use the uid=, gid= and umask= mount options.
+  Note that because all the files on an HFS filesystem belong to a
+  single user and group and have a single umask, the full AppleShare
+  permission scheme will not work.
+
+  One additional limitation is that the Desktop database on the disk is
+  stored in afpd's format and is separate from any existing database
+  maintained by the Finder when the volume is used on a Macintosh.
+  This mode is known to be compatible with afpd from Netatalk versions
+  1.4b1 and 1.4b2, and known to be incompatible with the afpd from
+  version 1.3.3.  As of this writing Netatalk version 1.4 has not yet
+  been released.  However, it is likely that hfs_fs's afpd mode will be
+  compatible with afpd from Netatalk version 1.4 when it is released.
+
+  44..22..  ccaassee=={{aassiiss,, lloowweerr}}
+
+  default value: asis
+
+  This option determines if Mac filenames are presented in their
+  original case or in all lowercase.  Filename matching is not affected,
+  so either way foo and Foo refer to the same file but ls will list Foo
+  with case=asis, and foo with case=lower.  (Same as for the HPFS
+  filesystem.)
+
+     aassiiss
+        Filenames are reported in the case they were created with.
+
+     lloowweerr
+        Filenames are reported in lowercase.
+
+  44..33..  ccoonnvv=={{aauuttoo,, bbiinnaarryy,, tteexxtt}}
+
+  default value: binary
+
+  This option controls CR<->NL conversion of Macintosh _d_a_t_a _f_o_r_k_s.  Any
+  translation takes place only for files accessed with the read() and
+  write() system calls (either directly or through the stdio functions).
+  Access through mmap() is unaffected.  (Similar to the conv= option for
+  the MS-DOS filesystem.)
+
+     aauuttoo
+        If the Finder's type for a file is TEXT or ttro, then CR
+        characters are converted to NL characters when read, and NL
+        characters are converted to CR characters when written.
+
+        Please e-mail me if you know of any other types that indicate
+        pure text files.
+
+     bbiinnaarryy
+        No CR<->NL conversion is done.
+
+     tteexxtt
+        In all data forks, regardless of the Finder's type for the file,
+        CR characters are converted to NL characters when read, and NL
+        characters are converted to CR characters when written.
+
+  44..44..  ccrreeaattoorr==cccccccc
+
+  default value: ``????''
+
+  Specifies the 4-character string specifying the Finder's Creator for
+  new files.
+
+  44..55..  ffoorrkk=={{ccaapp,, ddoouubbllee,, nneettaattaallkk}}
+
+  default value: cap
+
+  This option determines how resource forks and the Finder's metadata
+  are represented within the structure of the Linux filesystem.
+
+     ccaapp
+        The scheme used by the Columbia AppleTalk Package's AUFS.
+        Associated with each directory are two special directories and a
+        metadata file.  The directory ./bar is represented by:
+
+        ..//bbaarr
+           The directory itself, containing subdirectories, the data
+           forks of files, and the following two special directories.
+
+        ..//bbaarr//..rreessoouurrccee
+           A special directory holding resource forks of the files in
+           ./bar.
+
+        ..//bbaarr//..ffiinnddeerriinnffoo
+           A special directory holding metadata files for the files and
+           subdirectories in ./bar.
+
+        ..//..ffiinnddeerriinnffoo//bbaarr
+           The metadata file for the directory ./bar.
+
+        The files in a directory are represented as three files:
+
+        ..//ffoooo
+           The data fork of the file ./foo.
+
+        ..//..rreessoouurrccee//ffoooo
+           The resource fork of the file ./foo.
+
+        ..//..ffiinnddeerriinnffoo//ffoooo
+           The metadata file for the file ./foo.
+
+        Additionally, the file .rootinfo in the root directory of the
+        HFS filesystem is a metadata file for the root directory.
+
+        Documentation on the format of file containing the Finder's
+        metadata is included in the Columbia AppleTalk Package.
+
+     ddoouubbllee
+        The ``AppleDouble'' format recommended by Apple.  (Apple's other
+        recommended format, ``AppleSingle'', is not yet implemented.)
+
+        Associated with each directory is an AppleDouble ``header
+        file''.  The directory ./bar is represented by:
+
+        ..//bbaarr
+           The directory itself, containing subdirectories, the data
+           forks for files, and the header files for files and
+           subdirectories.
+
+        ..//%%bbaarr
+           The header file for the directory ./bar, containing the
+           Finder's metadata for the directory.
+
+        The files in a directory are represented as two files:
+
+        ..//ffoooo
+           The data fork of the file ./foo.
+
+        ..//%%ffoooo
+           The header file for the file ./foo, containing the resource
+           fork and the Finder's metadata for the file.
+
+        Additionally, the file %RootInfo in the root directory of the
+        HFS filesystem is a header file for the root directory.  This is
+        not quite the %RootInfo file referred to in the AppleDouble
+        specification.
+
+        The header files used in this scheme are version 2 AppleDouble
+        header files.  Their format is documented in
+        ``AppleSingle/AppleDouble Formats: Developer's Note (9/94)'',
+        available from from Apple's Developer Services Page
+        <http://devworld.apple.com>.
+
+        Note that the naming convention for the header file can cause
+        name conflicts.  For instance, using Apple's 7-bit ASCII name
+        conversion the name %Desktop could be interpreted either as the
+        header file for the file Desktop or as the file with 0xDE as the
+        hexadecimal representation of its first character, and "sktop"
+        as the remaining 5 characters.  The problem arises when both
+        files exist, since only one will be accessible.  The behavior of
+        the HFS module in the case of such a conflict is undefined, and
+        may change in future releases.  (If this causes problems for
+        you, please don't report it as a bug; I didn't design this
+        ``standard'', Apple did.)
+
+     nneettaattaallkk
+        The scheme used by the Netatalk afpd.
+
+        Associated with each directory is a special directory and a
+        metadata file.  The directory ./bar is represented by:
+
+        ..//bbaarr
+           The directory itself, containing subdirectories, the data
+           forks of files, and the following special directory.
+
+        ..//bbaarr//..AApppplleeDDoouubbllee
+           A special directory holding AppleDouble header files for
+           ./bar and the files it contains, but not for the
+           subdirectories it contains.
+
+        ..//bbaarr//..AApppplleeDDoouubbllee//..PPaarreenntt
+           The header file for the directory ./bar, containing the
+           Finder's metadata for the directory.
+
+        The files in a directory are represented as two files:
+
+        ..//ffoooo
+           The data fork of the file ./foo.
+
+        ..//..AApppplleeDDoouubbllee//ffoooo
+           The header file for file ./foo, containing the resource fork
+           and the Finder's metadata.
+
+        The header files used in this scheme are version 1 AppleDouble
+        header files.  Their format is documented in the ``Apple II File
+        Type Notes'' under the type ``$E0.0002/$E0.0003-AppleDouble'',
+        and in Appendix B of the ``A/UX Toolbox: Macintosh ROM
+        Interface'' manual.
+
+  44..66..  ggiidd==nn
+
+  default value: gid of the mounting process
+
+  Specifies the group that owns all files and directories on the
+  filesystem.  (Same as for the MS-DOS and HPFS filesystems.)
+
+  44..77..  nnaammeess=={{77bbiitt,, 88bbiitt,, aallpphhaa,, ccaapp,, llaattiinn,, nneettaattaallkk,, ttrriivviiaall}}
+
+  default value: varies as follows
+
+  +o  If the fork option is set to double, then names defaults to alpha.
+
+  +o  If the fork option is set to netatalk, then names defaults to
+     netatalk.
+
+  +o  If the fork option is set to cap (or has taken that value by
+     default), then names defaults to cap.
+
+  This option determines how to convert between valid Macintosh
+  filenames and valid Linux filenames.  The 7bit, 8bit and alpha options
+  correspond to Apple's recommended conventions named ``7-bit ASCII'',
+  ``8-bit'' and ``7-bit alphanumeric''.
+
+     77bbiitt
+        When converting from Macintosh filenames to Linux filenames the
+        NULL (0x00), slash (/) and percent (%) characters and the
+        extended 8-bit characters (hexadecimal codes 0x80-0xff) are
+        replaced by a percent character (%) followed by the two-digit
+        hexadecimal code for the character.
+
+        When converting from Linux filenames to Macintosh filenames the
+        string "%YZ" is replaced by the character with hexadecimal code
+        0xYZ.  If 0xYZ is not a valid hexadecimal number or is the code
+        for NULL or colon (:) then the string "%YZ" is unchanged.  A
+        colon (:) is replaced by a pipe character (|).
+
+     88bbiitt
+        When converting from Macintosh filenames to Linux filenames the
+        NULL (0x00), slash (/) and percent (%) characters are replaced
+        by a percent character (%) followed by the two-digit hexadecimal
+        code for the character.
+
+        When converting from Linux filenames to Macintosh filenames the
+        string "%YZ" is replaced by the character with hexadecimal code
+        0xYZ.  If 0xYZ is not a valid hexadecimal number or is the code
+        for NULL or colon (:) then the string "%YZ" is unchanged.  A
+        colon (:) is replaced by a pipe character (|).
+
+     aallpphhaa
+        When converting from Macintosh filenames to Linux filenames only
+        the alphanumeric characters (a-z, A-Z and 0-9), the underscore
+        (_) and the last period (.) in the filename are unchanged.  The
+        remaining characters are replaced by a percent character (%)
+        followed by the two-digit hexadecimal code for the character.
+
+        When converting from Linux filenames to Macintosh filenames the
+        string "%YZ" is replaced by the character with hexadecimal code
+        0xYZ.  If 0xYZ is not a valid hexadecimal number or is the code
+        for NULL or colon (:) then the string "%YZ" is unchanged.  A
+        colon (:) is replaced by a pipe character (|).
+
+     ccaapp
+        The convention used by the Columbia AppleTalk Package's AUFS.
+
+        When converting from Macintosh filenames to Linux filenames the
+        characters from space ( ) through tilde (~) (ASCII 32-126) are
+        unchanged, with the exception of slash (/).  The slash (/) and
+        all characters outside the range 32-126 are replaced by a colon
+        (:) followed by the two-digit hexadecimal code for the
+        character.
+
+        When converting from Linux filenames to Macintosh filenames the
+        string ":YZ" is replaced by the character with hexadecimal code
+        0xYZ.  If 0xYZ is not a valid hexadecimal number or is the code
+        for NULL or colon (:) then the colon is replaced by a pipe
+        character (|).
+
+     llaattiinn
+        When converting from Macintosh filenames to Linux filenames the
+        characters from space ( ) through tilde (~) (ASCII 32-126) are
+        unchanged, with the exception of slash (/) and percent (%).  The
+        extended 8-bit Macintosh characters with equivalents in the
+        Latin-1 character set are replaced by those equivalents.  The
+        remaining characters are replaced by a percent character (%)
+        followed by the two-digit hexadecimal code for the character.
+
+        When converting from Linux filenames to Macintosh filenames the
+        string "%YZ" is replaced by the character with hexadecimal code
+        0xYZ.  If 0xYZ is not a valid hexadecimal number or is the code
+        for NULL or colon (:) then the string "%YZ" is unchanged. The
+        Latin-1 characters with equivalents in the extended 8-bit
+        Macintosh character set are replaced by those equivalents.  A
+        colon (:) is replaced by a pipe character (|).
+
+        Thanks to Holger Schemel ([email protected]) for
+        contributing this conversion mode.
+
+     nneettaattaallkk
+        The convention used by the Netatalk afpd.
+
+        When converting from Macintosh filenames to Linux filenames the
+        characters from space ( ) through tilde (~) (ASCII 32-126) are
+        unchanged, with the exception of slash (/) and any initial
+        period (.).  The slash (/)  and any initial period (.) and all
+        characters outside the range 32-126 are replaced by a colon (:)
+        followed by the two-digit hexadecimal code for the character.
+
+        When converting from Linux filenames to Macintosh filenames the
+        string ":YZ" is replaced by the character with hexadecimal code
+        0xYZ.  If 0xYZ is not a valid hexadecimal number or is the code
+        for NULL or colon (:) then the colon is replaced by a pipe
+        character (|).
+
+     ttrriivviiaall
+        When converting from Macintosh filenames to Linux filenames a
+        slash character (/) is replaced by a colon (:).
+
+        When converting from Linux filenames to Macintosh filenames a
+        colon (:) is replaced by a slash character (/).
+
+  44..88..  ppaarrtt==nn
+
+  default value: 0
+
+  Specifies which HFS partition to mount from a Macintosh CDROM or hard
+  drive.  Partitions are numbered from 0 and count only those identified
+  in the partition table as containing HFS filesystems.
+
+  Note that in versions before 0.8.3 partitions were numbered from 1.
+
+  44..99..  qquuiieett
+
+  If included in the options, then chown and chmod operations will not
+  return errors, but will instead fail silently.  (Same as for the MS-
+  DOS and HPFS filesystems.)
+
+  44..1100..  ttyyppee==cccccccc
+
+  default value: ``????''
+
+  Specifies the 4-character string specifying the Finder's Type for new
+  files.
+  44..1111..  uuiidd==nn
+
+  default value: uid of the mounting process
+
+  Specifies the user that owns all files and directories on the
+  filesystem.  (Same as for the MS-DOS and HPFS filesystems.)
+
+  44..1122..  uummaasskk==nn
+
+  default value: umask of the mounting process
+
+  Specifies (in octal) the umask used for all files and directories.
+  (Same as for the MS-DOS and HPFS filesystems.)
+
+  55..  WWrriittiinngg ttoo HHFFSS FFiilleessyysstteemmss
+
+  Each of the values of the fork mount option yields a different
+  representation of the Macintosh-specific parts of a file within the
+  structure of the Linux filesystem.  There are, therefore, slightly
+  different steps involved in copying files if you want to preserve the
+  resource forks and the Finder's metadata.
+
+  Regardless of the value of the fork mount option you can do virtually
+  everything to the data fork of a file that you can to a file on any
+  other filesystem.  The limitations are essentially the same as those
+  imposed by the MS-DOS filesystem:
+
+  +o  You can't change the uid or gid of files.
+
+  +o  You can't set the set-uid, set-gid or sticky permission bits.
+
+  +o  You can't clear the execute permission bits.
+
+  Likewise you can do virtually everything to a directory that you can
+  to a directory on another file system with the following exceptions:
+
+  +o  You can't create, delete or rename resource forks of files or the
+     Finder's metadata.  Note, however, that they are created (with
+     defaults values), deleted and renamed along with the corresponding
+     data fork or directory.
+
+  +o  You can't change permissions on directories.
+
+  +o  You can't change the uid or gid of directories.
+
+  +o  You can't create multiple links to files.
+
+  +o  You can't create symlinks, device files, sockets or FIFOs.
+
+  55..11..  WWrriittiinngg wwiitthh ffoorrkk==ccaapp
+
+  Unlike the other schemes for representing forked files, the CAP scheme
+  presents the resource fork as an independent file; the resource fork
+  of ./foo is ./.resource/foo.  Therefore, you can treat it as a normal
+  file.  You can do anything to a resource fork that you can do to a
+  data fork, except that you cannot enable execute permissions on a
+  resource fork.  Therefore, resource forks are not suitable for holding
+  Linux executables or shared libraries.
+
+  If you plan to use the resource fork on a Macintosh then you must obey
+  the format of a valid resource fork.  This format is documented in
+  Chapter 1 of Apple's _I_n_s_i_d_e _M_a_c_i_n_t_o_s_h_: _M_o_r_e _M_a_c_i_n_t_o_s_h _T_o_o_l_b_o_x.  hfs_fs
+  knows nothing about this format and so can do nothing to enforce it.
+
+  The current support for reading and writing is sufficient to allow
+  copying of entire directories with tar, as long as both the source and
+  destination are mounted with fork=cap.  tar may complain about being
+  unable to change the uid, gid or mode of files.  This is normal and is
+  an unavoidable side effect of the having a single uid, gid and umask
+  for the entire filesystem.
+
+  It is impossible to create a resource fork or a Finder metadata file.
+  However, they are created automatically when the data fork is created.
+  Therefore, if you wish to copy a single file including both forks and
+  the Finder's metadata then you must create the data fork first.  Then
+  you can copy the resource fork and the Finder's metadata.  For
+  instance to copy the file foo to dir/bar you should do the following:
+
+  1. cp foo dir/bar
+
+  2. cp .resource/foo dir/.resource/bar
+
+  3. cp .finderinfo/foo dir/.finderinfo/bar
+
+     You may get ``Operation not permitted'' errors from cp when it
+     tries to change the permissions on files.  These errors can safely
+     be ignored.  This method will work even if the file dir/bar exists.
+
+  If you wish to move foo to dir/bar and foo and dir are on the same
+  filesystem then you only need to execute mv foo dir/bar and the
+  resource fork and the Finder's metadata will move too.  However, if
+  foo and dir are on different filesystem then this will lose the
+  resource fork and metadata.  Therefore, it is safest to always move
+  files as follows:
+
+  1. cp foo dir/bar
+
+  2. cp .resource/foo dir/.resource/bar
+
+  3. cp .finderinfo/foo dir/.finderinfo/bar
+
+  4. rm foo
+
+     You may get ``Operation not permitted'' errors from cp when it
+     tries to change the permissions on files.  These errors can safely
+     be ignored.  This method will work even if the file dir/bar exists.
+
+  Directories have no resource fork but you may wish to create a
+  directory which has the same location and view on the Finder's screen
+  as an existing one.  This can be done by copying the Finder metadata
+  file.  To give the directory bar the same location, layout, creation
+  date and modify date as foo you simply execute cp .finderinfo/foo
+  .finderinfo/bar.
+
+  When copying an entire directory with cp -R you may also wish to copy
+  the metadata for the directory:
+
+  1. cp -R foo bar
+
+  2. cp .finderinfo/foo .finderinfo/bar
+
+     You may get ``Operation not permitted'' errors from cp when it
+     tries to change the permissions on files.  These errors can safely
+     be ignored.
+
+  55..22..  WWrriittiinngg wwiitthh ffoorrkk==ddoouubbllee
+
+  The current support for reading and writing header files is sufficient
+  to allow copying of entire directories with tar, as long as both the
+  source and destination are mounted with fork=double.  tar may complain
+  about being unable to change the uid, gid or mode of files.  This is
+  normal and is an unavoidable side effect of the having a single uid,
+  gid and umask for the entire filesystem.
+
+  It is impossible to create a header file.  However, they are created
+  automatically when the data fork is created.  Therefore, if you wish
+  to copy a single file including both forks and the Finder's metadata
+  then you must create the data fork first.  Then you can copy the
+  header file.  instance to copy the file foo to dir/bar you should do
+  the following:
+
+  1. cp foo dir/bar
+
+  2. cp %foo dir/%bar
+     You may get ``Operation not permitted'' errors from cp when it
+     tries to change the permissions on files.  These errors can safely
+     be ignored.  This method will work even if the file dir/bar exists.
+
+  If you wish to move foo to dir/bar and foo and dir are on the same
+  filesystem then you only need to execute mv foo dir/bar and the header
+  file will move too.  However, if foo and dir are on different
+  filesystem then this will lose the header file.  Therefore, it is
+  safest to always move files as follows:
+
+  1. cp foo dir/bar
+
+  2. cp %foo dir/%bar
+
+  3. rm foo
+
+     You may get ``Operation not permitted'' errors from cp when it
+     tries to change the permissions on files.  These errors can safely
+     be ignored.  This method will work even if the file dir/bar exists.
+
+  Directories have no resource fork but you may wish to create a
+  directory which has the same location and view on the Finder's screen
+  as an existing one.  This can be done by copying the corresponding
+  header file.  To give the directory bar the same location, layout,
+  creation date and modify date as foo simply execute cp %foo %bar.
+
+  When copying an entire directory with cp -R you may also wish to copy
+  the header file for the directory as well:
+
+  1. cp -R foo bar
+
+  2. cp %foo %bar
+
+     You may get ``Operation not permitted'' errors from cp when it
+     tries to change the permissions on files.  These errors can safely
+     be ignored.
+
+  55..33..  WWrriittiinngg wwiitthh ffoorrkk==nneettaattaallkk
+
+  The current support for reading and writing header files is sufficient
+  to allow copying of entire directories with tar, as long as both the
+  source and destination are mounted fork=netatalk.  tar may complain
+  about being unable to change the uid, gid or mode of files.  This is
+  normal and is an unavoidable side effect of the having a single uid,
+  gid and umask for the entire filesystem.
+
+  It is impossible to create a header file.  However, they are created
+  automatically when the data fork is created.  Therefore, if you wish
+  to copy a single file including both forks and the Finder's metadata
+  then you must create the data fork first.  Then you can copy the
+  header file.  instance to copy the file foo to dir/bar you should do
+  the following:
+  1. cp foo dir/bar
+
+  2. cp .AppleDouble/foo dir/.AppleDouble/bar
+
+     You may get ``Operation not permitted'' errors from cp when it
+     tries to change the permissions on files.  These errors can safely
+     be ignored.  This method will work even if the file dir/bar exists.
+
+  If you wish to move foo to dir/bar and foo and dir are on the same
+  filesystem then you only need to execute mv foo dir/bar and the header
+  file will move too.  However, if foo and dir are on different
+  filesystem then this will lose the header file.  Therefore, it is
+  safest to always move files as follows:
+
+  1. cp foo dir/bar
+
+  2. cp .AppleDouble/foo dir/.AppleDouble/bar
+
+  3. rm foo
+
+     You may get ``Operation not permitted'' errors from cp when it
+     tries to change the permissions on files.  These errors can safely
+     be ignored.  This method will work even if the file dir/bar exists.
+
+  Directories have no resource fork but you may wish to create a
+  directory which has the same location and view on the Finder's screen
+  as an existing one.  This can be done by copying the corresponding
+  header file.  To give the directory bar the same location, layout,
+  creation date and modify date as foo you simply execute cp
+  foo/.AppleDouble/.Parent bar/.AppleDouble/.Parent.
+
+  Because the fork=netatalk scheme holds the header file for a directory
+  within that directory, directories can safely be copied with cp -R foo
+  bar with no loss of information.  However, you may get ``Operation not
+  permitted'' errors from cp when it tries to change the permissions on
+  files.  These errors can safely be ignored.
+
+  66..  AA GGuuiiddee ttoo SSppeecciiaall FFiillee FFoorrmmaattss
+
+  Each of the values of the fork mount option yields different special
+  files to represent the Macintosh-specific parts of a file within the
+  structure of the Linux filesystem.  You can write to these special
+  files to change things such as the Creator and Type of a file.
+  However, to do so safely you must follow certain rules to avoid
+  corrupting the data.  Additionally, there are certain fields in the
+  special files that you can't change (writes to them will fail
+  silently).
+
+  66..11..  CCAAPP ..ffiinnddeerriinnffoo FFiilleess
+
+  The Finder's metadata for the file ./foo in held in the file
+  ./.finderinfo/foo.  The file has a fixed format defined in hfs_fs.h as
+  follows:
+
+  ______________________________________________________________________
+  struct hfs_cap_info {
+          __u8    fi_fndr[32];            /* Finder's info */
+          __u16   fi_attr;                /* AFP attributes */
+          __u8    fi_magic1;              /* Magic number: */
+  #define HFS_CAP_MAGIC1          0xFF
+          __u8    fi_version;             /* Version of this structure: */
+  #define HFS_CAP_VERSION         0x10
+          __u8    fi_magic;               /* Another magic number: */
+  #define HFS_CAP_MAGIC           0xDA
+          __u8    fi_bitmap;              /* Bitmap of which names are valid: */
+  #define HFS_CAP_SHORTNAME       0x01
+  #define HFS_CAP_LONGNAME        0x02
+          __u8    fi_shortfilename[12+1]; /* "short name" (unused) */
+          __u8    fi_macfilename[32+1];   /* Original (Macintosh) name */
+          __u8    fi_comln;               /* Length of comment (always 0) */
+          __u8    fi_comnt[200];          /* Finder comment (unused) */
+          /* optional:    used by aufs only if compiled with USE_MAC_DATES */
+          __u8    fi_datemagic;           /* Magic number for dates extension: */
+  #define HFS_CAP_DMAGIC          0xDA
+          __u8    fi_datevalid;           /* Bitmap of which dates are valid: */
+  #define HFS_CAP_MDATE           0x01
+  #define HFS_CAP_CDATE           0x02
+          __u8    fi_ctime[4];            /* Creation date (in AFP format) */
+          __u8    fi_mtime[4];            /* Modify date (in AFP format) */
+          __u8    fi_utime[4];            /* Un*x time of last mtime change */
+  };
+  ______________________________________________________________________
+
+  The type __u8 is an unsigned character, and __u16 is an unsigned
+  16-bit integer.
+
+  Currently only the fields fi_fndr, fi_attr, fi_ctime and fi_mtime can
+  be changed.  Writes to the other fields are silently ignored.
+  However, you shouldn't write random bytes to the other fields, since
+  they may be writable in the future.
+
+  The fi_fndr field is the ``Finder info'' and ``Extended Finder info''
+  for a file or directory.  These structures are described in various
+  books on Macintosh programming.  The portion of the most interest is
+  probably the first 8 bytes which, for a file, give the 4-byte Type
+  followed by the 4-byte Creator.
+
+  The fi_attr field is the AFP attributes of the file or directory.
+  While you can write any value to this field, only the ``write-
+  inhibit'' bit is significant.  Setting or clearing this bit will clear
+  or set the write bits in the file's permissions.  When you read from
+  this field anything you may have written is lost.  If the file has
+  write permissions enabled then you will read zero from this field.
+  With write permission disabled you will read back 0x01 0xA0, which
+  corresponds to setting the ``write-inhibit'', ``rename-inhibit'' and
+  ``delete-inhibit'' bits.
+
+  The fi_ctime and fi_mtime are the Macintosh created and modified time
+  for the file or directory, and are 32-bit signed integers in network
+  byteorder giving seconds from 00:00 GMT Jan. 1, 2000.
+
+  66..22..  AApppplleeDDoouubbllee HHeeaaddeerr FFiilleess
+
+  Both the fork=double and fork=netatalk schemes for representing forked
+  files use AppleDouble header files to contain the resource fork and
+  the Finder's metadata together in a single file.
+
+  The AppleDouble format specifies a fixed-format header which describes
+  which fields are contained in the remainder of the file, where they
+  are located in the file and how long they are.  A full description of
+  the version 1 format used when fork=netatalk is available from ??????.
+  The version 2 format used when fork=double is documented in ??????.
+  The discussion that follows assumes you have read and understood these
+  documents, which may be difficult until I've replaced the ``??????''s
+  above with something more informative :-).
+
+  Due to the variable structure of an AppleDouble header file you must
+  not use buffered I/O when reading or writing them; you should only use
+  the read() and write() system calls.  It is also important that you
+  make some effort to coordinate processes that are reading and writing
+  the same header file, since a reader will receive the wrong data if
+  the location of a given entry has changed since it read the descriptor
+  for the entry.  If a process tries to read the descriptor table while
+  it is changing then it is possible to read totally meaningless data.
+
+  When a header file is opened it is initially presented with a default
+  header layout.  You may write to the header and change the layout, but
+  when all file descriptors for the file or directory have been closed
+  the change in format is lost and subsequent opens will yield the
+  default layout.  Changes to supported entries are made directly to the
+  filesystem and are thus preserved when the file is closed and
+  reopened.
+
+  hfs_fs currently uses a fixed-size table to hold the descriptors.
+  Therefore you are limited to HFS_HDR_MAX (currently 10) descriptors.
+  In the unlikely event that you try to write a header with more
+  descriptors, a warning will be issued by the kernel, and extra
+  descriptors will be ignored.  This should be considered a bug in
+  hfs_fs and will hopefully change sooner rather than later.
+
+  The results of specifying overlapping entries is undefined and should
+  not be relied upon to remain unchanged from one version of hfs_fs to
+  the next.  There is no valid reason to define overlapping entries, so
+  just don't do it!
+
+  Changes to the magic number and version fields are preserved until all
+  file descriptors are closed, however the only significance given to
+  them internally is that the 16 bytes following the version changes
+  meaning according to the version.  For version 1 header files these 16
+  bytes contain the string ``Macintosh'' followed by 7 spaces.  For any
+  other value of the version field these 16 bytes are all zeros.  In
+  either case writes to these 16 bytes are silently ignored.
+
+  Since the magic number and version are given no other significance
+  internally, you are free to do many things that violate the official
+  formats.  For instance you can create an entry for the data fork in a
+  header file with an AppleDouble magic number or create ``File Info''
+  (id=7) entries in version 2 header files and ``File Dates Info''
+  (id=8) entries in version 1 header files.  However, future versions of
+  hfs_fs may enforce the format more strictly.
+
+  Entry ids 1, 2, 7, 8, 9 and 10 (``Data Fork'', ``Resource Fork'',
+  ``File Info'', ``File Dates Info'', ``Finder Info'' and ``Macintosh
+  File Info'') are fully supported, meaning that their contents may be
+  read and written and that data written is preserved when the file is
+  closed and reopened.  The data and resource forks are, of course,
+  supported only for files, not directories.
+
+  Entry id 7 specifies some of the same data given by ids 8 and 10.  If
+  you create a header file with an entry for id 7 and for ids 8 or 10,
+  then the behavior with respect to their interaction is undefined.  A
+  header that contains an entry for id 7 and for ids 8 or 10 is not
+  valid as either a version 1 or a version 2 header file, so there is no
+  reason to do this and future versions of hfs_fs may prevent it.
+
+  Entry id 3 (``Real Name'') is read-only, since it will change
+  automatically when a file is renamed.  Writes to the corresponding
+  entry are silently ignored.
+
+  All other entry ids are ignored.  You may create descriptors for them;
+  in fact the default header layout when fork=netatalk includes a
+  descriptor for id 4 (``Comment'').  However writes to the entries
+  corresponding to the ignored ids fail silently and reads from the
+  entries always return zeros.  However, you shouldn't write random
+  bytes to unsupported entries, since they may be supported in the
+  future.
+
+  All of the supported entry types except the data and resource forks
+  have a fixed length.  If you give them a smaller length in the
+  descriptor then you are unable to access part of the corresponding
+  entry.  If you give them a larger length in the descriptor, then the
+  corresponding entry is padded with zeros and writes to the extra space
+  are silently ignored.
+
+  Writes to the length field of descriptors for the data and resource
+  forks will cause the corresponding fork to grow (with zero padding) or
+  shrink to the indicated length.
+
+  If you have an entry for the data fork then the descriptor's length
+  field does not change automatically to reflect any modification of the
+  data fork directly (the data does change however).  If the data fork
+  is longer than the descriptor indicates, then a portion of it is
+  inaccessible.  If the data fork is shorter than the descriptor
+  indicates then reads will be padded with zeros.
+
+  Writes beyond the end of the data or resource fork that extend into
+  empty space between entries or beyond the end of the file will extend
+  the corresponding fork, automatically changing the length field of the
+  corresponding descriptor.  Writes to any other space between entries
+  are silently ignored and read of such spaces always return zeros.
+
+  Calling truncate() on a header file can change the length of either
+  fork and such a change will automatically be reflected in the length
+  field of the corresponding descriptor.  If truncate() shortens the
+  file so that the entry for either fork would extend beyond the new end
+  of the file then the fork is shortened to fit in the space that
+  remains, or to zero bytes if the corresponding entry is now entirely
+  beyond the end of the file.  If the last entry in a header file is the
+  data or resource fork then a call to truncate() that extends the
+  header file will extend the fork with zeros.  Note that this happens
+  even if there was previously space between the end of the fork and the
+  end of the file.
+
+  77..  FFrreeqquueennttllyy AAsskkeedd QQuueessttiioonnss ((aanndd tthheeiirr aannsswweerrss))
+
+  This is a quick and dirty attempt at answering some of the more common
+  questions I receive about hfs_fs.  Additions are welcome.
+
+  77..11..  WWhhaatt iiss HHFFSS??
+
+  HFS stands for ``Hierarchical File System'' and is the filesystem used
+  by the Mac Plus and all later Macintosh models.  Earlier Macintosh
+  models used MFS (``Macintosh File System''), which is not supported by
+  hfs_fs.
+
+  77..22..  DDooeess hhffss__ffss aallllooww mmee ttoo mmoouunntt AApppplleeSShhaarree vvoolluummeess??
+
+  No, hfs_fs is for mounting local volumes only.  I am not aware of any
+  software that allows Linux to act as an AppleShare client.
+  77..33..  WWhhyy ccaann II mmoouunntt ssoommee HHFFSS CCDDRROOMMss bbuutt nnoott ootthheerrss??
+
+  In the past there was a known incompatibility with some ``hybrid''
+  CDROMs that appear as HFS disks on Macs and as ISO9660 disks on other
+  systems.  I think I have fixed the problem.  So, if you encounter this
+  particular problem or have problems with specific non-hybrid CDROMs
+  please e-mail me with the title and manufacturer of the CD.
+
+  77..44..  WWhhaatt ddooeess ````oonnllyy 11002244--cchhaarr bblloocckkss iimmpplleemmeenntteedd ((551122))'''' mmeeaann??
+
+  This message comes from the kernel and indicates that an attempt was
+  made to read a 512-byte block from a device that doesn't support
+  512-byte blocks.  hfs_fs only works with 512-byte blocks, and
+  therefore doesn't function with these devices.  Eventually hfs_fs will
+  be able to use 1024-byte blocks when necessary.
+
+  77..55..  WWhhyy ddoo II ggeett aa mmeessssaaggee aabboouutt aa bbaadd oorr uunnkknnoowwnn ppaarrttiittiioonn ttaabbllee??
+
+  The Linux kernel doesn't yet know anything about Macintosh partition
+  tables, so it gives this warning when it can't find a partition table
+  it recognizes.  The decoding of Mac partition tables is done by
+  hfs_fs, so you should still be able to mount the disk.
+
+  77..66..  CCaann II mmoouunntt mmuullttiippllee HHFFSS ppaarrttiittiioonnss ffrroomm tthhee ssaammee MMaacciinnttoosshh
+  ddiisskk??
+
+  Not simultaneously.  Because the Linux kernel doesn't understand the
+  Macintosh partition table, hfs_fs must access the raw device.
+  Therefore, the kernel thinks the entire drive is in use and prevents
+  additional mounts on it.  However, you can use the part=n mount option
+  to select which HFS partition to mount.
+
+  77..77..  IInn wwhhaatt wwaayyss ccaann II wwrriittee ttoo HHFFSS vvoolluummeess??
+
+  hfs_fs is as capable as the MS-DOS or VFAT filesystems, except that
+  certain things can only be done with a file's data fork.
+
+  You ccaann:
+
+  +o  Create, delete and rename directories and data forks of files with
+     the caveat that names are case insensitive (so foo and Foo are the
+     same file or directory).
+
+  +o  Run Linux executables or shared libraries on an HFS disk if they
+     are stored in the data fork of a file.
+
+  +o  Read, write and truncate both forks of files and the Finder's
+     metadata of files and directories.
+
+  +o  Mmap data forks of files (and the resource fork if the filesystem
+     is mounted with the fork=cap option).
+
+  +o  Toggle the 'w' permission bits (as a group) of data forks.
+
+  +o  Change the i_mtime of files and directories.
+
+  You ccaannnnoott:
+
+  +o  Create, delete or rename resource forks of files or the Finder's
+     metadata.  Note, however, that they are created (with defaults
+     values), deleted and renamed along with the corresponding data fork
+     or directory.
+
+  +o  Run Linux executables or shared libraries on an HFS disk if they
+     are stored in the resource fork of a file.
+
+  +o  Mmap the Finder's metadata (when fork=cap) or AppleDouble header
+     files (when fork=double or fork=netatalk).
+
+  +o  Change permissions on directories.
+
+  +o  Change the uid or gid of files or directories.
+
+  +o  Set the set-uid, set-gid or sticky permission bits.
+
+  +o  Create multiple links to files.
+
+  +o  Create symlinks, device files, sockets or FIFOs.
+
+  77..88..  DDooeess hhffss__ffss wwoorrkk wwiitthh 440000kk oorr 880000kk MMaacciinnttoosshh ddiisskkeetttteess??
+
+  Yes and no.  The software is fully capable of dealing with HFS disks
+  of any size.  However, the 400k and 800k diskettes are written in a
+  physical format that is incompatible with most non-Macintosh floppy
+  drives.  Note also that almost all 400k Macintosh diskettes are MFS,
+  not HFS.
+
+  77..99..  HHooww ccaann II ffoorrmmaatt aann HHFFSS vvoolluummee??
+
+  Robert Leslie ([email protected]) has written a package for working with
+  HFS volumes (like mtools plus a graphical interface).  One program in
+  the package is hformat which can format HFS volumes.  The latest
+  version can be found on the HFS Utilities home page
+  <http://www.mars.org/home/rob/proj/hfs/>.
+
+  88..  RReeppoorrttiinngg BBuuggss
+
+  If you'd like any problems you encounter fixed, you'll need to provide
+  a detailed bug report.  However, you should check the FAQ first to be
+  certain that your problem is not a known limitation of hfs_fs.  If
+  your bug doesn't appear in the FAQ then you should e-mail me at
+  [email protected].
+
+  88..11..  WWhhaatt GGooeess iinn aa BBuugg RReeppoorrtt
+
+  When writing your bug report, include any facts you think might be
+  relevant; I'd much rather have a bunch of extra facts than need to e-
+  mail you to get the information.  At a minimum the following
+  information should be included:
+
+  +o  The version of hfs_fs you are using.
+
+  +o  The kernel version you are using.
+
+  +o  The version of gcc you used to compile the module, including
+     whether it is producing ELF or a.out output.
+
+  +o  The version of the module utilities used to load hfs.o.
+
+  +o  Any unofficial kernel patches or loadable modules you are using.
+
+  +o  The type of media you are working with (floppy, CDROM, ZIP Drive,
+     etc.).
+
+  +o  The steps required to reproduce the bug, including mount options
+     used.  (If you can't reproduce the bug tell me everything you did
+     the one time it did occur, but be warned that non-reproducible bugs
+     can only rarely be fixed.)
+
+  There is a script, BUG_INFO, in the hfs_fs source directory that will
+  attempt to determine the first four items for you.
+  88..22..  HHooww ttoo RReeppoorrtt aa KKeerrnneell OOooppss oorr GGPPFF
+
+  If you encounter a bug that causes a kernel Oops or a General
+  Protection Fault then you'll need to collect some information for the
+  bug report before you reboot.  It is important that you do this before
+  rebooting, since the module is unlikely to be loaded at the same
+  address after the reboot.
+
+  You should include all the information that the kernel prints to the
+  console or to the system logs.  However, the EIP and Stack Trace are
+  addresses in _y_o_u_r kernel and mean nothing to me without more
+  information.  Using your System.map file (or either ksymoops or klogd)
+  determine which functions the EIP and Stack Trace are in.  If you do
+  this by hand using your System.map file then the correct symbol is the
+  one of type t or T with the largest address less than or equal to the
+  one you are resolving.
+
+  If the Oops or GPF was in the hfs_fs code then the EIP and the top
+  levels of the Stack Trace will be in a loadable module, rather than in
+  the kernel proper.  So, their symbols will not be in the file
+  System.map.  Therefore, you will need to use /proc/ksyms, or a loadmap
+  produced by passing the -m option to insmod, to locate those symbols.
+  Keep in mind that neither of these files is sorted.
+
+  99..  LLeeggaall NNoottiicceess
+
+  99..11..  TThhiiss DDooccuummeenntt
+
+  This document is Copyright (c) 1996, 1997 by Paul H. Hargrove.
+
+  Permission is granted to make and distribute verbatim copies of this
+  document provided the copyright notice and this permission notice are
+  preserved on all copies.
+
+  Permission is granted to copy and distribute modified versions of this
+  document under the conditions for verbatim copies above, provided a
+  notice clearly stating that the document is a modified version is also
+  included in the modified document.
+
+  Permission is granted to copy and distribute translations of this
+  document into another language, under the conditions specified above
+  for modified versions.
+
+  Permission is granted to convert this document into another media
+  under the conditions specified above for modified versions provided
+  the requirement to acknowledge the source document is fulfilled by
+  inclusion of an obvious reference to the source document in the new
+  media. Where there is any doubt as to what defines ``obvious'' the
+  copyright owner reserves the right to decide.
+
+  99..22..  TThhee SSooffttwwaarree
+
+  The hfs_fs software is Copyright (c) 1994-1997 by Paul H. Hargrove.
+
+  This software is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2, or (at your option)
+  any later version.
+
+  This software is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this software in the file ``COPYING''; if not, write to the
+  Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
+  USA.
+
+  99..22..11..  TThhee CCoolluummbbiiaa AApppplleeTTaallkk PPaacckkaaggee ffoorr UUNNIIXX
+
+  The source code distribution of the Columbia AppleTalk Package for
+  UNIX, version 6.0, (CAP) was used as a _s_p_e_c_i_f_i_c_a_t_i_o_n of the location
+  and format of files used by CAP's Aufs.  No code from CAP appears in
+  hfs_fs.  hfs_fs is not a work ``derived'' from CAP in the sense of
+  intellectual property law.
+
+  99..22..22..  NNeettaattaallkk
+
+  The source code distributions of Netatalk, versions 1.3.3b2 and 1.4b2,
+  were used as a _s_p_e_c_i_f_i_c_a_t_i_o_n of the location and format of files used
+  by Netatalk's afpd.  No code from Netatalk appears in hfs_fs.  hfs_fs
+  is not a work ``derived'' from Netatalk in the sense of intellectual
+  property law.
+
+  99..33..  TTrraaddeemmaarrkkss
+
+  +o  ``Finder'' is a trademarks of Apple Computer, Inc.
+
+  +o  ``Apple'', ``AppleShare'', ``AppleTalk'' and ``Macintosh'' are
+     registered trademarks of Apple Computer, Inc.
+
+  +o  ``Microsoft'' and ``MS-DOS'' are registered trademarks of Microsoft
+     Corporation.
+
+  +o  All other trademarks are the property of their respective owners.
+
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/fs/hfs/TODO linux-2.0.29/fs/hfs/TODO
--- linux.vanilla/fs/hfs/TODO   Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/fs/hfs/TODO    Wed Apr  9 04:41:47 1997
@@ -0,0 +1,54 @@
+The hfs_fs "to do" list.
+------------------------
+Items are broken down into groups and the groups are listed in order
+from most important to least important.  The items within each group
+are not placed in any particular order.  The order in which items are
+listed probably doesn't correlate well with the order they will be
+addressed.
+
+Genuine bugs:
+1.     Header files have compiled-in limit (currently 10) on descriptors.
+
+Missing features:
+1.     The partition code should be migrated into the kernel to allow
+       simultaneous access to multiple partitions on a single disk.
+2.     1k block support is needed for some devices.
+3.     An ioctl()-based interface is needed to provide a consistent way
+       to do things under all of the representations of forked files.
+
+Possible additional "fork" mount options:
+1.     AppleSingle.
+2.     The scheme MacOS uses on FAT disks (PC Exchange).
+3.     "Flat" (no resource forks or metadata).
+
+Performance issues:
+1.     Use drAllocPtr to speed block allocations.
+2.     Keep a real cache of bnodes, rather than just a hash table of
+       the ones that are currently in use.
+3.     Keep a real cache of extent records, rather than just a linked
+       list of the ones that are currently in use and the one most
+       recently used.  This is particularly needed to get acceptable
+       performance with multiple readers on a file.  Perhaps simply
+       keep them in memory once they've been read until the file is
+       closed.
+
+Implementation details:
+1.     Allocation scheme could/should be closer to that used by Apple.
+2.     B*-tree insertion could/should be closer to that used by Apple.
+3.     Magic-number checks on data structures are rarely done.
+4.     Error recovery is needed for failed binsert(), bdelete() and rename().
+5.     Deadlock detection is needed to make insert_empty_bnode() and
+       bdelete() less likely to hang on a corrupted B-tree.
+6.     Metadata for covered directories shouldn't appear in the filesystem.
+       Under CAP and AppleDouble it currently does.  However, the obvious
+       solution is a real performance killer and is not worth implementing.
+
+Fantasy features:
+1.     Access Desktop file/database for comment and icon.
+2.     Implement mmap() for AppleDouble header files and CAP info files.
+3.     Implement AppleShare client support.
+
+Suggestions/comments/questions are welcome.
+Code addressing any of the issues listed above is especially welcome.
+Paul H. Hargrove
[email protected]
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/fs/hfs/balloc.c linux-2.0.29/fs/hfs/balloc.c
--- linux.vanilla/fs/hfs/balloc.c       Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/fs/hfs/balloc.c        Thu Apr 10 12:22:02 1997
@@ -0,0 +1,433 @@
+/*
+ * linux/fs/hfs/balloc.c
+ *
+ * Copyright (C) 1995-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * hfs_bnode_alloc() and hfs_bnode_bitop() are based on GPLed code
+ * Copyright (C) 1995  Michael Dreher
+ *
+ * This file contains the code to create and destroy nodes
+ * in the B-tree structure.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ *
+ * The code in this file initializes some structures which contain
+ * pointers by calling memset(&foo, 0, sizeof(foo)).
+ * This produces the desired behavior only due to the non-ANSI
+ * assumption that the machine representation of NULL is all zeros.
+ */
+
+#include <linux/hfs_btree.h>
+
+/*================ File-local functions ================*/
+
+/*
+ * get_new_node()
+ *
+ * Get a buffer for a new node with out reading it from disk.
+ */
+static hfs_buffer get_new_node(struct hfs_btree *tree, hfs_u32 node)
+{
+       int tmp;
+       hfs_buffer retval = HFS_BAD_BUFFER;
+
+       tmp = hfs_extent_map(&tree->entry.u.file.data_fork, node, 0);
+       if (tmp) {
+               retval = hfs_buffer_get(tree->sys_mdb, tmp, 0);
+       }
+       return retval;
+}
+
+/*
+ * hfs_bnode_init()
+ *
+ * Description:
+ *   Initialize a newly allocated bnode.
+ * Input Variable(s):
+ *   struct hfs_btree *tree: Pointer to a B-tree
+ *   hfs_u32 node: the node number to allocate
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   struct hfs_bnode_ref for the new node
+ * Preconditions:
+ *   'tree' points to a "valid" (struct hfs_btree)
+ *   'node' exists and has been allocated in the bitmap of bnodes.
+ * Postconditions:
+ *   On success:
+ *    The node is not read from disk, nor added to the bnode cache.
+ *    The 'sticky' and locking-related fields are all zero/NULL.
+ *    The bnode's nd{[FB]Link, Type, NHeight} fields are uninitialized.
+ *    The bnode's ndNRecs field and offsets table indicate an empty bnode.
+ *   On failure:
+ *    The node is deallocated.
+ */
+static struct hfs_bnode_ref hfs_bnode_init(struct hfs_btree * tree,
+                                          hfs_u32 node)
+{
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+       extern int bnode_count;
+#endif
+       struct hfs_bnode_ref retval;
+
+       retval.lock_type = HFS_LOCK_NONE;
+       if (!HFS_NEW(retval.bn)) {
+               hfs_warn("hfs_bnode_init: out of memory.\n");
+               goto bail2;
+       }
+
+       /* Partially initialize the in-core structure */
+       memset(retval.bn, 0, sizeof(*retval.bn));
+       retval.bn->magic = HFS_BNODE_MAGIC;
+       retval.bn->tree = tree;
+       retval.bn->node = node;
+       hfs_bnode_lock(&retval, HFS_LOCK_WRITE);
+
+       retval.bn->buf = get_new_node(tree, node);
+       if (!hfs_buffer_ok(retval.bn->buf)) {
+               goto bail1;
+       }
+
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+       ++bnode_count;
+#endif
+
+       /* Partially initialize the on-disk structure */
+       memset(hfs_buffer_data(retval.bn->buf), 0, HFS_SECTOR_SIZE);
+       hfs_put_hs(sizeof(struct NodeDescriptor), RECTBL(retval.bn, 1));
+
+       return retval;
+
+bail1:
+       HFS_DELETE(retval.bn);
+bail2:
+       /* clear the bit in the bitmap */
+       hfs_bnode_bitop(tree, node, 0);
+       return retval;
+}
+
+/*
+ * init_mapnode()
+ *
+ * Description:
+ *   Initializes a given node as a mapnode in the given tree.
+ * Input Variable(s):
+ *   struct hfs_bnode *bn: the node to add the mapnode after.
+ *   hfs_u32: the node to use as a mapnode.
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   struct hfs_bnode *: the new mapnode or NULL
+ * Preconditions:
+ *   'tree' is a valid (struct hfs_btree).
+ *   'node' is the number of the first node in 'tree' that is not
+ *    represented by a bit in the existing mapnodes.
+ * Postconditions:
+ *   On failure 'tree' is unchanged and NULL is returned.
+ *   On success the node given by 'node' has been added to the linked
+ *    list of mapnodes attached to 'tree', and has been initialized as
+ *    a valid mapnode with its first bit set to indicate itself as
+ *    allocated.
+ */
+static struct hfs_bnode *init_mapnode(struct hfs_bnode *bn, hfs_u32 node)
+{
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+       extern int bnode_count;
+#endif
+       struct hfs_bnode *retval;
+
+       if (!HFS_NEW(retval)) {
+               hfs_warn("hfs_bnode_add: out of memory.\n");
+               return NULL;
+       }
+
+       memset(retval, 0, sizeof(*retval));
+       retval->magic = HFS_BNODE_MAGIC;
+       retval->tree = bn->tree;
+       retval->node = node;
+       retval->sticky = HFS_STICKY;
+       retval->buf = get_new_node(bn->tree, node);
+       if (!hfs_buffer_ok(retval->buf)) {
+               HFS_DELETE(retval);
+               return NULL;
+       }
+
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+       ++bnode_count;
+#endif
+
+       /* Initialize the bnode data structure */
+       memset(hfs_buffer_data(retval->buf), 0, HFS_SECTOR_SIZE);
+       retval->ndFLink = 0;
+       retval->ndBLink = bn->node;
+       retval->ndType = ndMapNode;
+       retval->ndNHeight = 0;
+       retval->ndNRecs = 1;
+       hfs_put_hs(sizeof(struct NodeDescriptor), RECTBL(retval, 1));
+       hfs_put_hs(0x1fa,                         RECTBL(retval, 2));
+       hfs_set_bit(0, bnode_key(retval, 1));
+       retval->prev = bn;
+       hfs_bnode_commit(retval);
+
+       bn->ndFLink = node;
+       bn->next = retval;
+       hfs_bnode_commit(bn);
+
+       return retval;
+}
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_bnode_bitop()
+ *
+ * Description:
+ *   Allocate/free the requested node of a B-tree of the hfs filesystem
+ *   by setting/clearing the corresponding bit in the B-tree bitmap.
+ *   The size of the B-tree will not be changed.
+ * Input Variable(s):
+ *   struct hfs_btree *tree: Pointer to a B-tree
+ *   hfs_u32 bitnr: The node number to free
+ *   int set: 0 to clear the bit, non-zero to set it.
+ * Output Variable(s):
+ *   None
+ * Returns:
+ *    0: no error
+ *   -1: The node was already allocated/free, nothing has been done.
+ *   -2: The node is out of range of the B-tree.
+ *   -4: not enough map nodes to hold all the bits
+ * Preconditions:
+ *   'tree' points to a "valid" (struct hfs_btree)
+ *   'bitnr' is a node number within the range of the btree, which is
+ *   currently free/allocated.
+ * Postconditions:
+ *   The bit number 'bitnr' of the node bitmap is set/cleared and the
+ *   number of free nodes in the btree is decremented/incremented by one.
+ */
+int hfs_bnode_bitop(struct hfs_btree *tree, hfs_u32 bitnr, int set)
+{
+       struct hfs_bnode *bn;   /* the current bnode */
+       hfs_u32 bmap_size;      /* number of bits in the current bitmap */
+       hfs_u32 *bmap;          /* address of the hfs_u32 containing the bit */
+
+       if (bitnr >= tree->bthNNodes) {
+               hfs_warn("hfs_bnode_bitop: node number out of range.\n");
+               return -2;
+       }
+
+       bn = &tree->head;
+       for (;;) {
+               bmap_size = 8*bnode_rsize(bn, bn->ndNRecs);
+
+               if (bitnr < bmap_size) {
+                       break;
+               }
+
+               /* continue on to next map node if available */
+               if (!(bn = bn->next)) {
+                       hfs_warn("hfs_bnode_bitop: too few map nodes.\n");
+                       return -4;
+               }
+               bitnr -= bmap_size;
+       }
+
+       /* bitmap is always on a 32-bit boundary: */
+       bmap = (hfs_u32 *)bnode_key(bn, bn->ndNRecs)+(bitnr>>5);
+       bitnr %= 32;
+       if ((set && hfs_set_bit(bitnr, bmap)) ||
+           (!set && !hfs_clear_bit(bitnr, bmap))) {
+               hfs_warn("hfs_bnode_bitop: bitmap corruption.\n");
+               return -1;
+       }
+       hfs_buffer_dirty(bn->buf);
+
+       /* adjust the free count */
+       tree->bthFree += (set ? -1 : 1);
+       tree->dirt = 1;
+
+       return 0;
+}
+
+/*
+ * hfs_bnode_alloc()
+ *
+ * Description:
+ *   Find a cleared bit in the B-tree node bitmap of the hfs filesystem,
+ *   set it and return the corresponding bnode, with its contents zeroed.
+ *   When there is no free bnode in the tree, an error is returned, no
+ *   new nodes will be added by this function!
+ * Input Variable(s):
+ *   struct hfs_btree *tree: Pointer to a B-tree
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   struct hfs_bnode_ref for the new bnode
+ * Preconditions:
+ *   'tree' points to a "valid" (struct hfs_btree)
+ *   There is at least one free bnode.
+ * Postconditions:
+ *   On success:
+ *     The corresponding bit in the btree bitmap is set.
+ *     The number of free nodes in the btree is decremented by one.
+ *   The node is not read from disk, nor added to the bnode cache.
+ *   The 'sticky' field is uninitialized.
+ */
+struct hfs_bnode_ref hfs_bnode_alloc(struct hfs_btree *tree)
+{
+       struct hfs_bnode *bn;   /* the current bnode */
+       hfs_u32 bitnr = 0;      /* which bit are we examining */
+       hfs_u32 bmap_size;      /* number of bits in current bitmap */
+       hfs_u16 first;          /* the first clear bit in this bnode */
+       hfs_u32 *bitmap;        /* address of the bitmap in this bnode */
+
+       bn = &tree->head;
+       for (;;) {
+               bmap_size = 8*bnode_rsize(bn, bn->ndNRecs);
+               /* bitmap is always on a 32-bit boundary: */
+               bitmap = (hfs_u32 *)bnode_key(bn, bn->ndNRecs);
+
+               /* search the current node */
+               first = hfs_find_first_zero_bit(bitmap, bmap_size);
+               if (first < bmap_size) {
+                       break;
+               }
+
+               /* continue search in next map node */
+               bn = bn->next;
+
+               if (!bn) {
+                       hfs_warn("hfs_bnode_alloc: too few map nodes.\n");
+                       goto bail;
+               }
+               bitnr += bmap_size;
+       }
+
+       if ((bitnr += first) >= tree->bthNNodes) {
+               hfs_warn("hfs_bnode_alloc: no free nodes found, "
+                        "count wrong?\n");
+               goto bail;
+       }
+
+       if (hfs_set_bit(first % 32, bitmap + (first>>5))) {
+               hfs_warn("hfs_bnode_alloc: bitmap corruption.\n");
+               goto bail;
+       }
+       hfs_buffer_dirty(bn->buf);
+
+       /* decrement the free count */
+       --tree->bthFree;
+       tree->dirt = 1;
+
+       return hfs_bnode_init(tree, bitnr);
+
+bail:
+       return (struct hfs_bnode_ref){NULL, HFS_LOCK_NONE};
+}
+
+/*
+ * hfs_btree_extend()
+ *
+ * Description:
+ *   Adds nodes to a B*-tree if possible.
+ * Input Variable(s):
+ *   struct hfs_btree *tree: the btree to add nodes to.
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   void
+ * Preconditions:
+ *   'tree' is a valid (struct hfs_btree *).
+ * Postconditions:
+ *   If possible the number of nodes indicated by the tree's clumpsize
+ *    have been added to the tree, updating all in-core and on-disk
+ *    allocation information.
+ *   If insufficient disk-space was available then fewer nodes may have
+ *    been added than would be expected based on the clumpsize.
+ *   In the case of the extents B*-tree this function will add fewer
+ *    nodes than expected if adding more would result in an extent
+ *    record for the extents tree being added to the extents tree.
+ *    The situation could be dealt with, but doing so confuses Macs.
+ */
+void hfs_btree_extend(struct hfs_btree *tree)
+{
+       struct hfs_bnode_ref head;
+       struct hfs_bnode *bn, *tmp;
+       struct hfs_cat_entry *entry = &tree->entry;
+       struct hfs_mdb *mdb = entry->mdb;
+       hfs_u32 old_nodes, new_nodes, total_nodes, new_mapnodes, seen;
+
+       old_nodes = entry->u.file.data_fork.psize;
+
+       entry->u.file.data_fork.lsize += 1; /* rounded up to clumpsize */
+       hfs_extent_adj(&entry->u.file.data_fork);
+
+       total_nodes = entry->u.file.data_fork.psize;
+       entry->u.file.data_fork.lsize = total_nodes << HFS_SECTOR_SIZE_BITS;
+       new_nodes = total_nodes - old_nodes;
+       if (!new_nodes) {
+               return;
+       }
+
+       head = hfs_bnode_find(tree, 0, HFS_LOCK_WRITE);
+       if (!(bn = head.bn)) {
+               hfs_warn("hfs_btree_extend: header node not found.\n");
+               return;
+       }
+
+       seen = 0;
+       new_mapnodes = 0;
+       for (;;) {
+               seen += 8 * bnode_rsize(bn, bn->ndNRecs);
+
+               if (seen >= total_nodes) {
+                       break;
+               }
+
+               if (!bn->next) {
+                       tmp = init_mapnode(bn, seen);
+                       if (!tmp) {
+                               hfs_warn("hfs_btree_extend: "
+                                        "can't build mapnode.\n");
+                               hfs_bnode_relse(&head);
+                               return;
+                       }
+                       ++new_mapnodes;
+               }
+               bn = bn->next;
+       }
+       hfs_bnode_relse(&head);
+
+       tree->bthNNodes = total_nodes;
+       tree->bthFree += (new_nodes - new_mapnodes);
+       tree->dirt = 1;
+
+       /* write the backup MDB, not returning until it is written */
+       hfs_mdb_commit(mdb, 1);
+
+       return;
+}
+
+/*
+ * hfs_bnode_free()
+ *
+ * Remove a node from the cache and mark it free in the bitmap.
+ */
+int hfs_bnode_free(struct hfs_bnode_ref *bnr)
+{
+       hfs_u32 node = bnr->bn->node;
+       struct hfs_btree *tree = bnr->bn->tree;
+
+       if (bnr->bn->count != 1) {
+               hfs_warn("hfs_bnode_free: count != 1.\n");
+               return -EIO;
+       }
+
+       hfs_bnode_relse(bnr);
+       hfs_bnode_bitop(tree, node, 0);
+       return 0;
+}
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/fs/hfs/bdelete.c linux-2.0.29/fs/hfs/bdelete.c
--- linux.vanilla/fs/hfs/bdelete.c      Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/fs/hfs/bdelete.c       Thu Apr 10 12:21:52 1997
@@ -0,0 +1,483 @@
+/*
+ * linux/fs/hfs/bdelete.c
+ *
+ * Copyright (C) 1995-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the code to delete records in a B-tree.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include <linux/hfs_btree.h>
+
+/*================ Variable-like macros ================*/
+
+#define FULL (HFS_SECTOR_SIZE - sizeof(struct NodeDescriptor))
+#define NO_SPACE (HFS_SECTOR_SIZE+1)
+
+/*================ File-local functions ================*/
+
+/*
+ * bdelete_nonempty()
+ *
+ * Description:
+ *   Deletes a record from a given bnode without regard to it becoming empty.
+ * Input Variable(s):
+ *   struct hfs_brec* brec: pointer to the brec for the deletion
+ *   struct hfs_belem* belem: which node in 'brec' to delete from
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   void
+ * Preconditions:
+ *   'brec' points to a valid (struct hfs_brec).
+ *   'belem' points to a valid (struct hfs_belem) in 'brec'.
+ * Postconditions:
+ *   The record has been inserted in the position indicated by 'brec'.
+ */
+static void bdelete_nonempty(struct hfs_brec *brec, struct hfs_belem *belem)
+{
+       int i, rec, nrecs, tomove;
+       hfs_u16 size;
+       hfs_u8 *start;
+       struct hfs_bnode *bnode = belem->bnr.bn;
+
+       rec = belem->record;
+       nrecs = bnode->ndNRecs;
+       size = bnode_rsize(bnode, rec);
+       tomove = bnode_offset(bnode, nrecs+1) - bnode_offset(bnode, rec+1);
+
+       /* adjust the record table */
+       for (i = rec+1; i <= nrecs; ++i) {
+               hfs_put_hs(bnode_offset(bnode,i+1) - size, RECTBL(bnode,i));
+       }
+
+       /* move it down */
+       start = bnode_key(bnode, rec);
+       memmove(start, start + size, tomove);
+
+       /* update record count */
+       --bnode->ndNRecs;
+}
+
+/*
+ * del_root()
+ *
+ * Description:
+ *   Delete the current root bnode.
+ * Input Variable(s):
+ *   struct hfs_bnode_ref *root: reference to the root bnode
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   int: 0 on success, error code on failure
+ * Preconditions:
+ *   'root' refers to the root bnode with HFS_LOCK_WRITE access.
+ *   None of 'root's children are held with HFS_LOCK_WRITE access.
+ * Postconditions:
+ *   The current 'root' node is removed from the tree and the depth
+ *    of the tree is reduced by one.
+ *   If 'root' is an index node with exactly one child, then that
+ *    child becomes the new root of the tree.
+ *   If 'root' is an empty leaf node the tree becomes empty.
+ *   Upon return access to 'root' is relinquished.
+ */
+static int del_root(struct hfs_bnode_ref *root)
+{
+       struct hfs_btree *tree = root->bn->tree;
+       struct hfs_bnode_ref child;
+       hfs_u32 node;
+
+       if (root->bn->ndNRecs > 1) {
+               return 0;
+       } else if (root->bn->ndNRecs == 0) {
+               /* tree is empty */
+               tree->bthRoot = 0;
+               tree->root = NULL;
+               tree->bthRoot = 0;
+               tree->bthFNode = 0;
+               tree->bthLNode = 0;
+               --tree->bthDepth;
+               tree->dirt = 1;
+               if (tree->bthDepth) {
+                       hfs_warn("hfs_bdelete: empty tree with bthDepth=%d\n",
+                                tree->bthDepth);
+                       goto bail;
+               }
+               return hfs_bnode_free(root);
+       } else if (root->bn->ndType == ndIndxNode) {
+               /* tree is non-empty */
+               node = hfs_get_hl(bkey_record(bnode_datastart(root->bn)));
+
+               child = hfs_bnode_find(tree, node, HFS_LOCK_READ);
+               if (!child.bn) {
+                       hfs_warn("hfs_bdelete: can't read child node.\n");
+                       goto bail;
+               }
+
+               child.bn->sticky = HFS_STICKY;
+               if (child.bn->next) {
+                       child.bn->next->prev = child.bn->prev;
+               }
+               if (child.bn->prev) {
+                       child.bn->prev->next = child.bn->next;
+               }
+               if (bhash(tree, child.bn->node) == child.bn) {
+                       bhash(tree, child.bn->node) = child.bn->next;
+               }
+               child.bn->next = NULL;
+               child.bn->prev = NULL;
+
+               tree->bthRoot = child.bn->node;
+               tree->root = child.bn;
+               hfs_bnode_relse(&child);
+
+               tree->bthRoot = node;
+               tree->bthFNode = node;
+               tree->bthLNode = node;
+               --tree->bthDepth;
+               tree->dirt = 1;
+               if (!tree->bthDepth) {
+                       hfs_warn("hfs_bdelete: non-empty tree with "
+                                "bthDepth == 0\n");
+                       goto bail;
+               }
+               return hfs_bnode_free(root);    /* marks tree dirty */
+       }
+       hfs_bnode_relse(root);
+       return 0;
+
+bail:
+       hfs_bnode_relse(root);
+       return -EIO;
+}
+
+
+/*
+ * delete_empty_bnode()
+ *
+ * Description:
+ *   Removes an empty non-root bnode from between 'left' and 'right'
+ * Input Variable(s):
+ *   hfs_u32 left_node: node number of 'left' or zero if 'left' is invalid
+ *   struct hfs_bnode_ref *left: reference to the left neighbor of the
+ *    bnode to remove, or invalid if no such neighbor exists.
+ *   struct hfs_bnode_ref *center: reference to the bnode to remove
+ *   hfs_u32 right_node: node number of 'right' or zero if 'right' is invalid
+ *   struct hfs_bnode_ref *right: reference to the right neighbor of the
+ *    bnode to remove, or invalid if no such neighbor exists.
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   void
+ * Preconditions:
+ *   'left_node' is as described above.
+ *   'left' points to a valid (struct hfs_bnode_ref) having HFS_LOCK_WRITE
+ *    access and referring to the left neighbor of 'center' if such a
+ *    neighbor exists, or invalid if no such neighbor exists.
+ *   'center' points to a valid (struct hfs_bnode_ref) having HFS_LOCK_WRITE
+ *    access and referring to the bnode to delete.
+ *   'right_node' is as described above.
+ *   'right' points to a valid (struct hfs_bnode_ref) having HFS_LOCK_WRITE
+ *    access and referring to the right neighbor of 'center' if such a
+ *    neighbor exists, or invalid if no such neighbor exists.
+ * Postconditions:
+ *   If 'left' is valid its 'ndFLink' field becomes 'right_node'.
+ *   If 'right' is valid its 'ndBLink' field becomes 'left_node'.
+ *   If 'center' was the first leaf node then the tree's 'bthFNode'
+ *    field becomes 'right_node'
+ *   If 'center' was the last leaf node then the tree's 'bthLNode'
+ *    field becomes 'left_node'
+ *   'center' is NOT freed and access to the nodes is NOT relinquished.
+ */
+static void delete_empty_bnode(hfs_u32 left_node, struct hfs_bnode_ref *left,
+                              struct hfs_bnode_ref *center,
+                              hfs_u32 right_node, struct hfs_bnode_ref *right)
+{
+       struct hfs_bnode *bnode = center->bn;
+
+       if (left_node) {
+               left->bn->ndFLink = right_node;
+       } else if (bnode->ndType == ndLeafNode) {
+               bnode->tree->bthFNode = right_node;
+               bnode->tree->dirt = 1;
+       }
+
+       if (right_node) {
+               right->bn->ndBLink = left_node;
+       } else if (bnode->ndType == ndLeafNode) {
+               bnode->tree->bthLNode = left_node;
+               bnode->tree->dirt = 1;
+       }
+}
+
+/*
+ * balance()
+ *
+ * Description:
+ *   Attempt to equalize space usage in neighboring bnodes.
+ * Input Variable(s):
+ *   struct hfs_bnode *left: the left bnode.
+ *   struct hfs_bnode *right: the right bnode.
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   void
+ * Preconditions:
+ *   'left' and 'right' point to valid (struct hfs_bnode)s obtained
+ *    with HFS_LOCK_WRITE access, and are neighbors.
+ * Postconditions:
+ *   Records are shifted either left or right to make the space usage
+ *   nearly equal.  When exact equality is not possible the break
+ *   point is chosen to reduce data movement.
+ *   The key corresponding to 'right' in its parent is NOT updated.
+ */
+static void balance(struct hfs_bnode *left, struct hfs_bnode *right)
+{
+       int index, left_free, right_free, half;
+
+       left_free = bnode_freespace(left);
+       right_free = bnode_freespace(right);
+       half = (left_free + right_free)/2;
+
+       if (left_free < right_free) {
+               /* shift right to balance */
+               index = left->ndNRecs + 1;
+               while (right_free >= half) {
+                       --index;
+                       right_free -= bnode_rsize(left,index)+sizeof(hfs_u16);
+               }
+               if (index < left->ndNRecs) {
+#if defined(DEBUG_ALL) || defined(DEBUG_BALANCE)
+                       hfs_warn("shifting %d of %d recs right to balance: ",
+                              left->ndNRecs - index, left->ndNRecs);
+#endif
+                       hfs_bnode_shift_right(left, right, index+1);
+#if defined(DEBUG_ALL) || defined(DEBUG_BALANCE)
+                       hfs_warn("%d,%d\n", left->ndNRecs, right->ndNRecs);
+#endif
+               }
+       } else {
+               /* shift left to balance */
+               index = 0;
+               while (left_free >= half) {
+                       ++index;
+                       left_free -= bnode_rsize(right,index)+sizeof(hfs_u16);
+               }
+               if (index > 1) {
+#if defined(DEBUG_ALL) || defined(DEBUG_BALANCE)
+                       hfs_warn("shifting %d of %d recs left to balance: ",
+                              index-1, right->ndNRecs);
+#endif
+                       hfs_bnode_shift_left(left, right, index-1);
+#if defined(DEBUG_ALL) || defined(DEBUG_BALANCE)
+                       hfs_warn("%d,%d\n", left->ndNRecs, right->ndNRecs);
+#endif
+               }
+       }
+}
+
+/*
+ * bdelete()
+ *
+ * Delete the given record from a B-tree.
+ */
+static int bdelete(struct hfs_brec *brec)
+{
+       struct hfs_btree *tree = brec->tree;
+       struct hfs_belem *belem = brec->bottom;
+       struct hfs_belem *parent = (belem-1);
+       struct hfs_bnode *bnode;
+       hfs_u32 left_node, right_node;
+       struct hfs_bnode_ref left, right;
+       int left_space, right_space, min_space;
+       int fix_right_key;
+       int fix_key;
+
+       while ((belem > brec->top) &&
+              (belem->flags & (HFS_BPATH_UNDERFLOW | HFS_BPATH_FIRST))) {
+               bnode = belem->bnr.bn;
+               fix_key = belem->flags & HFS_BPATH_FIRST;
+               fix_right_key = 0;
+
+               bdelete_nonempty(brec, belem);
+
+               if (bnode->node == tree->root->node) {
+                       del_root(&belem->bnr);
+                       --brec->bottom;
+                       goto done;
+               }
+
+               /* check for btree corruption which could lead to deadlock */
+               left_node = bnode->ndBLink;
+               right_node = bnode->ndFLink;
+               if ((left_node && hfs_bnode_in_brec(left_node, brec)) ||
+                   (right_node && hfs_bnode_in_brec(right_node, brec)) ||
+                   (left_node == right_node)) {
+                       hfs_warn("hfs_bdelete: corrupt btree\n");
+                       hfs_brec_relse(brec, NULL);
+                       return -EIO;
+               }
+
+               /* grab the left neighbor if it exists */
+               if (left_node) {
+                       hfs_bnode_lock(&belem->bnr, HFS_LOCK_RESRV);
+                       left = hfs_bnode_find(tree,left_node,HFS_LOCK_WRITE);
+                       if (!left.bn) {
+                               hfs_warn("hfs_bdelete: unable to read left "
+                                        "neighbor.\n");
+                               hfs_brec_relse(brec, NULL);
+                               return -EIO;
+                       }
+                       hfs_bnode_lock(&belem->bnr, HFS_LOCK_WRITE);
+                       if (parent->record != 1) {
+                               left_space = bnode_freespace(left.bn);
+                       } else {
+                               left_space = NO_SPACE;
+                       }
+               } else {
+                       left.bn = NULL;
+                       left_space = NO_SPACE;
+               }
+
+               /* grab the right neighbor if it exists */
+               if (right_node) {
+                       right = hfs_bnode_find(tree,right_node,HFS_LOCK_WRITE);
+                       if (!right.bn) {
+                               hfs_warn("hfs_bdelete: unable to read right "
+                                        "neighbor.\n");
+                               hfs_bnode_relse(&left);
+                               hfs_brec_relse(brec, NULL);
+                               return -EIO;
+                       }
+                       if (parent->record < parent->bnr.bn->ndNRecs) {
+                               right_space = bnode_freespace(right.bn);
+                       } else {
+                               right_space = NO_SPACE;
+                       }
+               } else {
+                       right.bn = NULL;
+                       right_space = NO_SPACE;
+               }
+
+               if (left_space < right_space) {
+                       min_space = left_space;
+               } else {
+                       min_space = right_space;
+               }
+
+               if (min_space == NO_SPACE) {
+                       hfs_warn("hfs_bdelete: no siblings?\n");
+                       hfs_brec_relse(brec, NULL);
+                       return -EIO;
+               }
+
+               if (bnode->ndNRecs == 0) {
+                       delete_empty_bnode(left_node, &left, &belem->bnr,
+                                          right_node, &right);
+               } else if (min_space + bnode_freespace(bnode) >= FULL) {
+                       if ((right_space == NO_SPACE) ||
+                           ((right_space == min_space) &&
+                            (left_space != NO_SPACE))) {
+                               hfs_bnode_shift_left(left.bn, bnode,
+                                                    bnode->ndNRecs);
+                       } else {
+                               hfs_bnode_shift_right(bnode, right.bn, 1);
+                               fix_right_key = 1;
+                       }
+                       delete_empty_bnode(left_node, &left, &belem->bnr,
+                                          right_node, &right);
+               } else if (min_space == right_space) {
+                       balance(bnode, right.bn);
+                       fix_right_key = 1;
+               } else {
+                       balance(left.bn, bnode);
+                       fix_key = 1;
+               }
+
+               if (fix_right_key) {
+                       hfs_bnode_update_key(brec, belem, right.bn, 1);
+               }
+
+               hfs_bnode_relse(&left);
+               hfs_bnode_relse(&right);
+
+               if (bnode->ndNRecs) {
+                       if (fix_key) {
+                               hfs_bnode_update_key(brec, belem, bnode, 0);
+                       }
+                       goto done;
+               }
+
+               hfs_bnode_free(&belem->bnr);
+               --brec->bottom;
+               belem = parent;
+               --parent;
+       }
+
+       if (belem < brec->top) {
+               hfs_warn("hfs_bdelete: Missing parent.\n");
+               hfs_brec_relse(brec, NULL);
+               return -EIO;
+       }
+
+       bdelete_nonempty(brec, belem);
+
+done:
+       hfs_brec_relse(brec, NULL);
+       return 0;
+}
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_bdelete()
+ *
+ * Delete the requested record from a B-tree.
+ */
+int hfs_bdelete(struct hfs_btree *tree, const struct hfs_bkey *key)
+{
+       struct hfs_belem *belem;
+       struct hfs_bnode *bnode;
+       struct hfs_brec brec;
+       int retval;
+
+       if (!tree || (tree->magic != HFS_BTREE_MAGIC) || !key) {
+               hfs_warn("hfs_bdelete: invalid arguments.\n");
+               return -EINVAL;
+       }
+
+       retval = hfs_bfind(&brec, tree, key, HFS_BFIND_DELETE);
+       if (!retval) {
+               belem = brec.bottom;
+               bnode = belem->bnr.bn;
+
+               belem->flags = 0;
+               if ((bnode->ndNRecs * sizeof(hfs_u16) + bnode_end(bnode) -
+                    bnode_rsize(bnode, belem->record)) < FULL/2) {
+                       belem->flags |= HFS_BPATH_UNDERFLOW;
+               }
+               if (belem->record == 1) {
+                       belem->flags |= HFS_BPATH_FIRST;
+               }
+
+               if (!belem->flags) {
+                       hfs_brec_lock(&brec, brec.bottom);
+               } else {
+                       hfs_brec_lock(&brec, NULL);
+               }
+
+               retval = bdelete(&brec);
+               if (!retval) {
+                       --brec.tree->bthNRecs;
+                       brec.tree->dirt = 1;
+               }
+               hfs_brec_relse(&brec, NULL);
+       }
+       return retval;
+}
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/fs/hfs/bfind.c linux-2.0.29/fs/hfs/bfind.c
--- linux.vanilla/fs/hfs/bfind.c        Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/fs/hfs/bfind.c Thu Apr 10 12:35:26 1997
@@ -0,0 +1,322 @@
+/*
+ * linux/fs/hfs/bfind.c
+ *
+ * Copyright (C) 1995, 1996  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the code to access records in a btree.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include <linux/hfs_btree.h>
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_brec_relse()
+ *
+ * Description:
+ *   This function releases some of the nodes associated with a brec.
+ * Input Variable(s):
+ *   struct hfs_brec *brec: pointer to the brec to release some nodes from.
+ *   struct hfs_belem *elem: the last node to release or NULL for all
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   void
+ * Preconditions:
+ *   'brec' points to a "valid" (struct hfs_brec)
+ * Postconditions:
+ *   All nodes between the indicated node and the beginning of the path
+ *    are released.
+ */
+void hfs_brec_relse(struct hfs_brec *brec, struct hfs_belem *elem)
+{
+       if (!elem) {
+               elem = brec->bottom;
+       }
+
+       while (brec->top <= elem) {
+               hfs_bnode_relse(&brec->top->bnr);
+               ++brec->top;
+       }
+}
+
+/*
+ * hfs_bfind()
+ *
+ * Description:
+ *   This function has sole responsibility for locating existing
+ *   records in a B-tree.  Given a B-tree and a key it locates the
+ *   "greatest" record "less than or equal to" the given key.  The
+ *   exact behavior is determined by the bits of the flags variable as
+ *   follows:
+ *     ('flags' & HFS_LOCK_MASK):
+ *      The lock_type argument to be used when calling hfs_bnode_find().
+ *     HFS_BFIND_EXACT: only accept an exact match, otherwise take the
+ *     "largest" record less than 'target' as a "match"
+ *     HFS_BFIND_LOCK: request HFS_LOCK_WRITE access to the node containing
+ *     the "matching" record when it is located
+ *     HFS_BPATH_FIRST: keep access to internal nodes when accessing their
+ *      first child.
+ *     HFS_BPATH_OVERFLOW: keep access to internal nodes when the accessed
+ *      child is too full to insert another pointer record.
+ *     HFS_BPATH_UNDERFLOW: keep access to internal nodes when the accessed
+ *      child is would be less than half full upon removing a pointer record.
+ * Input Variable(s):
+ *   struct hfs_brec *brec: pointer to the (struct hfs_brec) to hold
+ *    the search results.
+ *   struct hfs_bkey *target: pointer to the (struct hfs_bkey)
+ *    to search for
+ *   int flags: bitwise OR of flags which determine the function's behavior
+ * Output Variable(s):
+ *   'brec' contains the results of the search on success or is invalid
+ *    on failure.
+ * Returns:
+ *   int: 0 or 1 on success or an error code on failure:
+ *     -EINVAL: one of the input variables was NULL.
+ *     -ENOENT: tree is valid but empty or no "matching" record was located.
+ *      If the HFS_BFIND_EXACT bit of 'flags' is not set then the case of no
+ *      matching record will give a 'brec' with a 'record' field of zero
+ *      rather than returning this error.
+ *     -EIO: an I/O operation or an assertion about the structure of a
+ *       valid B-tree failed indicating corruption of either the B-tree
+ *       structure on the disk or one of the in-core structures representing
+ *       the B-tree.
+ *      (This could also be returned if a kmalloc() call failed in a
+ *      subordinate routine that is intended to get the data from the
+ *      disk or the buffer cache.)
+ * Preconditions:
+ *   'brec' is NULL or points to a (struct hfs_brec) with a 'tree' field
+ *    which points to a valid (struct hfs_btree).
+ *   'target' is NULL or points to a "valid" (struct hfs_bkey)
+ * Postconditions:
+ *   If 'brec', 'brec->tree' or 'target' is NULL then -EINVAL is returned.
+ *   If 'brec', 'brec->tree' and 'target' are non-NULL but the tree
+ *   is empty then -ENOENT is returned.
+ *   If 'brec', 'brec->tree' and 'target' are non-NULL but the call to
+ *   hfs_brec_init() fails then '*brec' is NULL and -EIO is returned.
+ *   If 'brec', 'brec->tree' and 'target' are non-NULL and the tree is
+ *   non-empty then the tree is searched as follows:
+ *    If any call to hfs_brec_next() fails or returns a node that is
+ *     neither an index node nor a leaf node then -EIO is returned to
+ *     indicate that the B-tree or buffer-cache are corrupted.
+ *    If every record in the tree is "greater than" the given key
+ *     and the HFS_BFIND_EXACT bit of 'flags' is set then -ENOENT is returned.
+ *    If every record in the tree is "greater than" the given key
+ *     and the HFS_BFIND_EXACT bit of 'flags' is clear then 'brec' refers
+ *     to the first leaf node in the tree and has a 'record' field of
+ *     zero, and 1 is returned.
+ *    If a "matching" record is located with key "equal to" 'target'
+ *     then the return value is 0 and 'brec' indicates the record.
+ *    If a "matching" record is located with key "greater than" 'target'
+ *     then the behavior is determined as follows:
+ *     If the HFS_BFIND_EXACT bit of 'flags' is not set then 1 is returned
+ *       and 'brec' refers to the "matching" record.
+ *     If the HFS_BFIND_EXACT bit of 'flags' is set then -ENOENT is returned.
+ *    If the return value is non-negative and the HFS_BFIND_LOCK bit of
+ *     'flags' is set then hfs_brec_lock() is called on the bottom element
+ *     of 'brec' before returning.
+ */
+int hfs_bfind(struct hfs_brec *brec, struct hfs_btree *tree,
+             const struct hfs_bkey *target, int flags)
+{
+       struct hfs_belem *curr;
+       struct hfs_bkey *key;
+       struct hfs_bnode *bn;
+       int result, ntype;
+
+       /* check for invalid arguments */
+       if (!brec || (tree->magic != HFS_BTREE_MAGIC) || !target) {
+               return -EINVAL;
+       }
+
+       /* check for empty tree */
+       if (!tree->root || !tree->bthNRecs) {
+               return -ENOENT;
+       }
+
+       /* start search at root of tree */
+       if (!(curr = hfs_brec_init(brec, tree, flags))) {
+               return -EIO;
+       }
+
+       /* traverse the tree */
+       do {
+               bn = curr->bnr.bn;
+
+               if (!curr->record) {
+                       hfs_warn("hfs_bfind: empty bnode\n");
+                       hfs_brec_relse(brec, NULL);
+                       return -EIO;
+               }
+
+               /* reverse linear search yielding largest key "less
+                  than or equal to" 'target'.
+                  It is questionable whether a binary search would be
+                  significantly faster */
+               do {
+                       key = belem_key(curr);
+                       if (!key->KeyLen) {
+                               hfs_warn("hfs_bfind: empty key\n");
+                               hfs_brec_relse(brec, NULL);
+                               return -EIO;
+                       }
+                       result = (tree->compare)(target, key);
+               } while ((result<0) && (--curr->record));
+
+               ntype = bn->ndType;
+
+               /* see if all keys > target */
+               if (!curr->record) {
+                       if (bn->ndBLink) {
+                               /* at a node other than the left-most at a
+                                  given level it means the parent had an
+                                  incorrect key for this child */
+                               hfs_brec_relse(brec, NULL);
+                               hfs_warn("hfs_bfind: corrupted b-tree %d.\n",
+                                        (int)ntohl(tree->entry.cnid));
+                               return -EIO;
+                       }
+                       if (flags & HFS_BFIND_EXACT) {
+                               /* we're not going to find it */
+                               hfs_brec_relse(brec, NULL);
+                               return -ENOENT;
+                       }
+                       if (ntype == ndIndxNode) {
+                               /* since we are at the left-most node at
+                                  the current level and looking for the
+                                  predecessor of 'target' keep going down */
+                               curr->record = 1;
+                       } else {
+                               /* we're at first leaf so fall through */
+                       }
+               }
+
+               /* get next node if necessary */
+               if ((ntype == ndIndxNode) && !(curr = hfs_brec_next(brec))) {
+                       return -EIO;
+               }
+       } while (ntype == ndIndxNode);
+
+       if (key->KeyLen > tree->bthKeyLen) {
+               hfs_warn("hfs_bfind: oversized key\n");
+               hfs_brec_relse(brec, NULL);
+               return -EIO;
+       }
+
+       if (ntype != ndLeafNode) {
+               hfs_warn("hfs_bfind: invalid node type %02x in node %d of "
+                        "btree %d\n", bn->ndType, bn->node,
+                        (int)ntohl(tree->entry.cnid));
+               hfs_brec_relse(brec, NULL);
+               return -EIO;
+       }
+
+       if ((flags & HFS_BFIND_EXACT) && result) {
+               hfs_brec_relse(brec, NULL);
+               return -ENOENT;
+       }
+
+       if (!(flags & HFS_BPATH_MASK)) {
+               hfs_brec_relse(brec, brec->bottom-1);
+       }
+
+       if (flags & HFS_BFIND_LOCK) {
+               hfs_brec_lock(brec, brec->bottom);
+       }
+
+       brec->key  = brec_key(brec);
+       brec->data = bkey_record(brec->key);
+
+       return result ? 1 : 0;
+}
+
+/*
+ * hfs_bsucc()
+ *
+ * Description:
+ *   This function overwrites '*brec' with its successor in the B-tree,
+ *   obtaining the same type of access.
+ * Input Variable(s):
+ *   struct hfs_brec *brec: address of the (struct hfs_brec) to overwrite
+ *    with its successor
+ * Output Variable(s):
+ *   struct hfs_brec *brec: address of the successor of the original
+ *    '*brec' or to invalid data
+ * Returns:
+ *   int: 0 on success, or one of -EINVAL, -EIO, or -EINVAL on failure
+ * Preconditions:
+ *   'brec' pointers to a "valid" (struct hfs_brec)
+ * Postconditions:
+ *   If the given '*brec' is not "valid" -EINVAL is returned and
+ *    '*brec' is unchanged.
+ *   If the given 'brec' is "valid" but has no successor then -ENOENT
+ *    is returned and '*brec' is invalid.
+ *   If a call to hfs_bnode_find() is necessary to find the successor,
+ *    but fails then -EIO is returned and '*brec' is invalid.
+ *   If none of the three previous conditions prevents finding the
+ *    successor of '*brec', then 0 is returned, and '*brec' is overwritten
+ *    with the (struct hfs_brec) for its successor.
+ *   In the cases when '*brec' is invalid, the old records is freed.
+ */
+int hfs_bsucc(struct hfs_brec *brec, int count)
+{
+       struct hfs_belem *belem;
+       struct hfs_bnode *bn;
+
+       if (!brec || !(belem = brec->bottom) || (belem != brec->top) ||
+           !(bn = belem->bnr.bn) || (bn->magic != HFS_BNODE_MAGIC) ||
+           !bn->tree || (bn->tree->magic != HFS_BTREE_MAGIC) ||
+           !hfs_buffer_ok(bn->buf)) {
+               hfs_warn("hfs_bsucc: invalid/corrupt arguments.\n");
+               return -EINVAL;
+       }
+
+       while (count) {
+               int left = bn->ndNRecs - belem->record;
+
+               if (left < count) {
+                       struct hfs_bnode_ref old;
+                       hfs_u32 node;
+
+                       /* Advance to next node */
+                       if (!(node = bn->ndFLink)) {
+                               hfs_brec_relse(brec, belem);
+                               return -ENOENT;
+                       }
+                       if (node == bn->node) {
+                               hfs_warn("hfs_bsucc: corrupt btree\n");
+                               hfs_brec_relse(brec, belem);
+                               return -EIO;
+                       }
+                       old = belem->bnr;
+                       belem->bnr = hfs_bnode_find(brec->tree, node,
+                                                   belem->bnr.lock_type);
+                       hfs_bnode_relse(&old);
+                       if (!(bn = belem->bnr.bn)) {
+                               return -EIO;
+                       }
+                       belem->record = 1;
+                       count -= (left + 1);
+               } else {
+                       belem->record += count;
+                       break;
+               }
+       }
+       brec->key  = belem_key(belem);
+       brec->data = bkey_record(brec->key);
+
+       if (brec->key->KeyLen > brec->tree->bthKeyLen) {
+               hfs_warn("hfs_bsucc: oversized key\n");
+               hfs_brec_relse(brec, NULL);
+               return -EIO;
+       }
+
+       return 0;
+}
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/fs/hfs/bins_del.c linux-2.0.29/fs/hfs/bins_del.c
--- linux.vanilla/fs/hfs/bins_del.c     Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/fs/hfs/bins_del.c      Thu Apr 10 12:23:26 1997
@@ -0,0 +1,231 @@
+/*
+ * linux/fs/hfs/bins_del.c
+ *
+ * Copyright (C) 1995-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the code common to inserting and deleting records
+ * in a B-tree.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include <linux/hfs_btree.h>
+
+/*================ File-local functions ================*/
+
+/*
+ * hfs_bnode_update_key()
+ *
+ * Description:
+ *   Updates the key for a bnode in its parent.
+ *   The key change is propagated up the tree as necessary.
+ * Input Variable(s):
+ *   struct hfs_brec *brec: the search path to update keys in
+ *   struct hfs_belem *belem: the search path element with the changed key
+ *   struct hfs_bnode *bnode: the bnode with the changed key
+ *   int offset: the "distance" from 'belem->bn' to 'bnode':
+ *    0 if the change is in 'belem->bn',
+ *    1 if the change is in its right sibling, etc.
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   void
+ * Preconditions:
+ *   'brec' points to a valid (struct hfs_brec)
+ *   'belem' points to a valid (struct hfs_belem) in 'brec'.
+ *   'bnode' points to a valid (struct hfs_bnode) which is non-empty
+ *    and is 'belem->bn' or one of its siblings.
+ *   'offset' is as described above.
+ * Postconditions:
+ *   The key change is propagated up the tree as necessary.
+ */
+void hfs_bnode_update_key(struct hfs_brec *brec, struct hfs_belem *belem,
+                         struct hfs_bnode *bnode, int offset)
+{
+       int record = (--belem)->record + offset;
+       void *key = bnode_datastart(bnode) + 1;
+       int keysize = brec->tree->bthKeyLen;
+       struct hfs_belem *limit;
+
+       memcpy(1+bnode_key(belem->bnr.bn, record), key, keysize);
+
+       /* don't trash the header */
+       if (brec->top > &brec->elem[1]) {
+               limit = brec->top;
+       } else {
+               limit = &brec->elem[1];
+       }
+
+       while ((belem > limit) && (record == 1)) {
+               record = (--belem)->record;
+               memcpy(1+belem_key(belem), key, keysize);
+       }
+}
+
+/*
+ * hfs_bnode_shift_right()
+ *
+ * Description:
+ *   Shifts some records from a node to its right neighbor.
+ * Input Variable(s):
+ *   struct hfs_bnode* left: the node to shift records from
+ *   struct hfs_bnode* right: the node to shift records to
+ *   hfs_u16 first: the number of the first record in 'left' to move to 'right'
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   void
+ * Preconditions:
+ *   'left' and 'right' point to valid (struct hfs_bnode)s.
+ *   'left' contains at least 'first' records.
+ *   'right' has enough free space to hold the records to be moved from 'left'
+ * Postconditions:
+ *   The record numbered 'first' and all records after it in 'left' are
+ *   placed at the beginning of 'right'.
+ *   The key corresponding to 'right' in its parent is NOT updated.
+ */
+void hfs_bnode_shift_right(struct hfs_bnode *left, struct hfs_bnode *right,
+                          int first)
+{
+       int i, adjust, nrecs;
+       unsigned size;
+       hfs_u16 *to, *from;
+
+       if ((first <= 0) || (first > left->ndNRecs)) {
+               hfs_warn("bad argument to shift_right: first=%d, nrecs=%d\n",
+                      first, left->ndNRecs);
+               return;
+       }
+
+       /* initialize variables */
+       nrecs = left->ndNRecs + 1 - first;
+       size = bnode_end(left) - bnode_offset(left, first);
+
+       /* move (possibly empty) contents of right node forward */
+       memmove(bnode_datastart(right) + size,
+               bnode_datastart(right),
+               bnode_end(right) - sizeof(struct NodeDescriptor));
+
+       /* copy in new records */
+       memcpy(bnode_datastart(right), bnode_key(left,first), size);
+
+       /* fix up offsets in right node */
+       i = right->ndNRecs + 1;
+       from = RECTBL(right, i);
+       to = from - nrecs;
+       while (i--) {
+               hfs_put_hs(hfs_get_hs(from++) + size, to++);
+       }
+       adjust = sizeof(struct NodeDescriptor) - bnode_offset(left, first);
+       i = nrecs-1;
+       from = RECTBL(left, first+i);
+       while (i--) {
+               hfs_put_hs(hfs_get_hs(from++) + adjust, to++);
+       }
+
+       /* fix record counts */
+       left->ndNRecs -= nrecs;
+       right->ndNRecs += nrecs;
+}
+
+/*
+ * hfs_bnode_shift_left()
+ *
+ * Description:
+ *   Shifts some records from a node to its left neighbor.
+ * Input Variable(s):
+ *   struct hfs_bnode* left: the node to shift records to
+ *   struct hfs_bnode* right: the node to shift records from
+ *   hfs_u16 last: the number of the last record in 'right' to move to 'left'
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   void
+ * Preconditions:
+ *   'left' and 'right' point to valid (struct hfs_bnode)s.
+ *   'right' contains at least 'last' records.
+ *   'left' has enough free space to hold the records to be moved from 'right'
+ * Postconditions:
+ *   The record numbered 'last' and all records before it in 'right' are
+ *   placed at the end of 'left'.
+ *   The key corresponding to 'right' in its parent is NOT updated.
+ */
+void hfs_bnode_shift_left(struct hfs_bnode *left, struct hfs_bnode *right,
+                         int last)
+{
+       int i, adjust, nrecs;
+       unsigned size;
+       hfs_u16 *to, *from;
+
+       if ((last <= 0) || (last > right->ndNRecs)) {
+               hfs_warn("bad argument to shift_left: last=%d, nrecs=%d\n",
+                      last, right->ndNRecs);
+               return;
+       }
+
+       /* initialize variables */
+       size = bnode_offset(right, last + 1) - sizeof(struct NodeDescriptor);
+
+       /* copy records to left node */
+       memcpy(bnode_dataend(left), bnode_datastart(right), size);
+
+       /* move (possibly empty) remainder of right node backward */
+       memmove(bnode_datastart(right), bnode_datastart(right) + size,
+                       bnode_end(right) - bnode_offset(right, last + 1));
+
+       /* fix up offsets */
+       nrecs = left->ndNRecs;
+       i = last;
+       from = RECTBL(right, 2);
+       to = RECTBL(left, nrecs + 2);
+       adjust = bnode_offset(left, nrecs + 1) - sizeof(struct NodeDescriptor);
+       while (i--) {
+               hfs_put_hs(hfs_get_hs(from--) + adjust, to--);
+       }
+       i = right->ndNRecs + 1 - last;
+       ++from;
+       to = RECTBL(right, 1);
+       while (i--) {
+               hfs_put_hs(hfs_get_hs(from--) - size, to--);
+       }
+
+       /* fix record counts */
+       left->ndNRecs += last;
+       right->ndNRecs -= last;
+}
+
+/*
+ * hfs_bnode_in_brec()
+ *
+ * Description:
+ *   Determines whethet a given bnode is part of a given brec.
+ *   This is used to avoid deadlock in the case of a corrupted b-tree.
+ * Input Variable(s):
+ *   hfs_u32 node: the number of the node to check for.
+ *   struct hfs_brec* brec: the brec to check in.
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   int: 1 it found, 0 if not
+ * Preconditions:
+ *   'brec' points to a valid struct hfs_brec.
+ * Postconditions:
+ *   'brec' is unchanged.
+ */
+int hfs_bnode_in_brec(hfs_u32 node, const struct hfs_brec *brec)
+{
+       const struct hfs_belem *belem = brec->bottom;
+
+       while (belem && (belem >= brec->top)) {
+               if (belem->bnr.bn && (belem->bnr.bn->node == node)) {
+                       return 1;
+               }
+               --belem;
+       }
+       return 0;
+}
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/fs/hfs/binsert.c linux-2.0.29/fs/hfs/binsert.c
--- linux.vanilla/fs/hfs/binsert.c      Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/fs/hfs/binsert.c       Thu Apr 10 12:23:41 1997
@@ -0,0 +1,541 @@
+/*
+ * linux/fs/hfs/binsert.c
+ *
+ * Copyright (C) 1995-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the code to insert records in a B-tree.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include <linux/hfs_btree.h>
+
+/*================ File-local functions ================*/
+
+/*
+ * binsert_nonfull()
+ *
+ * Description:
+ *   Inserts a record in a given bnode known to have sufficient space.
+ * Input Variable(s):
+ *   struct hfs_brec* brec: pointer to the brec for the insertion
+ *   struct hfs_belem* belem: the element in the search path to insert in
+ *   struct hfs_bkey* key: pointer to the key for the record to insert
+ *   void* data: pointer to the record to insert
+ *   hfs_u16 keysize: size of the key to insert
+ *   hfs_u16 datasize: size of the record to insert
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   NONE
+ * Preconditions:
+ *   'brec' points to a valid (struct hfs_brec).
+ *   'belem' points to a valid (struct hfs_belem) in 'brec', the node
+ *    of which has enough free space to insert 'key' and 'data'.
+ *   'key' is a pointer to a valid (struct hfs_bkey) of length 'keysize'
+ *    which, in sorted order, belongs at the location indicated by 'brec'.
+ *   'data' is non-NULL an points to appropriate data of length 'datasize'
+ * Postconditions:
+ *   The record has been inserted in the position indicated by 'brec'.
+ */
+static void binsert_nonfull(struct hfs_brec *brec, struct hfs_belem *belem,
+                           const struct hfs_bkey *key, const void *data,
+                           hfs_u8 keysize, hfs_u16 datasize)
+{
+       int i, rec, nrecs, size, tomove;
+       hfs_u8 *start;
+       struct hfs_bnode *bnode = belem->bnr.bn;
+
+       rec = ++(belem->record);
+       size = ROUND(keysize+1) + datasize;
+       nrecs = bnode->ndNRecs + 1;
+       tomove = bnode_offset(bnode, nrecs) - bnode_offset(bnode, rec);
+
+       /* adjust the record table */
+       for (i = nrecs; i >= rec; --i) {
+               hfs_put_hs(bnode_offset(bnode,i) + size, RECTBL(bnode,i+1));
+       }
+
+       /* make room */
+       start = bnode_key(bnode, rec);
+       memmove(start + size, start, tomove);
+
+       /* copy in the key and the data*/
+       *start = keysize;
+       keysize = ROUND(keysize+1);
+       memcpy(start + 1, (hfs_u8 *)key + 1, keysize-1);
+       memcpy(start + keysize, data, datasize);
+
+       /* update record count */
+       ++bnode->ndNRecs;
+}
+
+/*
+ * add_root()
+ *
+ * Description:
+ *   Adds a new root to a B*-tree, increasing its height.
+ * Input Variable(s):
+ *   struct hfs_btree *tree: the tree to add a new root to
+ *   struct hfs_bnode *left: the new root's first child or NULL
+ *   struct hfs_bnode *right: the new root's second child or NULL
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   void
+ * Preconditions:
+ *   'tree' points to a valid (struct hfs_btree).
+ *   'left' and 'right' point to valid (struct hfs_bnode)s, which
+ *    resulted from splitting the old root node, or are both NULL
+ *    if there was no root node before.
+ * Postconditions:
+ *   Upon success a new root node is added to 'tree' with either
+ *    two children ('left' and 'right') or none.
+ */
+static void add_root(struct hfs_btree *tree,
+                    struct hfs_bnode *left,
+                    struct hfs_bnode *right)
+{
+       struct hfs_bnode_ref bnr;
+       struct hfs_bnode *root;
+       struct hfs_bkey *key;
+       int keylen = tree->bthKeyLen;
+
+       if (left && !right) {
+               hfs_warn("add_root: LEFT but no RIGHT\n");
+               return;
+       }
+
+       bnr = hfs_bnode_alloc(tree);
+       if (!(root = bnr.bn)) {
+               return;
+       }
+
+       root->sticky = HFS_STICKY;
+       tree->root = root;
+       tree->bthRoot = root->node;
+       ++tree->bthDepth;
+
+       root->ndNHeight = tree->bthDepth;
+       root->ndFLink = 0;
+       root->ndBLink = 0;
+
+       if (!left) {
+               /* tree was empty */
+               root->ndType  = ndLeafNode;
+               root->ndNRecs = 0;
+
+               tree->bthFNode = root->node;
+               tree->bthLNode = root->node;
+       } else {
+               root->ndType  = ndIndxNode;
+               root->ndNRecs = 2;
+
+               hfs_put_hs(sizeof(struct NodeDescriptor) + ROUND(1+keylen) +
+                          sizeof(hfs_u32), RECTBL(root, 2));
+               key = bnode_key(root, 1);
+               key->KeyLen = keylen;
+               memcpy(key->value,
+                      ((struct hfs_bkey *)bnode_key(left, 1))->value, keylen);
+               hfs_put_hl(left->node, bkey_record(key));
+
+               hfs_put_hs(sizeof(struct NodeDescriptor) + 2*ROUND(1+keylen) +
+                          2*sizeof(hfs_u32), RECTBL(root, 3));
+               key = bnode_key(root, 2);
+               key->KeyLen = keylen;
+               memcpy(key->value,
+                      ((struct hfs_bkey *)bnode_key(right, 1))->value, keylen);
+               hfs_put_hl(right->node, bkey_record(key));
+
+               /* the former root (left) is now just a normal node */
+               left->sticky = HFS_NOT_STICKY;
+               if ((left->next = bhash(tree, left->node))) {
+                       left->next->prev = left;
+               }
+               bhash(tree, left->node) = left;
+       }
+       hfs_bnode_relse(&bnr);
+       tree->dirt = 1;
+}
+
+/*
+ * insert_empty_bnode()
+ *
+ * Description:
+ *   Adds an empty node to the right of 'left'.
+ * Input Variable(s):
+ *   struct hfs_btree *tree: the tree to add a node to
+ *   struct hfs_bnode *left: the node to add a node after
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   struct hfs_bnode_ref *: reference to the new bnode.
+ * Preconditions:
+ *   'tree' points to a valid (struct hfs_btree) with at least 1 free node.
+ *   'left' points to a valid (struct hfs_bnode) belonging to 'tree'.
+ * Postconditions:
+ *   If NULL is returned then 'tree' and 'left' are unchanged.
+ *   Otherwise a node with 0 records is inserted in the tree to the right
+ *   of the node 'left'.  The 'ndFLink' of 'left' and the 'ndBLink' of
+ *   the former right-neighbor of 'left' (if one existed) point to the
+ *   new node. If 'left' had no right neighbor and is a leaf node the
+ *   the 'bthLNode' of 'tree' points to the new node.  The free-count and
+ *   bitmap for 'tree' are kept current by hfs_bnode_alloc() which supplies
+ *   the required node.
+ */
+static struct hfs_bnode_ref insert_empty_bnode(struct hfs_btree *tree,
+                                              struct hfs_bnode *left)
+{
+       struct hfs_bnode_ref retval;
+       struct hfs_bnode_ref right;
+
+       retval = hfs_bnode_alloc(tree);
+       if (!retval.bn) {
+               hfs_warn("hfs_binsert: out of bnodes?.\n");
+               goto done;
+       }
+       retval.bn->sticky = HFS_NOT_STICKY;
+       if ((retval.bn->next = bhash(tree, retval.bn->node))) {
+               retval.bn->next->prev = retval.bn;
+       }
+       bhash(tree, retval.bn->node) = retval.bn;
+
+       if (left->ndFLink) {
+               right = hfs_bnode_find(tree, left->ndFLink, HFS_LOCK_WRITE);
+               if (!right.bn) {
+                       hfs_warn("hfs_binsert: corrupt btree.\n");
+                       hfs_bnode_bitop(tree, retval.bn->node, 0);
+                       hfs_bnode_relse(&retval);
+                       goto done;
+               }
+               right.bn->ndBLink = retval.bn->node;
+               hfs_bnode_relse(&right);
+       } else if (left->ndType == ndLeafNode) {
+               tree->bthLNode = retval.bn->node;
+               tree->dirt = 1;
+       }
+
+       retval.bn->ndFLink   = left->ndFLink;
+       retval.bn->ndBLink   = left->node;
+       retval.bn->ndType    = left->ndType;
+       retval.bn->ndNHeight = left->ndNHeight;
+       retval.bn->ndNRecs   = 0;
+
+       left->ndFLink = retval.bn->node;
+
+ done:
+       return retval;
+}
+
+/*
+ * split()
+ *
+ * Description:
+ *   Splits an over full node during insertion.
+ *   Picks the split point that results in the most-nearly equal
+ *   space usage in the new and old nodes.
+ * Input Variable(s):
+ *   struct hfs_belem *elem: the over full node.
+ *   int size: the number of bytes to be used by the new record and its key.
+ * Output Variable(s):
+ *   struct hfs_belem *elem: changed to indicate where the new record
+ *    should be inserted.
+ * Returns:
+ *   struct hfs_bnode_ref: reference to the new bnode.
+ * Preconditions:
+ *   'elem' points to a valid path element corresponding to the over full node.
+ *   'size' is positive.
+ * Postconditions:
+ *   The records in the node corresponding to 'elem' are redistributed across
+ *   the old and new nodes so that after inserting the new record, the space
+ *   usage in these two nodes is as equal as possible.
+ *   'elem' is updated so that a call to binsert_nonfull() will insert the
+ *   new record in the correct location.
+ */
+static inline struct hfs_bnode_ref split(struct hfs_belem *elem, int size)
+{
+       struct hfs_bnode *bnode = elem->bnr.bn;
+       int nrecs, cutoff, index, tmp, used, in_right;
+       struct hfs_bnode_ref right;
+
+       right = insert_empty_bnode(bnode->tree, bnode);
+       if (right.bn) {
+               nrecs = bnode->ndNRecs;
+               cutoff = (size + bnode_end(bnode) -
+                             sizeof(struct NodeDescriptor) +
+                             (nrecs+1)*sizeof(hfs_u16))/2;
+               used = 0;
+               in_right = 1;
+               /* note that this only works because records sizes are even */
+               for (index=1; index <= elem->record; ++index) {
+                       tmp = (sizeof(hfs_u16) + bnode_rsize(bnode, index))/2;
+                       used += tmp;
+                       if (used > cutoff) {
+                               goto found;
+                       }
+                       used += tmp;
+               }
+               tmp = (size + sizeof(hfs_u16))/2;
+               used += tmp;
+               if (used > cutoff) {
+                       goto found;
+               }
+               in_right = 0;
+               used += tmp;
+               for (; index <= nrecs; ++index) {
+                       tmp = (sizeof(hfs_u16) + bnode_rsize(bnode, index))/2;
+                       used += tmp;
+                       if (used > cutoff) {
+                               goto found;
+                       }
+                       used += tmp;
+               }
+               /* couldn't find the split point! */
+               hfs_bnode_relse(&right);
+       }
+       return right;
+
+found:
+       if (in_right) {
+               elem->bnr = right;
+               elem->record -= index-1;
+       }
+       hfs_bnode_shift_right(bnode, right.bn, index);
+
+       return right;
+}
+
+/*
+ * binsert()
+ *
+ * Description:
+ *   Inserts a record in a tree known to have enough room, even if the
+ *   insertion requires the splitting of nodes.
+ * Input Variable(s):
+ *    struct hfs_brec *brec: partial path to the node to insert in
+ *    const struct hfs_bkey *key: key for the new record
+ *    const void *data: data for the new record
+ *    hfs_u8 keysize: size of the key
+ *    hfs_u16 datasize: size of the data
+ *    int reserve: number of nodes reserved in case of splits
+ * Output Variable(s):
+ *    *brec = NULL
+ * Returns:
+ *    int: 0 on success, error code on failure
+ * Preconditions:
+ *    'brec' points to a valid (struct hfs_brec) corresponding to a
+ *     record in a leaf node, after which a record is to be inserted,
+ *     or to "record 0" of the leaf node if the record is to be inserted
+ *     before all existing records in the node.         The (struct hfs_brec)
+ *     includes all ancestors of the leaf node that are needed to
+ *     complete the insertion including the parents of any nodes that
+ *     will be split.
+ *    'key' points to a valid (struct hfs_bkey) which is appropriate
+ *     to this tree, and which belongs at the insertion point.
+ *    'data' points data appropriate for the indicated node.
+ *    'keysize' gives the size in bytes of the key.
+ *    'datasize' gives the size in bytes of the data.
+ *    'reserve' gives the number of nodes that have been reserved in the
+ *     tree to allow for splitting of nodes.
+ * Postconditions:
+ *    All 'reserve'd nodes have been either used or released.
+ *    *brec = NULL
+ *    On success the key and data have been inserted at the indicated
+ *    location in the tree, all appropriate fields of the in-core data
+ *    structures have been changed and updated versions of the on-disk
+ *    data structures have been scheduled for write-back to disk.
+ *    On failure the B*-tree is probably invalid both on disk and in-core.
+ *
+ *    XXX: Some attempt at repair might be made in the event of failure,
+ *    or the fs should be remounted read-only so things don't get worse.
+ */
+static int binsert(struct hfs_brec *brec, const struct hfs_bkey *key,
+                  const void *data, hfs_u8 keysize, hfs_u16 datasize,
+                  int reserve)
+{
+       struct hfs_bnode_ref left, right, other;
+       struct hfs_btree *tree = brec->tree;
+       struct hfs_belem *belem = brec->bottom;
+       int tmpsize = 1 + tree->bthKeyLen;
+       struct hfs_bkey *tmpkey = hfs_malloc(tmpsize);
+       hfs_u32 node;
+
+       while ((belem >= brec->top) && (belem->flags & HFS_BPATH_OVERFLOW)) {
+               left = belem->bnr;
+               if (left.bn->ndFLink &&
+                    hfs_bnode_in_brec(left.bn->ndFLink, brec)) {
+                       hfs_warn("hfs_binsert: corrupt btree\n");
+                       tree->reserved -= reserve;
+                       hfs_free(tmpkey, tmpsize);
+                       return -EIO;
+               }
+
+               right = split(belem, ROUND(keysize+1) + ROUND(datasize));
+               --reserve;
+               --tree->reserved;
+               if (!right.bn) {
+                       hfs_warn("hfs_binsert: unable to split node!\n");
+                       tree->reserved -= reserve;
+                       hfs_free(tmpkey, tmpsize);
+                       return -ENOSPC;
+               }
+               binsert_nonfull(brec, belem, key, data, keysize, datasize);
+
+               if (belem->bnr.bn == left.bn) {
+                       other = right;
+                       if (belem->record == 1) {
+                               hfs_bnode_update_key(brec, belem, left.bn, 0);
+                       }
+               } else {
+                       other = left;
+               }
+
+               if (left.bn->node == tree->root->node) {
+                       add_root(tree, left.bn, right.bn);
+                       hfs_bnode_relse(&other);
+                       goto done;
+               }
+
+               data = &node;
+               datasize = sizeof(node);
+               node = htonl(right.bn->node);
+               key = tmpkey;
+               keysize = tree->bthKeyLen;
+               memcpy(tmpkey, bnode_key(right.bn, 1), keysize+1);
+               hfs_bnode_relse(&other);
+
+               --belem;
+       }
+
+       if (belem < brec->top) {
+               hfs_warn("hfs_binsert: Missing parent.\n");
+               tree->reserved -= reserve;
+               hfs_free(tmpkey, tmpsize);
+               return -EIO;
+       }
+
+       binsert_nonfull(brec, belem, key, data, keysize, datasize);
+
+done:
+       tree->reserved -= reserve;
+       hfs_free(tmpkey, tmpsize);
+       return 0;
+}
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_binsert()
+ *
+ * Description:
+ *   This function inserts a new record into a b-tree.
+ * Input Variable(s):
+ *   struct hfs_btree *tree: pointer to the (struct hfs_btree) to insert in
+ *   struct hfs_bkey *key: pointer to the (struct hfs_bkey) to insert
+ *   void *data: pointer to the data to associate with 'key' in the b-tree
+ *   unsigned int datasize: the size of the data
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   int: 0 on success, error code on failure
+ * Preconditions:
+ *   'tree' points to a valid (struct hfs_btree)
+ *   'key' points to a valid (struct hfs_bkey)
+ *   'data' points to valid memory of length 'datasize'
+ * Postconditions:
+ *   If zero is returned then the record has been inserted in the
+ *    indicated location updating all in-core data structures and
+ *    scheduling all on-disk data structures for write-back.
+ */
+int hfs_binsert(struct hfs_btree *tree, const struct hfs_bkey *key,
+               const void *data, hfs_u16 datasize)
+{
+       struct hfs_brec brec;
+       struct hfs_belem *belem;
+       int err, reserve, retval;
+       hfs_u8 keysize;
+
+       if (!tree || (tree->magic != HFS_BTREE_MAGIC) || !key || !data) {
+               hfs_warn("hfs_binsert: invalid arguments.\n");
+               return -EINVAL;
+       }
+
+       if (key->KeyLen > tree->bthKeyLen) {
+               hfs_warn("hfs_binsert: oversized key\n");
+               return -EINVAL;
+       }
+
+restart:
+       if (!tree->bthNRecs) {
+               /* create the root bnode */
+               add_root(tree, NULL, NULL);
+               if (!hfs_brec_init(&brec, tree, HFS_BFIND_INSERT)) {
+                       hfs_warn("hfs_binsert: failed to create root.\n");
+                       return -ENOSPC;
+               }
+       } else {
+               err = hfs_bfind(&brec, tree, key, HFS_BFIND_INSERT);
+               if (err < 0) {
+                       hfs_warn("hfs_binsert: hfs_brec_find failed.\n");
+                       return err;
+               } else if (err == 0) {
+                       hfs_brec_relse(&brec, NULL);
+                       return -EEXIST;
+               }
+       }
+
+       keysize = key->KeyLen;
+       datasize = ROUND(datasize);
+       belem = brec.bottom;
+       belem->flags = 0;
+       if (bnode_freespace(belem->bnr.bn) <
+                           (sizeof(hfs_u16) + ROUND(keysize+1) + datasize)) {
+               belem->flags |= HFS_BPATH_OVERFLOW;
+       }
+       if (belem->record == 0) {
+               belem->flags |= HFS_BPATH_FIRST;
+       }
+
+       if (!belem->flags) {
+               hfs_brec_lock(&brec, brec.bottom);
+               reserve = 0;
+       } else {
+               reserve = brec.bottom - brec.top;
+               if (brec.top == 0) {
+                       ++reserve;
+               }
+               /* make certain we have enough nodes to proceed */
+               if ((tree->bthFree - tree->reserved) < reserve) {
+                       hfs_brec_relse(&brec, NULL);
+                       while (tree->lock) {
+                               hfs_sleep_on(&tree->wait);
+                       }
+                       tree->lock = 1;
+                       if ((tree->bthFree - tree->reserved) < reserve) {
+                               hfs_btree_extend(tree);
+                       }
+                       tree->lock = 0;
+                       hfs_wake_up(&tree->wait);
+                       if ((tree->bthFree - tree->reserved) < reserve) {
+                               return -ENOSPC;
+                       } else {
+                               goto restart;
+                       }
+               }
+               tree->reserved += reserve;
+               hfs_brec_lock(&brec, NULL);
+       }
+
+       retval = binsert(&brec, key, data, keysize, datasize, reserve);
+       hfs_brec_relse(&brec, NULL);
+       if (!retval) {
+               ++tree->bthNRecs;
+               tree->dirt = 1;
+       }
+       return retval;
+}
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/fs/hfs/bitmap.c linux-2.0.29/fs/hfs/bitmap.c
--- linux.vanilla/fs/hfs/bitmap.c       Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/fs/hfs/bitmap.c        Thu Apr 10 12:23:52 1997
@@ -0,0 +1,412 @@
+/*
+ * linux/fs/hfs/bitmap.c
+ *
+ * Copyright (C) 1996-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * Based on GPLed code Copyright (C) 1995  Michael Dreher
+ *
+ * This file contains the code to modify the volume bitmap:
+ * search/set/clear bits.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include <linux/hfs.h>
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_vbm_count_free()
+ *
+ * Description:
+ *   Count the number of consecutive cleared bits in the bitmap blocks of
+ *   the hfs MDB starting at bit number 'start'.  'mdb' had better
+ *   be locked or the indicated number of blocks may be no longer free,
+ *   when this functions returns!
+ * Input Variable(s):
+ *   struct hfs_mdb *mdb: Pointer to the hfs MDB
+ *   hfs_u16 start: bit number to start at
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   The number of consecutive cleared bits starting at bit 'start'
+ * Preconditions:
+ *   'mdb' points to a "valid" (struct hfs_mdb).
+ * Postconditions:
+ *   NONE
+ */
+hfs_u16 hfs_vbm_count_free(const struct hfs_mdb *mdb, hfs_u16 start)
+{
+       hfs_u16 block_nr;       /* index of the current bitmap block */
+       hfs_u16 bit_nr;         /* index of the current bit in block */
+       hfs_u16 count;          /* number of bits found so far */
+       hfs_u16 len;            /* number of bits found in this block */
+       hfs_u16 max_block;      /* index of last bitmap block */
+       hfs_u16 max_bits;       /* index of last bit in block */
+
+       /* is this a valid HFS MDB? */
+       if (!mdb) {
+               return 0;
+       }
+
+       block_nr = start / HFS_BM_BPB;
+       bit_nr   = start % HFS_BM_BPB;
+       max_block = (mdb->fs_ablocks + HFS_BM_BPB - 1) / HFS_BM_BPB - 1;
+
+       count = 0;
+       while (block_nr <= max_block) {
+               if (block_nr != max_block) {
+                       max_bits = HFS_BM_BPB;
+               } else {
+                       max_bits = mdb->fs_ablocks % HFS_BM_BPB;
+               }
+
+               len=hfs_count_zero_bits(hfs_buffer_data(mdb->bitmap[block_nr]),
+                                       max_bits, bit_nr, max_bits);
+               count += len;
+
+               /* see if we fell short of the end of this block */
+               if ((len + bit_nr) < max_bits) {
+                       break;
+               }
+
+               ++block_nr;
+               bit_nr = 0;
+       }
+       return count;
+}
+
+/*
+ * hfs_vbm_search_free()
+ *
+ * Description:
+ *   Search for 'num_bits' consecutive cleared bits in the bitmap blocks of
+ *   the hfs MDB. 'mdb' had better be locked or the returned range
+ *   may be no longer free, when this functions returns!
+ *   XXX Currently the search starts from bit 0, but it should start with
+ *   the bit number stored in 's_alloc_ptr' of the MDB.
+ * Input Variable(s):
+ *   struct hfs_mdb *mdb: Pointer to the hfs MDB
+ *   hfs_u16 *num_bits: Pointer to the number of cleared bits
+ *     to search for
+ * Output Variable(s):
+ *   hfs_u16 *num_bits: The number of consecutive clear bits of the
+ *     returned range. If the bitmap is fragmented, this will be less than
+ *     requested and it will be zero, when the disk is full.
+ * Returns:
+ *   The number of the first bit of the range of cleared bits which has been
+ *   found. When 'num_bits' is zero, this is invalid!
+ * Preconditions:
+ *   'mdb' points to a "valid" (struct hfs_mdb).
+ *   'num_bits' points to a variable of type (hfs_u16), which contains
+ *     the number of cleared bits to find.
+ * Postconditions:
+ *   'num_bits' is set to the length of the found sequence.
+ */
+hfs_u16 hfs_vbm_search_free(const struct hfs_mdb *mdb, hfs_u16 *num_bits)
+{
+       hfs_u16 block_nr; /* index of the current bitmap block */
+
+       /* position and length of current portion of a run */
+       hfs_u16 cur_pos, cur_len;
+
+       /* position and length of current complete run */
+       hfs_u16 pos=0, len=0;
+
+       /* position and length of longest complete run */
+       hfs_u16 longest_pos=0, longest_len=0;
+
+       void *bitmap; /* contents of the current bitmap block */
+       hfs_u16 max_block; /* upper limit of outer loop */
+       hfs_u16 max_bits; /* upper limit of inner loop */
+
+       /* is this a valid HFS MDB? */
+       if (!mdb) {
+               *num_bits = 0;
+               hfs_warn("hfs_vbm_search_free: not a valid MDB\n");
+               return 0;
+       }
+
+       /* make sure we have actual work to perform */
+       if (!(*num_bits)) {
+               return 0;
+       }
+
+       max_block = (mdb->fs_ablocks+HFS_BM_BPB-1) / HFS_BM_BPB - 1;
+
+       /* search all bitmap blocks */
+       for (block_nr = 0; block_nr <= max_block; block_nr++) {
+               bitmap = hfs_buffer_data(mdb->bitmap[block_nr]);
+
+               if (block_nr != max_block) {
+                       max_bits = HFS_BM_BPB;
+               } else {
+                       max_bits = mdb->fs_ablocks % HFS_BM_BPB;
+               }
+
+               cur_pos = 0;
+               do {
+                       cur_len = hfs_count_zero_bits(bitmap, max_bits,
+                                                     cur_pos, *num_bits);
+                       len += cur_len;
+                       if (len > longest_len) {
+                               longest_pos = pos;
+                               longest_len = len;
+                               if (len >= *num_bits) {
+                                       goto search_end;
+                               }
+                       }
+                       if ((cur_pos + cur_len) == max_bits) {
+                               break; /* zeros may continue into next block */
+                       }
+
+                       /* find start of next run of zeros */
+                       cur_pos = hfs_find_next_zero_bit(bitmap, max_bits,
+                                                        cur_pos + cur_len);
+                       pos = cur_pos + HFS_BM_BPB*block_nr;
+                       len = 0;
+               } while (cur_pos < max_bits);
+       }
+
+search_end:
+       *num_bits = longest_len;
+       return longest_pos;
+}
+
+
+/*
+ * hfs_set_vbm_bits()
+ *
+ * Description:
+ *   Set the requested bits in the volume bitmap of the hfs filesystem
+ * Input Variable(s):
+ *   struct hfs_mdb *mdb: Pointer to the hfs MDB
+ *   hfs_u16 start: The offset of the first bit
+ *   hfs_u16 count: The number of bits
+ * Output Variable(s):
+ *   None
+ * Returns:
+ *    0: no error
+ *   -1: One of the bits was already set.  This is a strange
+ *      error and when it happens, the filesystem must be repaired!
+ *   -2: One or more of the bits are out of range of the bitmap.
+ *   -3: The 's_magic' field of the MDB does not match
+ * Preconditions:
+ *   'mdb' points to a "valid" (struct hfs_mdb).
+ * Postconditions:
+ *   Starting with bit number 'start', 'count' bits in the volume bitmap
+ *   are set. The affected bitmap blocks are marked "dirty", the free
+ *   block count of the MDB is updated and the MDB is marked dirty.
+ */
+int hfs_set_vbm_bits(struct hfs_mdb *mdb, hfs_u16 start, hfs_u16 count)
+{
+       hfs_u16 block_nr;       /* index of the current bitmap block */
+       hfs_u16 u32_nr;         /* index of the current hfs_u32 in block */
+       hfs_u16 bit_nr;         /* index of the current bit in hfs_u32 */
+       hfs_u16 left = count;   /* number of bits left to be set */
+       hfs_u32 *bitmap;        /* the current bitmap block's contents */
+
+       /* is this a valid HFS MDB? */
+       if (!mdb) {
+               return -3;
+       }
+
+       /* is there any actual work to be done? */
+       if (!count) {
+               return 0;
+       }
+
+       /* are all of the bits in range? */
+       if ((start + count) > mdb->fs_ablocks) {
+               return -2;
+       }
+
+       block_nr = start / HFS_BM_BPB;
+       u32_nr = (start % HFS_BM_BPB) / 32;
+       bit_nr = start % 32;
+
+       /* bitmap is always on a 32-bit boundary */
+       bitmap = (hfs_u32 *)hfs_buffer_data(mdb->bitmap[block_nr]);
+
+       /* do any partial hfs_u32 at the start */
+       if (bit_nr != 0) {
+               while ((bit_nr < 32) && left) {
+                       if (hfs_set_bit(bit_nr, bitmap + u32_nr)) {
+                               hfs_buffer_dirty(mdb->bitmap[block_nr]);
+                               return -1;
+                       }
+                       ++bit_nr;
+                       --left;
+               }
+               bit_nr=0;
+
+               /* advance u32_nr and check for end of this block */
+               if (++u32_nr > 127) {
+                       u32_nr = 0;
+                       hfs_buffer_dirty(mdb->bitmap[block_nr]);
+                       ++block_nr;
+                       /* bitmap is always on a 32-bit boundary */
+                       bitmap = (hfs_u32 *)
+                                       hfs_buffer_data(mdb->bitmap[block_nr]);
+               }
+       }
+
+       /* do full hfs_u32s */
+       while (left > 31) {
+               if (bitmap[u32_nr] != ((hfs_u32)0)) {
+                       hfs_buffer_dirty(mdb->bitmap[block_nr]);
+                       return -1;
+               }
+               bitmap[u32_nr] = ~((hfs_u32)0);
+               left -= 32;
+
+               /* advance u32_nr and check for end of this block */
+               if (++u32_nr > 127) {
+                       u32_nr = 0;
+                       hfs_buffer_dirty(mdb->bitmap[block_nr]);
+                       ++block_nr;
+                       /* bitmap is always on a 32-bit boundary */
+                       bitmap = (hfs_u32 *)
+                                       hfs_buffer_data(mdb->bitmap[block_nr]);
+               }
+       }
+
+
+       /* do any partial hfs_u32 at end */
+       while (left) {
+               if (hfs_set_bit(bit_nr, bitmap + u32_nr)) {
+                       hfs_buffer_dirty(mdb->bitmap[block_nr]);
+                       return -1;
+               }
+               ++bit_nr;
+               --left;
+       }
+
+       hfs_buffer_dirty(mdb->bitmap[block_nr]);
+       mdb->free_ablocks -= count;
+
+       /* successful completion */
+       hfs_mdb_dirty(mdb->sys_mdb);
+       return 0;
+}
+
+/*
+ * hfs_clear_vbm_bits()
+ *
+ * Description:
+ *   Clear the requested bits in the volume bitmap of the hfs filesystem
+ * Input Variable(s):
+ *   struct hfs_mdb *mdb: Pointer to the hfs MDB
+ *   hfs_u16 start: The offset of the first bit
+ *   hfs_u16 count: The number of bits
+ * Output Variable(s):
+ *   None
+ * Returns:
+ *    0: no error
+ *   -1: One of the bits was already clear.  This is a strange
+ *      error and when it happens, the filesystem must be repaired!
+ *   -2: One or more of the bits are out of range of the bitmap.
+ *   -3: The 's_magic' field of the MDB does not match
+ * Preconditions:
+ *   'mdb' points to a "valid" (struct hfs_mdb).
+ * Postconditions:
+ *   Starting with bit number 'start', 'count' bits in the volume bitmap
+ *   are cleared. The affected bitmap blocks are marked "dirty", the free
+ *   block count of the MDB is updated and the MDB is marked dirty.
+ */
+int hfs_clear_vbm_bits(struct hfs_mdb *mdb, hfs_u16 start, hfs_u16 count)
+{
+       hfs_u16 block_nr;       /* index of the current bitmap block */
+       hfs_u16 u32_nr;         /* index of the current hfs_u32 in block */
+       hfs_u16 bit_nr;         /* index of the current bit in hfs_u32 */
+       hfs_u16 left = count;   /* number of bits left to be set */
+       hfs_u32 *bitmap;        /* the current bitmap block's contents */
+
+       /* is this a valid HFS MDB? */
+       if (!mdb) {
+               return -3;
+       }
+
+       /* is there any actual work to be done? */
+       if (!count) {
+               return 0;
+       }
+
+       /* are all of the bits in range? */
+       if ((start + count) > mdb->fs_ablocks) {
+               return -2;
+       }
+
+       block_nr = start / HFS_BM_BPB;
+       u32_nr = (start % HFS_BM_BPB) / 32;
+       bit_nr = start % 32;
+
+       /* bitmap is always on a 32-bit boundary */
+       bitmap = (hfs_u32 *)hfs_buffer_data(mdb->bitmap[block_nr]);
+
+       /* do any partial hfs_u32 at the start */
+       if (bit_nr != 0) {
+               while ((bit_nr < 32) && left) {
+                       if (!hfs_clear_bit(bit_nr, bitmap + u32_nr)) {
+                               hfs_buffer_dirty(mdb->bitmap[block_nr]);
+                               return -1;
+                       }
+                       ++bit_nr;
+                       --left;
+               }
+               bit_nr=0;
+
+               /* advance u32_nr and check for end of this block */
+               if (++u32_nr > 127) {
+                       u32_nr = 0;
+                       hfs_buffer_dirty(mdb->bitmap[block_nr]);
+                       ++block_nr;
+                       /* bitmap is always on a 32-bit boundary */
+                       bitmap = (hfs_u32 *)
+                                       hfs_buffer_data(mdb->bitmap[block_nr]);
+               }
+       }
+
+       /* do full hfs_u32s */
+       while (left > 31) {
+               if (bitmap[u32_nr] != ~((hfs_u32)0)) {
+                       hfs_buffer_dirty(mdb->bitmap[block_nr]);
+                       return -1;
+               }
+               bitmap[u32_nr] = ((hfs_u32)0);
+               left -= 32;
+
+               /* advance u32_nr and check for end of this block */
+               if (++u32_nr > 127) {
+                       u32_nr = 0;
+                       hfs_buffer_dirty(mdb->bitmap[block_nr]);
+                       ++block_nr;
+                       /* bitmap is always on a 32-bit boundary */
+                       bitmap = (hfs_u32 *)
+                                       hfs_buffer_data(mdb->bitmap[block_nr]);
+               }
+       }
+
+
+       /* do any partial hfs_u32 at end */
+       while (left) {
+               if (!hfs_clear_bit(bit_nr, bitmap + u32_nr)) {
+                       hfs_buffer_dirty(mdb->bitmap[block_nr]);
+                       return -1;
+               }
+               ++bit_nr;
+               --left;
+       }
+
+       hfs_buffer_dirty(mdb->bitmap[block_nr]);
+       mdb->free_ablocks += count;
+
+       /* successful completion */
+       hfs_mdb_dirty(mdb->sys_mdb);
+       return 0;
+}
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/fs/hfs/bitops.c linux-2.0.29/fs/hfs/bitops.c
--- linux.vanilla/fs/hfs/bitops.c       Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/fs/hfs/bitops.c        Thu Apr 10 12:24:04 1997
@@ -0,0 +1,160 @@
+/*
+ * linux/fs/hfs/bitops.c
+ *
+ * Copyright (C) 1996  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains functions to handle bitmaps in "left-to-right"
+ * bit-order such that the MSB of a 32-bit big-endian word is bit 0.
+ * (This corresponds to bit 7 of a 32-bit little-endian word.)
+ *
+ * I have tested and confirmed that the results are identical on the
+ * Intel x86, PowerPC and DEC Alpha processors.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ */
+
+#include <linux/hfs.h>
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_find_first_zero_bit()
+ *
+ * Description:
+ *   Given the address of a block of memory find the first (by
+ *   left-to-right ordering) zero bit in the first 'size' bits.
+ */
+hfs_u32 hfs_find_first_zero_bit(const void * addr, hfs_u32 size)
+{
+       const hfs_u32 *start = (const hfs_u32 *)addr;
+       const hfs_u32 *end   = start + ((size+31) >> 5);
+       const hfs_u32 *curr  = start;
+       int bit = 0;
+
+       while (curr < end) {
+               if (*curr == ~((hfs_u32)0)) {
+                       ++curr;
+               } else {
+                       while (hfs_test_bit(bit, curr)) {
+                              ++bit;
+                       }
+                       break;
+               }
+       }
+       return ((curr-start)<<5) | bit;
+}
+
+/*
+ * hfs_find_next_zero_bit()
+ *
+ * Description:
+ *   Given the address of a block of memory find the next (by
+ *   left-to-right bit-ordering) zero bit in the first 'size' bits.
+ */
+hfs_u32 hfs_find_next_zero_bit(const void * addr, hfs_u32 size, hfs_u32 offset)
+{
+       const hfs_u32 *start = (const hfs_u32 *)addr;
+       const hfs_u32 *end   = start + ((size+31) >> 5);
+       const hfs_u32 *curr  = start + (offset>>5);
+       int bit = offset % 32;
+
+       if (size < offset) {
+               return size;
+       }
+
+       /* finish up first partial hfs_u32 */
+       if (bit != 0) {
+               do {
+                       if (!hfs_test_bit(bit, curr)) {
+                               goto done;
+                       }
+                       ++bit;
+               } while (bit < 32);
+               bit = 0;
+               ++curr;
+       }
+
+       /* skip fully set hfs_u32 */
+       while (*curr == ~((hfs_u32)0)) {
+               if (curr == end) {
+                       goto done;
+               } else {
+                       ++curr;
+               }
+       }
+
+       /* check final partial hfs_u32 */
+       do {
+               if (!hfs_test_bit(bit, curr)) {
+                       goto done;
+               }
+               ++bit;
+       } while (bit<32);
+
+done:
+       return ((curr-start)<<5) | bit;
+}
+
+/*
+ * hfs_count_zero_bits()
+ *
+ * Description:
+ *  Given a block of memory, its length in bits, and a starting bit number,
+ *  determine the number of consecutive zero bits (in left-to-right ordering)
+ *  starting at that location, up to 'max'.
+ */
+hfs_u32 hfs_count_zero_bits(const void *addr, hfs_u32 size,
+                           hfs_u32 offset, hfs_u32 max)
+{
+       const hfs_u32 *curr = (const hfs_u32 *)addr + (offset>>5);
+       const hfs_u32 *end = (const hfs_u32 *)addr + ((size+31)>>5);
+       int bit = offset % 32;
+       hfs_u32 count = 0;
+
+       /* fix up max to avoid running off end */
+       if (max > (size - offset)) {
+               max = size - offset;
+       }
+
+       /* finish up first partial hfs_u32 */
+       if (bit != 0) {
+               do {
+                       if (hfs_test_bit(bit, curr)) {
+                               goto done;
+                       }
+                       ++count;
+                       ++bit;
+               } while (bit<32);
+               ++curr;
+       }
+
+       /* eat up any full hfs_u32s of zeros */
+       if (size > (offset + max)) {
+               size = offset + max;
+       }
+       while (*curr == ((hfs_u32)0)) {
+               if (curr == end) {
+                       goto done;
+               } else {
+                       ++curr;
+                       count += 32;
+               }
+       }
+
+       /* count bits in the last hfs_u32 */
+       bit = 0;
+       do {
+               if (hfs_test_bit(bit, curr)) {
+                       goto done;
+               }
+               ++count;
+               ++bit;
+       } while (bit<32);
+
+done:
+       if (count > max) {
+               count = max;
+       }
+       return count;
+}
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/fs/hfs/bnode.c linux-2.0.29/fs/hfs/bnode.c
--- linux.vanilla/fs/hfs/bnode.c        Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/fs/hfs/bnode.c Thu Apr 10 12:24:22 1997
@@ -0,0 +1,524 @@
+/*
+ * linux/fs/hfs/bnode.c
+ *
+ * Copyright (C) 1995-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the code to access nodes in the B-tree structure.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ *
+ * The code in this file initializes some structures which contain
+ * pointers by calling memset(&foo, 0, sizeof(foo)).
+ * This produces the desired behavior only due to the non-ANSI
+ * assumption that the machine representation of NULL is all zeros.
+ */
+
+#include <linux/hfs_btree.h>
+
+/*================ File-local variables ================*/
+
+/* debugging statistics */
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+int bnode_count = 0;
+#endif
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_bnode_delete()
+ *
+ * Description:
+ *   This function is called to remove a bnode from the cache and
+ *   release its resources.
+ * Input Variable(s):
+ *   struct hfs_bnode *bn: Pointer to the (struct hfs_bnode) to be
+ *   removed from the cache.
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   void
+ * Preconditions:
+ *   'bn' points to a "valid" (struct hfs_bnode).
+ * Postconditions:
+ *   The node 'bn' is removed from the cache, its memory freed and its
+ *   buffer (if any) released.
+ */
+void hfs_bnode_delete(struct hfs_bnode *bn)
+{
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+       --bnode_count;
+#endif
+       /* join neighbors */
+       if (bn->next) {
+               bn->next->prev = bn->prev;
+       }
+       if (bn->prev) {
+               bn->prev->next = bn->next;
+       }
+       /* fix cache slot if necessary */
+       if (bhash(bn->tree, bn->node) == bn) {
+               bhash(bn->tree, bn->node) = bn->next;
+       }
+       /* release resources */
+       hfs_buffer_put(bn->buf); /* safe: checks for NULL argument */
+       HFS_DELETE(bn);
+}
+
+
+/*
+ * hfs_bnode_read()
+ *
+ * Description:
+ *   This function creates a (struct hfs_bnode) and, if appropriate,
+ *   inserts it in the cache.
+ * Input Variable(s):
+ *   struct hfs_bnode *bnode: pointer to the new bnode.
+ *   struct hfs_btree *tree: pointer to the (struct hfs_btree)
+ *    containing the desired node
+ *   hfs_u32 node: the number of the desired node.
+ *   int sticky: the value to assign to the 'sticky' field.
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   (struct hfs_bnode *) pointing to the newly created bnode or NULL.
+ * Preconditions:
+ *   'bnode' points to a "valid" (struct hfs_bnode).
+ *   'tree' points to a "valid" (struct hfs_btree).
+ *   'node' is an existing node number in the B-tree.
+ * Postconditions:
+ *   The following are true of 'bnode' upon return:
+ *    The 'magic' field is set to indicate a valid (struct hfs_bnode).
+ *    The 'sticky', 'tree' and 'node' fields are initialized to the
+ *    values of the of the corresponding arguments.
+ *    If the 'sticky' argument is zero then the fields 'prev' and
+ *    'next' are initialized by inserting the (struct hfs_bnode) in the
+ *    linked list of the appropriate cache slot; otherwise they are
+ *    initialized to NULL.
+ *    The data is read from disk (or buffer cache) and the 'buf' field
+ *    points to the buffer for that data.
+ *    If no other processes tried to access this node while this
+ *    process was waiting on disk I/O (if necessary) then the
+ *    remaining fields are zero ('count', 'resrv', 'lock') or NULL
+ *    ('wqueue', 'rqueue') corresponding to no accesses.
+ *    If there were access attempts during I/O then they were blocked
+ *    until the I/O was complete, and the fields 'count', 'resrv',
+ *    'lock', 'wqueue' and 'rqueue' reflect the results of unblocking
+ *    those processes when the I/O was completed.
+ */
+void hfs_bnode_read(struct hfs_bnode *bnode, struct hfs_btree *tree,
+                   hfs_u32 node, int sticky)
+{
+       struct NodeDescriptor *nd;
+       int block;
+
+       /* Initialize the structure */
+       memset(bnode, 0, sizeof(*bnode));
+       bnode->magic = HFS_BNODE_MAGIC;
+       bnode->tree = tree;
+       bnode->node = node;
+       bnode->sticky = sticky;
+
+       if (sticky == HFS_NOT_STICKY) {
+               /* Insert it in the cache if appropriate */
+               if ((bnode->next = bhash(tree, node))) {
+                       bnode->next->prev = bnode;
+               }
+               bhash(tree, node) = bnode;
+       }
+
+       /* Make the bnode look like it is being
+          modified so other processes will wait for
+          the I/O to complete */
+       bnode->count = bnode->resrv = bnode->lock = 1;
+
+       /* Read in the node, possibly causing a schedule()
+          call.  If the I/O fails then emit a warning.  Each
+          process that was waiting on the bnode (including
+          the current one) will notice the failure and
+          hfs_bnode_relse() the node.  The last hfs_bnode_relse()
+          will call hfs_bnode_delete() and discard the bnode.  */
+
+       block = hfs_extent_map(&tree->entry.u.file.data_fork, node, 0);
+       if (!block) {
+               hfs_warn("hfs_bnode_read: bad node number 0x%08x\n", node);
+       } else if (hfs_buffer_ok(bnode->buf =
+                                hfs_buffer_get(tree->sys_mdb, block, 1))) {
+               /* read in the NodeDescriptor */
+               nd = (struct NodeDescriptor *)hfs_buffer_data(bnode->buf);
+               bnode->ndFLink    = hfs_get_hl(nd->ndFLink);
+               bnode->ndBLink    = hfs_get_hl(nd->ndBLink);
+               bnode->ndType     = nd->ndType;
+               bnode->ndNHeight  = nd->ndNHeight;
+               bnode->ndNRecs    = hfs_get_hs(nd->ndNRecs);
+       }
+
+       /* Undo our fakery with the lock state and
+          hfs_wake_up() anyone who we managed to trick */
+       --bnode->count;
+       bnode->resrv = bnode->lock = 0;
+       hfs_wake_up(&bnode->rqueue);
+}
+
+/*
+ * hfs_bnode_lock()
+ *
+ * Description:
+ *   This function does the locking of a bnode.
+ * Input Variable(s):
+ *   struct hfs_bnode *bn: pointer to the (struct hfs_bnode) to lock
+ *   int lock_type: the type of lock desired
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   void
+ * Preconditions:
+ *   'bn' points to a "valid" (struct hfs_bnode).
+ *   'lock_type' is a valid hfs_lock_t
+ * Postconditions:
+ *   The 'count' field of 'bn' is incremented by one.  If 'lock_type'
+ *   is HFS_LOCK_RESRV the 'resrv' field is also incremented.
+ */
+void hfs_bnode_lock(struct hfs_bnode_ref *bnr, int lock_type)
+{
+       struct hfs_bnode *bn = bnr->bn;
+
+       if ((lock_type == bnr->lock_type) || !bn) {
+               return;
+       }
+
+       if (bnr->lock_type == HFS_LOCK_WRITE) {
+               hfs_bnode_commit(bnr->bn);
+       }
+
+       switch (lock_type) {
+       default:
+               goto bail;
+               break;
+
+       case HFS_LOCK_READ:
+               /* We may not obtain read access if any process is
+                  currently modifying or waiting to modify this node.
+                  If we can't obtain access we wait on the rqueue
+                  wait queue to be woken up by the modifying process
+                  when it relinquishes its lock. */
+               switch (bnr->lock_type) {
+               default:
+                       goto bail;
+                       break;
+
+               case HFS_LOCK_NONE:
+                       while (bn->lock || bn->wqueue) {
+                               hfs_sleep_on(&bn->rqueue);
+                       }
+                       ++bn->count;
+                       break;
+               }
+               break;
+
+       case HFS_LOCK_RESRV:
+               /* We may not obtain a reservation (read access with
+                  an option to write later), if any process currently
+                  holds a reservation on this node.  That includes
+                  any process which is currently modifying this node.
+                  If we can't obtain access, then we wait on the
+                  rqueue wait queue to e woken up by the
+                  reservation-holder when it calls hfs_bnode_relse. */
+               switch (bnr->lock_type) {
+               default:
+                       goto bail;
+                       break;
+
+               case HFS_LOCK_NONE:
+                       while (bn->resrv) {
+                               hfs_sleep_on(&bn->rqueue);
+                       }
+                       bn->resrv = 1;
+                       ++bn->count;
+                       break;
+
+               case HFS_LOCK_WRITE:
+                       bn->lock = 0;
+                       hfs_wake_up(&bn->rqueue);
+                       break;
+               }
+               break;
+
+       case HFS_LOCK_WRITE:
+               switch (bnr->lock_type) {
+               default:
+                       goto bail;
+                       break;
+
+               case HFS_LOCK_NONE:
+                       while (bn->resrv) {
+                               hfs_sleep_on(&bn->rqueue);
+                       }
+                       bn->resrv = 1;
+                       ++bn->count;
+               case HFS_LOCK_RESRV:
+                       while (bn->count > 1) {
+                               hfs_sleep_on(&bn->wqueue);
+                       }
+                       bn->lock = 1;
+                       break;
+               }
+               break;
+
+       case HFS_LOCK_NONE:
+               switch (bnr->lock_type) {
+               default:
+                       goto bail;
+                       break;
+
+               case HFS_LOCK_READ:
+                       /* This process was reading this node.  If
+                          there is now exactly one other process using
+                          the node then hfs_wake_up() a (potentially
+                          nonexistent) waiting process.  Note that I
+                          refer to "a" process since the reservation
+                          system ensures that only one process can
+                          get itself on the wait queue.  */
+                       if (bn->count == 2) {
+                               hfs_wake_up(&bn->wqueue);
+                       }
+                       break;
+
+               case HFS_LOCK_WRITE:
+                       /* This process was modifying this node.
+                          Unlock the node and fall-through to the
+                          HFS_LOCK_RESRV case, since a 'reservation'
+                          is a prerequisite for HFS_LOCK_WRITE.  */
+                       bn->lock = 0;
+               case HFS_LOCK_RESRV:
+                       /* This process had placed a 'reservation' on
+                          this node, indicating an intention to
+                          possibly modify the node.  We can get to
+                          this spot directly (if the 'reservation'
+                          not converted to a HFS_LOCK_WRITE), or by
+                          falling through from the above case if the
+                          reservation was converted.
+                          Since HFS_LOCK_RESRV and HFS_LOCK_WRITE
+                          both block processes that want access
+                          (HFS_LOCK_RESRV blocks other processes that
+                          want reservations but allow HFS_LOCK_READ
+                          accesses, while HFS_LOCK_WRITE must have
+                          exclusive access and thus blocks both
+                          types) we hfs_wake_up() any processes that
+                          might be waiting for access.  If multiple
+                          processes are waiting for a reservation
+                          then the magic of process scheduling will
+                          settle the dispute. */
+                       bn->resrv = 0;
+                       hfs_wake_up(&bn->rqueue);
+                       break;
+               }
+               --bn->count;
+               break;
+       }
+       bnr->lock_type = lock_type;
+       return;
+
+bail:
+       hfs_warn("hfs_bnode_lock: invalid lock change: %d->%d.\n",
+               bnr->lock_type, lock_type);
+       return;
+}
+
+/*
+ * hfs_bnode_relse()
+ *
+ * Description:
+ *   This function is called when a process is done using a bnode.  If
+ *   the proper conditions are met then we call hfs_bnode_delete() to remove
+ *   it from the cache.         If it is not deleted then we update its state
+ *   to reflect one less process using it.
+ * Input Variable(s):
+ *   struct hfs_bnode *bn: pointer to the (struct hfs_bnode) to release.
+ *   int lock_type: The type of lock held by the process releasing this node.
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   void
+ * Preconditions:
+ *   'bn' is NULL or points to a "valid" (struct hfs_bnode).
+ * Postconditions:
+ *   If 'bn' meets the appropriate conditions (see below) then it is
+ *   kept in the cache and all fields are set to consistent values
+ *   which reflect one less process using the node than upon entry.
+ *   If 'bn' does not meet the conditions then it is deleted (see
+ *   hfs_bnode_delete() for postconditions).
+ *   In either case, if 'lock_type' is HFS_LOCK_WRITE
+ *   then the corresponding buffer is dirtied.
+ */
+void hfs_bnode_relse(struct hfs_bnode_ref *bnr)
+{
+       struct hfs_bnode *bn;
+
+       if (!bnr || !(bn = bnr->bn)) {
+               return;
+       }
+
+       /* We update the lock state of the node if it is still in use
+          or if it is "sticky" (such as the B-tree head and root).
+          Otherwise we just delete it.  */
+       if ((bn->count > 1) || (bn->rqueue) || (bn->sticky != HFS_NOT_STICKY)) {
+               hfs_bnode_lock(bnr, HFS_LOCK_NONE);
+       } else {
+               /* dirty buffer if we (might) have modified it */
+               if (bnr->lock_type == HFS_LOCK_WRITE) {
+                       hfs_bnode_commit(bn);
+               }
+               hfs_bnode_delete(bn);
+               bnr->lock_type = HFS_LOCK_NONE;
+       }
+       bnr->bn = NULL;
+}
+
+/*
+ * hfs_bnode_find()
+ *
+ * Description:
+ *   This function is called to obtain a bnode.  The cache is
+ *   searched for the node.  If it not found there it is added to
+ *   the cache by hfs_bnode_read().  There are two special cases node=0
+ *   (the header node) and node='tree'->bthRoot (the root node), in
+ *   which the nodes are obtained from fields of 'tree' without
+ *   consulting or modifying the cache.
+ * Input Variable(s):
+ *   struct hfs_tree *tree: pointer to the (struct hfs_btree) from
+ *    which to get a node.
+ *   int node: the node number to get from 'tree'.
+ *   int lock_type: The kind of access (HFS_LOCK_READ, or
+ *    HFS_LOCK_RESRV) to obtain to the node
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   (struct hfs_bnode_ref) Reference to the requested node.
+ * Preconditions:
+ *   'tree' points to a "valid" (struct hfs_btree).
+ * Postconditions:
+ *   If 'node' refers to a valid node in 'tree' and 'lock_type' has
+ *   one of the values listed above and no I/O errors occur then the
+ *   value returned refers to a valid (struct hfs_bnode) corresponding
+ *   to the requested node with the requested access type.  The node
+ *   is also added to the cache if not previously present and not the
+ *   root or header.
+ *   If the conditions given above are not met, the bnode in the
+ *   returned reference is NULL.
+ */
+struct hfs_bnode_ref hfs_bnode_find(struct hfs_btree *tree,
+                                   hfs_u32 node, int lock_type)
+{
+       struct hfs_bnode *bn;
+       struct hfs_bnode *empty = NULL;
+       struct hfs_bnode_ref bnr;
+
+       bnr.lock_type = HFS_LOCK_NONE;
+       bnr.bn = NULL;
+
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+       hfs_warn("hfs_bnode_find: %c %d:%d\n",
+                lock_type==HFS_LOCK_READ?'R':
+                       (lock_type==HFS_LOCK_RESRV?'V':'W'),
+                (int)ntohl(tree->entry.cnid), node);
+#endif
+
+       /* check special cases */
+       if (!node) {
+               bn = &tree->head;
+               goto return_it;
+       } else if (node == tree->bthRoot) {
+               bn = tree->root;
+               goto return_it;
+       }
+
+restart:
+       /* look for the node in the cache. */
+       bn = bhash(tree, node);
+       while (bn && (bn->magic == HFS_BNODE_MAGIC)) {
+               if (bn->node == node) {
+                       goto found_it;
+               }
+               bn = bn->next;
+       }
+
+       if (!empty) {
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+               ++bnode_count;
+#endif
+               if (HFS_NEW(empty)) {
+                       goto restart;
+               }
+               return bnr;
+       }
+       bn = empty;
+       hfs_bnode_read(bn, tree, node, HFS_NOT_STICKY);
+       goto return_it;
+
+found_it:
+       /* check validity */
+       if (bn->magic != HFS_BNODE_MAGIC) {
+               /* If we find a corrupt bnode then we return
+                  NULL.  However, we don't try to remove it
+                  from the cache or release its resources
+                  since we have no idea what kind of trouble
+                  we could get into that way. */
+               hfs_warn("hfs_bnode_find: bnode cache is corrupt.\n");
+               return bnr;
+       }
+       if (empty) {
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+               --bnode_count;
+#endif
+               HFS_DELETE(empty);
+       }
+
+return_it:
+       /* Wait our turn */
+       bnr.bn = bn;
+       hfs_bnode_lock(&bnr, lock_type);
+
+       /* Check for failure to read the node from disk */
+       if (!hfs_buffer_ok(bn->buf)) {
+               hfs_bnode_relse(&bnr);
+       }
+
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+       if (!bnr.bn) {
+               hfs_warn("hfs_bnode_find: failed\n");
+       } else {
+               hfs_warn("hfs_bnode_find: use %d(%d) lvl %d [%d]\n", bn->count,
+                        bn->buf->b_count, bn->ndNHeight, bnode_count);
+       }
+#endif
+
+       return bnr;
+}
+
+/*
+ * hfs_bnode_commit()
+ *
+ * Called to write a possibly dirty bnode back to disk.
+ */
+void hfs_bnode_commit(struct hfs_bnode *bn)
+{
+       if (hfs_buffer_ok(bn->buf)) {
+               struct NodeDescriptor *nd;
+               nd = (struct NodeDescriptor *)hfs_buffer_data(bn->buf);
+
+               hfs_put_hl(bn->ndFLink, nd->ndFLink);
+               hfs_put_hl(bn->ndBLink, nd->ndBLink);
+               nd->ndType    = bn->ndType;
+               nd->ndNHeight = bn->ndNHeight;
+               hfs_put_hs(bn->ndNRecs, nd->ndNRecs);
+               hfs_buffer_dirty(bn->buf);
+
+               /* increment write count */
+               hfs_mdb_dirty(bn->tree->sys_mdb);
+       }
+}
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/fs/hfs/brec.c linux-2.0.29/fs/hfs/brec.c
--- linux.vanilla/fs/hfs/brec.c Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/fs/hfs/brec.c  Thu Apr 10 12:24:37 1997
@@ -0,0 +1,239 @@
+/*
+ * linux/fs/hfs/brec.c
+ *
+ * Copyright (C) 1995-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the code to access records in a btree.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include <linux/hfs_btree.h>
+
+/*================ File-local functions ================*/
+
+/*
+ * first()
+ *
+ * returns HFS_BPATH_FIRST if elem->record == 1, 0 otherwise
+ */
+static inline int first(const struct hfs_belem *elem)
+{
+       return (elem->record == 1) ? HFS_BPATH_FIRST : 0;
+}
+
+/*
+ * overflow()
+ *
+ * return HFS_BPATH_OVERFLOW if the node has no room for an
+ * additional pointer record, 0 otherwise.
+ */
+static inline int overflow(const struct hfs_btree *tree,
+                          const struct hfs_bnode *bnode)
+{
+       /* there is some algebra involved in getting this form */
+       return ((HFS_SECTOR_SIZE - sizeof(hfs_u32)) <
+                (bnode_end(bnode) + (2+bnode->ndNRecs)*sizeof(hfs_u16) +
+                 ROUND(tree->bthKeyLen+1))) ?  HFS_BPATH_OVERFLOW : 0;
+}
+
+/*
+ * underflow()
+ *
+ * return HFS_BPATH_UNDERFLOW if the node will be less that 1/2 full
+ * upon removal of a pointer record, 0 otherwise.
+ */
+static inline int underflow(const struct hfs_btree *tree,
+                           const struct hfs_bnode *bnode)
+{
+       return ((bnode->ndNRecs * sizeof(hfs_u16) +
+                bnode_offset(bnode, bnode->ndNRecs)) <
+               (HFS_SECTOR_SIZE - sizeof(struct NodeDescriptor))/2) ?
+               HFS_BPATH_UNDERFLOW : 0;
+}
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_brec_next()
+ *
+ * Description:
+ *   Obtain access to a child of an internal node in a B-tree.
+ * Input Variable(s):
+ *   struct hfs_brec *brec: pointer to the (struct hfs_brec) to
+ *    add an element to.
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   struct hfs_belem *: pointer to the new path element or NULL
+ * Preconditions:
+ *   'brec' points to a "valid" (struct hfs_brec), the last element of
+ *   which corresponds to a record in a bnode of type ndIndxNode and the
+ *   'record' field indicates the index record for the desired child.
+ * Postconditions:
+ *   If the call to hfs_bnode_find() fails then 'brec' is released
+ *   and a NULL is returned.
+ *   Otherwise:
+ *    Any ancestors in 'brec' that are not needed (as determined by the
+ *     'keep_flags' field of 'brec) are released from 'brec'.
+ *    A new element is added to 'brec' corresponding to the desired
+ *     child.
+ *    The child is obtained with the same 'lock_type' field as its
+ *     parent.
+ *    The 'record' field is initialized to the last record.
+ *    A pointer to the new path element is returned.
+ */
+struct hfs_belem *hfs_brec_next(struct hfs_brec *brec)
+{
+       struct hfs_belem *elem = brec->bottom;
+       hfs_u32 node;
+       int lock_type;
+
+       /* release unneeded ancestors */
+       elem->flags = first(elem) |
+                     overflow(brec->tree, elem->bnr.bn) |
+                     underflow(brec->tree, elem->bnr.bn);
+       if (!(brec->keep_flags & elem->flags)) {
+               hfs_brec_relse(brec, brec->bottom-1);
+       } else if ((brec->bottom-2 >= brec->top) &&
+                  !(elem->flags & (elem-1)->flags)) {
+               hfs_brec_relse(brec, brec->bottom-2);
+       }
+
+       node = hfs_get_hl(belem_record(elem));
+       lock_type = elem->bnr.lock_type;
+
+       if (!node || hfs_bnode_in_brec(node, brec)) {
+               hfs_warn("hfs_bfind: corrupt btree\n");
+               hfs_brec_relse(brec, NULL);
+               return NULL;
+       }
+
+       ++elem;
+       ++brec->bottom;
+
+       elem->bnr = hfs_bnode_find(brec->tree, node, lock_type);
+       if (!elem->bnr.bn) {
+               hfs_brec_relse(brec, NULL);
+               return NULL;
+       }
+       elem->record = elem->bnr.bn->ndNRecs;
+
+       return elem;
+}
+
+/*
+ * hfs_brec_lock()
+ *
+ * Description:
+ *   This function obtains HFS_LOCK_WRITE access to the bnode
+ *   containing this hfs_brec. All descendents in the path from this
+ *   record to the leaf are given HFS_LOCK_WRITE access and all
+ *   ancestors in the path from the root to here are released.
+ * Input Variable(s):
+ *   struct hfs_brec *brec: pointer to the brec to obtain
+ *    HFS_LOCK_WRITE access to some of the nodes of.
+ *   struct hfs_belem *elem: the first node to lock or NULL for all
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   void
+ * Preconditions:
+ *   'brec' points to a "valid" (struct hfs_brec)
+ * Postconditions:
+ *   All nodes between the indicated node and the beginning of the path
+ *    are released.  hfs_bnode_lock() is called in turn on each node
+ *    from the indicated node to the leaf node of the path, with a
+ *    lock_type argument of HFS_LOCK_WRITE.  If one of those calls
+ *    results in deadlock, then this function will never return.
+ */
+void hfs_brec_lock(struct hfs_brec *brec, struct hfs_belem *elem)
+{
+       if (!elem) {
+               elem = brec->top;
+       } else if (elem > brec->top) {
+               hfs_brec_relse(brec, elem-1);
+       }
+
+       while (elem <= brec->bottom) {
+               hfs_bnode_lock(&elem->bnr, HFS_LOCK_WRITE);
+               ++elem;
+       }
+}
+
+/*
+ * hfs_brec_init()
+ *
+ * Description:
+ *   Obtain access to the root node of a B-tree.
+ *   Note that this first must obtain access to the header node.
+ * Input Variable(s):
+ *   struct hfs_brec *brec: pointer to the (struct hfs_brec) to
+ *    initialize
+ *   struct hfs_btree *btree: pointer to the (struct hfs_btree)
+ *   int lock_type: the type of access to get to the nodes.
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   struct hfs_belem *: pointer to the root path element or NULL
+ * Preconditions:
+ *   'brec' points to a (struct hfs_brec).
+ *   'tree' points to a valid (struct hfs_btree).
+ * Postconditions:
+ *   If the two calls to brec_bnode_find() succeed then the return value
+ *   points to a (struct hfs_belem) which corresponds to the root node
+ *   of 'brec->tree'.
+ *   Both the root and header nodes are obtained with the type of lock
+ *   given by (flags & HFS_LOCK_MASK).
+ *   The fields 'record' field of the root is set to its last record.
+ *   If the header node is not needed to complete the appropriate
+ *   operation (as determined by the 'keep_flags' field of 'brec') then
+ *   it is released before this function returns.
+ *   If either call to brec_bnode_find() fails, NULL is returned and the
+ *   (struct hfs_brec) pointed to by 'brec' is invalid.
+ */
+struct hfs_belem *hfs_brec_init(struct hfs_brec *brec, struct hfs_btree *tree,
+                               int flags)
+{
+       struct hfs_belem *head = &brec->elem[0];
+       struct hfs_belem *root = &brec->elem[1];
+       int lock_type = flags & HFS_LOCK_MASK;
+
+       brec->tree = tree;
+
+       head->bnr = hfs_bnode_find(tree, 0, lock_type);
+       if (!head->bnr.bn) {
+               return NULL;
+       }
+
+       root->bnr = hfs_bnode_find(tree, tree->bthRoot, lock_type);
+       if (!root->bnr.bn) {
+               hfs_bnode_relse(&head->bnr);
+               return NULL;
+       }
+
+       root->record = root->bnr.bn->ndNRecs;
+
+       brec->top = head;
+       brec->bottom = root;
+
+       brec->keep_flags = flags & HFS_BPATH_MASK;
+
+       /* HFS_BPATH_FIRST not applicable for root */
+       /* and HFS_BPATH_UNDERFLOW is different */
+       root->flags = overflow(tree, root->bnr.bn);
+       if (root->record < 3) {
+               root->flags |= HFS_BPATH_UNDERFLOW;
+       }
+
+       if (!(root->flags & brec->keep_flags)) {
+               hfs_brec_relse(brec, head);
+       }
+
+       return root;
+}
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/fs/hfs/btree.c linux-2.0.29/fs/hfs/btree.c
--- linux.vanilla/fs/hfs/btree.c        Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/fs/hfs/btree.c Thu Apr 10 12:25:31 1997
@@ -0,0 +1,316 @@
+/*
+ * linux/fs/hfs/btree.c
+ *
+ * Copyright (C) 1995-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the code to manipulate the B-tree structure.
+ * The catalog and extents files are both B-trees.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ *
+ * The code in this file initializes some structures which contain
+ * pointers by calling memset(&foo, 0, sizeof(foo)).
+ * This produces the desired behavior only due to the non-ANSI
+ * assumption that the machine representation of NULL is all zeros.
+ */
+
+#include <linux/hfs_btree.h>
+
+/*================ File-local functions ================*/
+
+/*
+ * hfs_bnode_ditch()
+ *
+ * Description:
+ *   This function deletes an entire linked list of bnodes, so it
+ *   does not need to keep the linked list consistent as
+ *   hfs_bnode_delete() does.
+ *   Called by hfs_btree_init() for error cleanup and by hfs_btree_free().
+ * Input Variable(s):
+ *   struct hfs_bnode *bn: pointer to the first (struct hfs_bnode) in
+ *    the linked list to be deleted.
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   void
+ * Preconditions:
+ *   'bn' is NULL or points to a "valid" (struct hfs_bnode) with a 'prev'
+ *    field of NULL.
+ * Postconditions:
+ *   'bn' and all (struct hfs_bnode)s in the chain of 'next' pointers
+ *   are deleted, freeing the associated memory and hfs_buffer_put()ing
+ *   the associated buffer.
+ */
+static void hfs_bnode_ditch(struct hfs_bnode *bn) {
+       struct hfs_bnode *tmp;
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+       extern int bnode_count;
+#endif
+
+       while (bn != NULL) {
+               tmp = bn->next;
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+               hfs_warn("deleting node %d from tree %d with count %d\n",
+                        bn->node, (int)ntohl(bn->tree->entry.cnid), bn->count);
+               --bnode_count;
+#endif
+               hfs_buffer_put(bn->buf); /* safe: checks for NULL argument */
+
+               /* free all but the header */
+               if (bn->node) {
+                       HFS_DELETE(bn);
+               }
+               bn = tmp;
+       }
+}
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_btree_free()
+ *
+ * Description:
+ *   This function frees a (struct hfs_btree) obtained from hfs_btree_init().
+ *   Called by hfs_put_super().
+ * Input Variable(s):
+ *   struct hfs_btree *bt: pointer to the (struct hfs_btree) to free
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   void
+ * Preconditions:
+ *   'bt' is NULL or points to a "valid" (struct hfs_btree)
+ * Postconditions:
+ *   If 'bt' points to a "valid" (struct hfs_btree) then all (struct
+ *    hfs_bnode)s associated with 'bt' are freed by calling
+ *    hfs_bnode_ditch() and the memory associated with the (struct
+ *    hfs_btree) is freed.
+ *   If 'bt' is NULL or not "valid" an error is printed and nothing
+ *    is changed.
+ */
+void hfs_btree_free(struct hfs_btree *bt)
+{
+       int lcv;
+
+       if (bt && (bt->magic == HFS_BTREE_MAGIC)) {
+               hfs_extent_free(&bt->entry.u.file.data_fork);
+
+               for (lcv=0; lcv<HFS_CACHELEN; ++lcv) {
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+                       hfs_warn("deleting nodes from bucket %d:\n", lcv);
+#endif
+                       hfs_bnode_ditch(bt->cache[lcv]);
+               }
+
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+               hfs_warn("deleting header and bitmap nodes\n");
+#endif
+               hfs_bnode_ditch(&bt->head);
+
+#if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
+               hfs_warn("deleting root node\n");
+#endif
+               hfs_bnode_ditch(bt->root);
+
+               HFS_DELETE(bt);
+       } else if (bt) {
+               hfs_warn("hfs_btree_free: corrupted hfs_btree.\n");
+       }
+}
+
+/*
+ * hfs_btree_init()
+ *
+ * Description:
+ *   Given some vital information from the MDB (HFS superblock),
+ *   initializes the fields of a (struct hfs_btree).
+ * Input Variable(s):
+ *   struct hfs_mdb *mdb: pointer to the MDB
+ *   ino_t cnid: the CNID (HFS_CAT_CNID or HFS_EXT_CNID) of the B-tree
+ *   hfs_u32 tsize: the size, in bytes, of the B-tree
+ *   hfs_u32 csize: the size, in bytes, of the clump size for the B-tree
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   (struct hfs_btree *): pointer to the initialized hfs_btree on success,
+ *    or NULL on failure
+ * Preconditions:
+ *   'mdb' points to a "valid" (struct hfs_mdb)
+ * Postconditions:
+ *   Assuming the inputs are what they claim to be, no errors occur
+ *   reading from disk, and no inconsistencies are noticed in the data
+ *   read from disk, the return value is a pointer to a "valid"
+ *   (struct hfs_btree).  If there are errors reading from disk or
+ *   inconsistencies are noticed in the data read from disk, then and
+ *   all resources that were allocated are released and NULL is
+ *   returned. If the inputs are not what they claim to be or if they
+ *   are unnoticed inconsistencies in the data read from disk then the
+ *   returned hfs_btree is probably going to lead to errors when it is
+ *   used in a non-trivial way.
+ */
+struct hfs_btree * hfs_btree_init(struct hfs_mdb *mdb, ino_t cnid,
+                                 hfs_byte_t ext[12],
+                                 hfs_u32 tsize, hfs_u32 csize)
+{
+       struct hfs_btree * bt;
+       struct BTHdrRec * th;
+       struct hfs_bnode * tmp;
+       unsigned int next;
+#if defined(DEBUG_HEADER) || defined(DEBUG_ALL)
+       unsigned char *p, *q;
+#endif
+
+       if (!mdb || !ext || !HFS_NEW(bt)) {
+               goto bail3;
+       }
+
+       bt->magic = HFS_BTREE_MAGIC;
+       bt->sys_mdb = mdb->sys_mdb;
+       bt->reserved = 0;
+       bt->lock = 0;
+       bt->wait = NULL;
+       bt->dirt = 0;
+       memset(bt->cache, 0, sizeof(bt->cache));
+       bt->entry.mdb = mdb;
+       bt->entry.cnid = cnid;
+       bt->entry.type = HFS_CDR_FIL;
+       bt->entry.u.file.magic = HFS_FILE_MAGIC;
+       bt->entry.u.file.clumpablks = (csize / mdb->alloc_blksz)
+                                               >> HFS_SECTOR_SIZE_BITS;
+       bt->entry.u.file.data_fork.entry = &bt->entry;
+       bt->entry.u.file.data_fork.lsize = tsize;
+       bt->entry.u.file.data_fork.psize = tsize >> HFS_SECTOR_SIZE_BITS;
+       bt->entry.u.file.data_fork.fork = HFS_FK_DATA;
+       hfs_extent_in(&bt->entry.u.file.data_fork, ext);
+
+       hfs_bnode_read(&bt->head, bt, 0, HFS_STICKY);
+       if (!hfs_buffer_ok(bt->head.buf)) {
+               goto bail2;
+       }
+       th = (struct BTHdrRec *)((char *)hfs_buffer_data(bt->head.buf) +
+                                               sizeof(struct NodeDescriptor));
+
+       /* read in the bitmap nodes (if any) */
+       tmp = &bt->head;
+       while ((next = tmp->ndFLink)) {
+               if (!HFS_NEW(tmp->next)) {
+                       goto bail2;
+               }
+               hfs_bnode_read(tmp->next, bt, next, HFS_STICKY);
+               if (!hfs_buffer_ok(tmp->next->buf)) {
+                       goto bail2;
+               }
+               tmp->next->prev = tmp;
+               tmp = tmp->next;
+       }
+
+       if (hfs_get_ns(th->bthNodeSize) != htons(HFS_SECTOR_SIZE)) {
+               hfs_warn("hfs_btree_init: bthNodeSize!=512 not supported\n");
+               goto bail2;
+       }
+
+       if (cnid == htonl(HFS_CAT_CNID)) {
+               bt->compare = (hfs_cmpfn)hfs_cat_compare;
+       } else if (cnid == htonl(HFS_EXT_CNID)) {
+               bt->compare = (hfs_cmpfn)hfs_ext_compare;
+       } else {
+               goto bail2;
+       }
+       bt->bthDepth  = hfs_get_hs(th->bthDepth);
+       bt->bthRoot   = hfs_get_hl(th->bthRoot);
+       bt->bthNRecs  = hfs_get_hl(th->bthNRecs);
+       bt->bthFNode  = hfs_get_hl(th->bthFNode);
+       bt->bthLNode  = hfs_get_hl(th->bthLNode);
+       bt->bthNNodes = hfs_get_hl(th->bthNNodes);
+       bt->bthFree   = hfs_get_hl(th->bthFree);
+       bt->bthKeyLen = hfs_get_hs(th->bthKeyLen);
+
+#if defined(DEBUG_HEADER) || defined(DEBUG_ALL)
+       hfs_warn("bthDepth %d\n", bt->bthDepth);
+       hfs_warn("bthRoot %d\n", bt->bthRoot);
+       hfs_warn("bthNRecs %d\n", bt->bthNRecs);
+       hfs_warn("bthFNode %d\n", bt->bthFNode);
+       hfs_warn("bthLNode %d\n", bt->bthLNode);
+       hfs_warn("bthKeyLen %d\n", bt->bthKeyLen);
+       hfs_warn("bthNNodes %d\n", bt->bthNNodes);
+       hfs_warn("bthFree %d\n", bt->bthFree);
+       p = (unsigned char *)hfs_buffer_data(bt->head.buf);
+       q = p + HFS_SECTOR_SIZE;
+       while (p < q) {
+               hfs_warn("%02x %02x %02x %02x %02x %02x %02x %02x "
+                        "%02x %02x %02x %02x %02x %02x %02x %02x\n",
+                        *p++, *p++, *p++, *p++, *p++, *p++, *p++, *p++,
+                        *p++, *p++, *p++, *p++, *p++, *p++, *p++, *p++);
+       }
+#endif
+
+       /* Read in the root if it exists.
+          The header always exists, but the root exists only if the
+          tree is non-empty */
+       if (bt->bthDepth && bt->bthRoot) {
+               if (!HFS_NEW(bt->root)) {
+                       goto bail2;
+               }
+               hfs_bnode_read(bt->root, bt, bt->bthRoot, HFS_STICKY);
+               if (!hfs_buffer_ok(bt->root->buf)) {
+                       goto bail1;
+               }
+       } else {
+               bt->root = NULL;
+       }
+
+       return bt;
+
+ bail1:
+       hfs_bnode_ditch(bt->root);
+ bail2:
+       hfs_bnode_ditch(&bt->head);
+       HFS_DELETE(bt);
+ bail3:
+       return NULL;
+}
+
+/*
+ * hfs_btree_commit()
+ *
+ * Called to write a possibly dirty btree back to disk.
+ */
+void hfs_btree_commit(struct hfs_btree *bt, hfs_byte_t ext[12], hfs_lword_t size)
+{
+       if (bt->dirt) {
+               struct BTHdrRec *th;
+               th = (struct BTHdrRec *)((char *)hfs_buffer_data(bt->head.buf) +
+                                                sizeof(struct NodeDescriptor));
+
+               hfs_put_hs(bt->bthDepth,  th->bthDepth);
+               hfs_put_hl(bt->bthRoot,   th->bthRoot);
+               hfs_put_hl(bt->bthNRecs,  th->bthNRecs);
+               hfs_put_hl(bt->bthFNode,  th->bthFNode);
+               hfs_put_hl(bt->bthLNode,  th->bthLNode);
+               hfs_put_hl(bt->bthNNodes, th->bthNNodes);
+               hfs_put_hl(bt->bthFree,   th->bthFree);
+               hfs_buffer_dirty(bt->head.buf);
+
+               /*
+                * Commit the bnodes which are not cached.
+                * The map nodes don't need to be committed here because
+                * they are committed every time they are changed.
+                */
+               hfs_bnode_commit(&bt->head);
+               if (bt->root) {
+                       hfs_bnode_commit(bt->root);
+               }
+
+
+               hfs_put_hl(bt->bthNNodes << HFS_SECTOR_SIZE_BITS, size);
+               hfs_extent_out(&bt->entry.u.file.data_fork, ext);
+               /* hfs_buffer_dirty(mdb->buf); (Done by caller) */
+
+               bt->dirt = 0;
+       }
+}
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/fs/hfs/catalog.c linux-2.0.29/fs/hfs/catalog.c
--- linux.vanilla/fs/hfs/catalog.c      Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/fs/hfs/catalog.c       Thu Apr 10 12:25:45 1997
@@ -0,0 +1,1587 @@
+/*
+ * linux/fs/hfs/catalog.c
+ *
+ * Copyright (C) 1995-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the functions related to the catalog B-tree.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * Cache code shamelessly stolen from
+ *     linux/fs/inode.c Copyright (C) 1991, 1992  Linus Torvalds
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ *
+ * The code in this file initializes some structures by calling
+ * memset(&foo, 0, sizeof(foo)).  This produces the desired behavior
+ * only due to the non-ANSI assumption that the machine representation
+ */
+
+#include <linux/hfs.h>
+
+/*================ Variable-like macros ================*/
+
+/* Number of hash table slots */
+#define CCACHE_NR 128
+
+/* Max number of entries in memory */
+#define CCACHE_MAX 1024
+
+/* Number of entries to fit in a single page on an i386 */
+#define CCACHE_INC ((4080-sizeof(void *))/sizeof(struct hfs_cat_entry))
+
+/*================ File-local data types ================*/
+
+/* The catalog record for a file */
+typedef struct {
+       hfs_byte_t      Flags;          /* Flags such as read-only */
+       hfs_byte_t      Typ;            /* file version number = 0 */
+       hfs_finfo_t     UsrWds;         /* data used by the Finder */
+       hfs_lword_t     FlNum;          /* The CNID */
+       hfs_word_t      StBlk;          /* obsolete */
+       hfs_lword_t     LgLen;          /* The logical EOF of the data fork*/
+       hfs_lword_t     PyLen;          /* The physical EOF of the data fork */
+       hfs_word_t      RStBlk;         /* obsolete */
+       hfs_lword_t     RLgLen;         /* The logical EOF of the rsrc fork */
+       hfs_lword_t     RPyLen;         /* The physical EOF of the rsrc fork */
+       hfs_lword_t     CrDat;          /* The creation date */
+       hfs_lword_t     MdDat;          /* The modified date */
+       hfs_lword_t     BkDat;          /* The last backup date */
+       hfs_fxinfo_t    FndrInfo;       /* more data for the Finder */
+       hfs_word_t      ClpSize;        /* number of bytes to allocate
+                                          when extending files */
+       hfs_byte_t      ExtRec[12];     /* first extent record
+                                          for the data fork */
+       hfs_byte_t      RExtRec[12];    /* first extent record
+                                          for the resource fork */
+       hfs_lword_t     Resrv;          /* reserved by Apple */
+} FIL_REC;
+
+/* the catalog record for a directory */
+typedef struct {
+       hfs_word_t      Flags;          /* flags */
+       hfs_word_t      Val;            /* Valence: number of files and
+                                          dirs in the directory */
+       hfs_lword_t     DirID;          /* The CNID */
+       hfs_lword_t     CrDat;          /* The creation date */
+       hfs_lword_t     MdDat;          /* The modification date */
+       hfs_lword_t     BkDat;          /* The last backup date */
+       hfs_dinfo_t     UsrInfo;        /* data used by the Finder */
+       hfs_dxinfo_t    FndrInfo;       /* more data used by Finder */
+       hfs_byte_t      Resrv[16];      /* reserved by Apple */
+} DIR_REC;
+
+/* the catalog record for a thread */
+typedef struct {
+       hfs_byte_t              Reserv[8];      /* reserved by Apple */
+       hfs_lword_t             ParID;          /* CNID of parent directory */
+       struct hfs_name         CName;          /* The name of this entry */
+} THD_REC;
+
+/* A catalog tree record */
+struct hfs_cat_rec {
+       hfs_byte_t              cdrType;        /* The type of entry */
+       hfs_byte_t              cdrResrv2;      /* padding */
+       union {
+               FIL_REC fil;
+               DIR_REC dir;
+               THD_REC thd;
+       } u;
+};
+
+typedef struct hfs_cat_entry *hfs_cat_entry_ptr;
+
+struct allocation_unit {
+       struct allocation_unit *next;
+       struct hfs_cat_entry entries[CCACHE_INC];
+};
+
+/*================ File-local variables ================*/
+
+static hfs_cat_entry_ptr hash_table[CCACHE_NR] = {NULL, };
+
+static struct hfs_cat_entry *first_entry = NULL;
+static hfs_wait_queue entry_wait;
+static int nr_entries = 0, nr_free_entries = 0;
+
+static struct allocation_unit *allocation = NULL;
+
+/*================ File-local functions ================*/
+
+/*
+ * brec_to_id
+ *
+ * Get the CNID from a brec
+ */
+static inline hfs_u32 brec_to_id(struct hfs_brec *brec)
+{
+       struct hfs_cat_rec *rec = brec->data;
+
+       return hfs_get_nl((rec->cdrType==HFS_CDR_FIL) ?
+                               rec->u.fil.FlNum : rec->u.dir.DirID);
+}
+
+/*
+ * hashfn()
+ *
+ * hash an (struct mdb *) and a (struct hfs_cat_key *) to an integer.
+ */
+static inline unsigned int hashfn(const struct hfs_mdb *mdb,
+                                 const struct hfs_cat_key *key)
+{
+#define LSB(X) (((unsigned char *)(&X))[3])
+       return ((unsigned int)LSB(mdb->create_date) ^
+               (unsigned int)key->ParID[3] ^
+               hfs_strhash(&key->CName)) % CCACHE_NR;
+#undef LSB
+}
+
+/*
+ * hash()
+ *
+ * hash an (struct mdb *) and a (struct hfs_cat_key *)
+ * to a pointer to a slot in the hash table.
+ */
+static inline struct hfs_cat_entry **hash(struct hfs_mdb *mdb,
+                                         const struct hfs_cat_key *key)
+{
+       return hash_table + hashfn(mdb, key);
+}
+
+/*
+ * insert_free()
+ *
+ * Add an entry to the front of the free list.
+ */
+static inline void insert_free(struct hfs_cat_entry *entry)
+{
+       struct hfs_cat_entry * prev, * next = first_entry;
+
+       first_entry = entry;
+       prev = next->prev;
+       entry->next = next;
+       entry->prev = prev;
+       prev->next = entry;
+       next->prev = entry;
+}
+
+/*
+ * remove_free()
+ *
+ * Remove an entry from the free list.
+ */
+static inline void remove_free(struct hfs_cat_entry *entry)
+{
+       if (first_entry == entry) {
+               first_entry = first_entry->next;
+       }
+       if (entry->next) {
+               entry->next->prev = entry->prev;
+       }
+       if (entry->prev) {
+               entry->prev->next = entry->next;
+       }
+       entry->next = entry->prev = NULL;
+}
+
+/*
+ * insert_hash()
+ *
+ * Add an entry to the front of the appropriate hash list
+ */
+static void insert_hash(struct hfs_cat_entry *entry)
+{
+       struct hfs_cat_entry **h;
+       h = hash(entry->mdb, &entry->key);
+
+       entry->hash_next = *h;
+       entry->hash_prev = NULL;
+       if (entry->hash_next) {
+               entry->hash_next->hash_prev = entry;
+       }
+       *h = entry;
+}
+
+/*
+ * remove_hash
+ *
+ * Remove an entry from its hash list (if any).
+ */
+static void remove_hash(struct hfs_cat_entry *entry)
+{
+       struct hfs_cat_entry **h;
+
+       if (entry->mdb) {
+               h = hash(entry->mdb, &entry->key);
+
+               if (*h == entry) {
+                       *h = entry->hash_next;
+               }
+               if (entry->hash_next) {
+                       entry->hash_next->hash_prev = entry->hash_prev;
+               }
+               if (entry->hash_prev) {
+                       entry->hash_prev->hash_next = entry->hash_next;
+               }
+               entry->hash_prev = entry->hash_next = NULL;
+       }
+}
+
+/*
+ * put_last_free()
+ *
+ * Move an entry to the end of the free list.
+ */
+static inline void put_last_free(struct hfs_cat_entry *entry)
+{
+       remove_free(entry);
+       entry->prev = first_entry->prev;
+       entry->prev->next = entry;
+       entry->next = first_entry;
+       entry->next->prev = entry;
+}
+
+/*
+ * grow_entries()
+ *
+ * Try to allocate more entries, adding them to the free list.
+ */
+static int grow_entries(void)
+{
+       struct allocation_unit *tmp;
+       struct hfs_cat_entry * entry;
+       int i;
+
+       if (!HFS_NEW(tmp)) {
+               return -ENOMEM;
+       }
+
+       memset(tmp, 0, sizeof(*tmp));
+
+       tmp->next = allocation;
+       allocation = tmp;
+       entry = tmp->entries;
+
+       i = CCACHE_INC;
+
+       nr_entries += i;
+       nr_free_entries += i;
+
+       if (!first_entry) {
+               entry->next = entry->prev = first_entry = entry++;
+               --i;
+       }
+
+       for ( i = CCACHE_INC - 1; i ; --i ) {
+               insert_free(entry++);
+       }
+       return 0;
+}
+
+/*
+ * wait_on_entry()
+ *
+ * Sleep until a locked entry is unlocked.
+ */
+static inline void wait_on_entry(struct hfs_cat_entry * entry)
+{
+       while (entry->lock) {
+               hfs_sleep_on(&entry->wait);
+       }
+}
+
+/*
+ * lock_entry()
+ *
+ * Obtain an exclusive lock on an entry.
+ */
+static void lock_entry(struct hfs_cat_entry * entry)
+{
+       wait_on_entry(entry);
+       entry->lock = 1;
+}
+
+/*
+ * lock_entry()
+ *
+ * Relinquish an exclusive lock on an entry.
+ */
+static void unlock_entry(struct hfs_cat_entry * entry)
+{
+       entry->lock = 0;
+       hfs_wake_up(&entry->wait);
+}
+
+/*
+ * clear_entry()
+ *
+ * Zero all the fields of an entry and place it on the free list.
+ */
+static void clear_entry(struct hfs_cat_entry * entry)
+{
+       wait_on_entry(entry);
+       remove_hash(entry);
+       remove_free(entry);
+       if (entry->count) {
+               nr_free_entries++;
+       }
+       /* zero all but the wait queue */
+       memset(&entry->next, 0,
+              sizeof(*entry) - offsetof(struct hfs_cat_entry, next));
+       insert_free(entry);
+}
+
+/*
+ * __read_entry()
+ *
+ * Convert a (struct hfs_cat_rec) to a (struct hfs_cat_entry).
+ */
+static void __read_entry(struct hfs_cat_entry *entry,
+                        const struct hfs_cat_rec *cat)
+{
+       entry->type = cat->cdrType;
+
+       if (cat->cdrType == HFS_CDR_DIR) {
+               struct hfs_dir *dir = &entry->u.dir;
+
+               entry->cnid = hfs_get_nl(cat->u.dir.DirID);
+
+               dir->magic = HFS_DIR_MAGIC;
+               dir->flags = hfs_get_ns(cat->u.dir.Flags);
+               memcpy(&entry->info.dir.dinfo, &cat->u.dir.UsrInfo, 16);
+               memcpy(&entry->info.dir.dxinfo, &cat->u.dir.FndrInfo, 16);
+               entry->create_date = hfs_get_nl(cat->u.dir.CrDat);
+               entry->modify_date = hfs_get_nl(cat->u.dir.MdDat);
+               entry->backup_date = hfs_get_nl(cat->u.dir.BkDat);
+               dir->dirs = dir->files = 0;
+       } else if (cat->cdrType == HFS_CDR_FIL) {
+               struct hfs_file *fil = &entry->u.file;
+
+               entry->cnid = hfs_get_nl(cat->u.fil.FlNum);
+
+               fil->magic = HFS_FILE_MAGIC;
+
+               fil->data_fork.fork = HFS_FK_DATA;
+               fil->data_fork.entry = entry;
+               fil->data_fork.lsize = hfs_get_hl(cat->u.fil.LgLen);
+               fil->data_fork.psize = hfs_get_hl(cat->u.fil.PyLen) >>
+                                                    HFS_SECTOR_SIZE_BITS;
+               hfs_extent_in(&fil->data_fork, cat->u.fil.ExtRec);
+
+               fil->rsrc_fork.fork = HFS_FK_RSRC;
+               fil->rsrc_fork.entry = entry;
+               fil->rsrc_fork.lsize = hfs_get_hl(cat->u.fil.RLgLen);
+               fil->rsrc_fork.psize = hfs_get_hl(cat->u.fil.RPyLen) >>
+                                                    HFS_SECTOR_SIZE_BITS;
+               hfs_extent_in(&fil->rsrc_fork, cat->u.fil.RExtRec);
+
+               memcpy(&entry->info.file.finfo, &cat->u.fil.UsrWds, 16);
+               memcpy(&entry->info.file.fxinfo, &cat->u.fil.FndrInfo, 16);
+
+               entry->create_date = hfs_get_nl(cat->u.fil.CrDat);
+               entry->modify_date = hfs_get_nl(cat->u.fil.MdDat);
+               entry->backup_date = hfs_get_nl(cat->u.fil.BkDat);
+               fil->clumpablks = (hfs_get_hs(cat->u.fil.ClpSize)
+                                       / entry->mdb->alloc_blksz)
+                                               >> HFS_SECTOR_SIZE_BITS;
+               fil->flags = cat->u.fil.Flags;
+       } else {
+               hfs_warn("hfs_fs: entry is neither file nor directory!\n");
+       }
+}
+
+/*
+ * count_dir_entries()
+ *
+ * Count the number of files and directories in a given directory.
+ */
+static inline void count_dir_entries(struct hfs_cat_entry *entry,
+                                    struct hfs_brec *brec)
+{
+       int error = 0;
+       hfs_u32 cnid;
+       hfs_u8 type;
+
+       if (!hfs_cat_open(entry, brec)) {
+               while (!(error = hfs_cat_next(entry, brec, 1, &cnid, &type))) {
+                       if (type == HFS_CDR_FIL) {
+                               ++entry->u.dir.files;
+                       } else if (type == HFS_CDR_DIR) {
+                               ++entry->u.dir.dirs;
+                       }
+               } /* -ENOENT is normal termination */
+       }
+       if (error != -ENOENT) {
+               entry->cnid = 0;
+       }
+}
+
+/*
+ * read_entry()
+ *
+ * Convert a (struct hfs_brec) to a (struct hfs_cat_entry).
+ */
+static inline void read_entry(struct hfs_cat_entry *entry,
+                             struct hfs_brec *brec)
+{
+       int need_count;
+       struct hfs_cat_rec *rec = brec->data;
+
+       __read_entry(entry, rec);
+
+       need_count = (rec->cdrType == HFS_CDR_DIR) && rec->u.dir.Val;
+
+       hfs_brec_relse(brec, NULL);
+
+       if (need_count) {
+               count_dir_entries(entry, brec);
+       }
+}
+
+/*
+ * __write_entry()
+ *
+ * Convert a (struct hfs_cat_entry) to a (struct hfs_cat_rec).
+ */
+static void __write_entry(const struct hfs_cat_entry *entry,
+                         struct hfs_cat_rec *cat)
+{
+       if (entry->type == HFS_CDR_DIR) {
+               const struct hfs_dir *dir = &entry->u.dir;
+
+               hfs_put_ns(dir->flags,             cat->u.dir.Flags);
+               hfs_put_hs(dir->dirs + dir->files, cat->u.dir.Val);
+               hfs_put_nl(entry->cnid,            cat->u.dir.DirID);
+               hfs_put_nl(entry->create_date,     cat->u.dir.CrDat);
+               hfs_put_nl(entry->modify_date,     cat->u.dir.MdDat);
+               hfs_put_nl(entry->backup_date,     cat->u.dir.BkDat);
+               memcpy(&cat->u.dir.UsrInfo, &entry->info.dir.dinfo, 16);
+               memcpy(&cat->u.dir.FndrInfo, &entry->info.dir.dxinfo, 16);
+       } else if (entry->type == HFS_CDR_FIL) {
+               const struct hfs_file *fil = &entry->u.file;
+
+               cat->u.fil.Flags = fil->flags;
+               hfs_put_nl(entry->cnid,            cat->u.fil.FlNum);
+               memcpy(&cat->u.fil.UsrWds, &entry->info.file.finfo, 16);
+               hfs_put_hl(fil->data_fork.lsize, cat->u.fil.LgLen);
+               hfs_put_hl(fil->data_fork.psize << HFS_SECTOR_SIZE_BITS,
+                                                       cat->u.fil.PyLen);
+               hfs_put_hl(fil->rsrc_fork.lsize, cat->u.fil.RLgLen);
+               hfs_put_hl(fil->rsrc_fork.psize << HFS_SECTOR_SIZE_BITS,
+                                                       cat->u.fil.RPyLen);
+               hfs_put_nl(entry->create_date,     cat->u.fil.CrDat);
+               hfs_put_nl(entry->modify_date,     cat->u.fil.MdDat);
+               hfs_put_nl(entry->backup_date,     cat->u.fil.BkDat);
+               memcpy(&cat->u.fil.FndrInfo, &entry->info.file.fxinfo, 16);
+               hfs_put_hs((fil->clumpablks * entry->mdb->alloc_blksz)
+                               << HFS_SECTOR_SIZE_BITS, cat->u.fil.ClpSize);
+               hfs_extent_out(&fil->data_fork, cat->u.fil.ExtRec);
+               hfs_extent_out(&fil->rsrc_fork, cat->u.fil.RExtRec);
+       } else {
+               hfs_warn("__write_entry: invalid entry\n");
+       }
+}
+
+/*
+ * write_entry()
+ *
+ * Write a modified entry back to the catalog B-tree.
+ */
+static void write_entry(struct hfs_cat_entry * entry)
+{
+       struct hfs_brec brec;
+       int error;
+
+       wait_on_entry(entry);
+       if (!entry->dirt) {
+               return;
+       }
+       if (!entry->deleted) {
+               entry->lock = 1;
+               error = hfs_bfind(&brec, entry->mdb->cat_tree,
+                                 HFS_BKEY(&entry->key), HFS_BFIND_WRITE);
+               if (!error) {
+                       if (entry->key_dirt) {
+                               /* key may have changed case due to a rename */
+                               entry->key_dirt = 0;
+                               if (brec.key->KeyLen != entry->key.KeyLen) {
+                                       hfs_warn("hfs_write_entry: key length "
+                                                "changed!\n");
+                                       error = 1;
+                               } else {
+                                       memcpy(brec.key, &entry->key,
+                                              entry->key.KeyLen);
+                               }
+                       } else if (entry->cnid != brec_to_id(&brec)) {
+                               hfs_warn("hfs_write_entry: CNID "
+                                        "changed unexpectedly!\n");
+                               error = 1;
+                       }
+                       if (!error) {
+                               __write_entry(entry, brec.data);
+                       }
+                       hfs_brec_relse(&brec, NULL);
+               }
+               if (error) {
+                       hfs_warn("hfs_write_entry: unable to write "
+                                "entry %08x\n", entry->cnid);
+               }
+               unlock_entry(entry);
+       }
+       entry->dirt = 0;
+}
+
+/*
+ * get_empty_entry()
+ *
+ * Allocate an unused entry.
+ */
+static inline struct hfs_cat_entry *get_empty_entry(void)
+{
+       struct hfs_cat_entry * entry, * best;
+       int i;
+
+       if ((nr_entries < CCACHE_MAX) &&
+           (nr_free_entries <= (nr_entries >> 1))) {
+               grow_entries();
+       }
+repeat:
+       entry = first_entry;
+       best = NULL;
+       for (i = nr_entries/2; i > 0; i--,entry = entry->next) {
+               if (!entry->count) {
+                       if (!best) {
+                               best = entry;
+                       }
+                       if (!entry->dirt && !entry->lock) {
+                               best = entry;
+                               break;
+                       }
+
+               }
+       }
+       if (!best || best->dirt || best->lock) {
+               if (nr_entries < CCACHE_MAX) {
+                       if (grow_entries() == 0) {
+                               goto repeat;
+                       }
+               }
+       }
+       entry = best;
+       if (!entry) {
+               hfs_warn("hfs_cat_get: No free entries\n");
+               hfs_sleep_on(&entry_wait);
+               goto repeat;
+       }
+       if (entry->lock) {
+               wait_on_entry(entry);
+               goto repeat;
+       }
+       if (entry->dirt) {
+               write_entry(entry);
+               goto repeat;
+       }
+       if (entry->count) {
+               goto repeat;
+       }
+
+       clear_entry(entry);
+       entry->count = 1;
+       entry->dirt = 0;
+       entry->deleted = 1;     /* so it gets cleared if discarded */
+
+       nr_free_entries--;
+       if (nr_free_entries < 0) {
+               hfs_warn ("hfs_get_empty_entry: bad free entry count.\n");
+               nr_free_entries = 0;
+       }
+       return entry;
+}
+
+/*
+ * get_entry()
+ *
+ * Try to return an entry for the indicated file or directory.
+ * If ('read' == 0) then no attempt will be made to read it from disk
+ * and a locked, but uninitialized, entry is returned.
+ */
+static struct hfs_cat_entry *get_entry(struct hfs_mdb *mdb,
+                                      const struct hfs_cat_key *key, int read)
+{
+       struct hfs_cat_entry **h;
+       struct hfs_cat_entry * entry;
+       struct hfs_cat_entry * empty = NULL;
+       struct hfs_brec brec;
+
+       h = hash(mdb, key);
+repeat:
+       for (entry = *h; entry ; entry = entry->hash_next) {
+               if (entry->mdb == mdb && !hfs_cat_compare(&entry->key, key)) {
+                       goto found_it;
+               }
+       }
+       if (!empty) {
+               empty = get_empty_entry();
+               if (empty && read &&
+                   hfs_bfind(&brec, mdb->cat_tree,
+                             HFS_BKEY(key), HFS_BFIND_READ_EQ)) {
+                       /* If we get here then:
+                               1) We got an empty entry.
+                               2) We want to read the record.
+                               3) We failed to find the record. */
+                       hfs_cat_put(empty);
+                       empty = NULL;
+               }
+               if (empty) {
+                       goto repeat;
+               }
+               return NULL;
+       }
+       entry = empty;
+       entry->deleted = 0;
+       entry->mdb = mdb;
+       memcpy(&entry->key, key, sizeof(*key));
+       put_last_free(entry);
+       insert_hash(entry);
+
+       entry->lock = 1;
+       if (!read) {
+               /* Return a locked but incomplete entry.  Note that the
+                  caller can tell it is incomplete since entry->cnid = 0. */
+               return entry;
+       }
+       read_entry(entry, &brec);
+       unlock_entry(entry);
+
+       goto return_it;
+
+found_it:
+       if (!entry->count) {
+               nr_free_entries--;
+       }
+       entry->count++;
+       if (empty) {
+               if (read) {
+                       hfs_brec_relse(&brec, NULL);
+               }
+               hfs_cat_put(empty);
+       }
+       wait_on_entry(entry);
+       if (entry->deleted) {
+               /* The entry was deleted while we slept */
+               hfs_cat_put(entry);
+               hfs_relinquish();
+               goto repeat;
+       }
+
+return_it:
+       if (!entry->cnid) {
+               /* There was an error reading the entry */
+               hfs_cat_put(entry);
+               entry = NULL;
+       }
+       return entry;
+}
+
+/*
+ * new_cnid()
+ *
+ * Allocate a CNID to use for a new file or directory.
+ */
+static inline hfs_u32 new_cnid(struct hfs_mdb *mdb)
+{
+       /* If the create succeeds then the mdb will get dirtied */
+       return htonl(mdb->next_id++);
+}
+
+/*
+ * update_dir()
+ *
+ * Update counts, times and dirt on a changed directory
+ */
+static void update_dir(struct hfs_mdb *mdb, struct hfs_cat_entry *dir,
+                      int is_dir, int count)
+{
+       /* update counts */
+       if (is_dir) {
+               mdb->dir_count += count;
+               dir->u.dir.dirs += count;
+               if (dir->cnid == htonl(HFS_ROOT_CNID)) {
+                       mdb->root_dirs += count;
+               }
+       } else {
+               mdb->file_count += count;
+               dir->u.dir.files += count;
+               if (dir->cnid == htonl(HFS_ROOT_CNID)) {
+                       mdb->root_files += count;
+               }
+       }
+
+       /* update times and dirt */
+       dir->modify_date = hfs_time();
+       dir->dirt = 1;
+}
+
+/*
+ * Add a writer to dir, excluding readers.
+ */
+static inline void start_write(struct hfs_cat_entry *dir)
+{
+       if (dir->u.dir.readers || dir->u.dir.read_wait) {
+               hfs_sleep_on(&dir->u.dir.write_wait);
+       }
+       ++dir->u.dir.writers;
+}
+
+/*
+ * Add a reader to dir, excluding writers.
+ */
+static inline void start_read(struct hfs_cat_entry *dir)
+{
+       if (dir->u.dir.writers || dir->u.dir.write_wait) {
+               hfs_sleep_on(&dir->u.dir.read_wait);
+       }
+       ++dir->u.dir.readers;
+}
+
+/*
+ * Remove a writer from dir, possibly admitting readers.
+ */
+static inline void end_write(struct hfs_cat_entry *dir)
+{
+       if (!(--dir->u.dir.writers)) {
+               hfs_wake_up(&dir->u.dir.read_wait);
+       }
+}
+
+/*
+ * Remove a reader from dir, possibly admitting writers.
+ */
+static inline void end_read(struct hfs_cat_entry *dir)
+{
+       if (!(--dir->u.dir.readers)) {
+               hfs_wake_up(&dir->u.dir.write_wait);
+       }
+}
+
+/*
+ * create_entry()
+ *
+ * Add a new file or directory to the catalog B-tree and
+ * return a (struct hfs_cat_entry) for it in '*result'.
+ */
+static int create_entry(struct hfs_cat_entry *parent, struct hfs_cat_key *key,
+                       const struct hfs_cat_rec *record, int is_dir,
+                       hfs_u32 cnid, struct hfs_cat_entry **result)
+{
+       struct hfs_mdb *mdb = parent->mdb;
+       struct hfs_cat_entry *entry;
+       struct hfs_cat_key thd_key;
+       struct hfs_cat_rec thd_rec;
+       int error, has_thread;
+
+       if (result) {
+               *result = NULL;
+       }
+
+       /* keep readers from getting confused by changing dir size */
+       start_write(parent);
+
+       /* create a locked entry in the cache */
+       entry = get_entry(mdb, key, 0);
+       if (!entry) {
+               /* The entry exists but can't be read */
+               error = -EIO;
+               goto done;
+       }
+       if (entry->cnid) {
+               /* The (unlocked) entry exists in the cache */
+               error = -EEXIST;
+               goto bail2;
+       }
+
+       /* limit directory valence to signed 16-bit integer */
+        if ((parent->u.dir.dirs + parent->u.dir.files) >= HFS_MAX_VALENCE) {
+               error = -ENOSPC;
+               goto bail1;
+       }
+
+       has_thread = is_dir || (record->u.fil.Flags & HFS_FIL_THD);
+
+       if (has_thread) {
+               /* init some fields for the thread record */
+               memset(&thd_rec, 0, sizeof(thd_rec));
+               thd_rec.cdrType = is_dir ? HFS_CDR_THD : HFS_CDR_FTH;
+               memcpy(&thd_rec.u.thd.ParID, &key->ParID,
+                      sizeof(hfs_u32) + sizeof(struct hfs_name));
+
+               /* insert the thread record */
+               hfs_cat_build_key(cnid, NULL, &thd_key);
+               error = hfs_binsert(mdb->cat_tree, HFS_BKEY(&thd_key),
+                                   &thd_rec, 2 + sizeof(THD_REC));
+               if (error) {
+                       goto bail1;
+               }
+       }
+
+       /* insert the record */
+       error = hfs_binsert(mdb->cat_tree, HFS_BKEY(key), record,
+                               is_dir ?  2 + sizeof(DIR_REC) :
+                                         2 + sizeof(FIL_REC));
+       if (error) {
+               if (has_thread && (error != -EIO)) {
+                       /* at least TRY to remove the thread record */
+                       (void)hfs_bdelete(mdb->cat_tree, HFS_BKEY(&thd_key));
+               }
+               goto bail1;
+       }
+
+       /* update the parent directory */
+       update_dir(mdb, parent, is_dir, 1);
+
+       /* complete the cache entry and return success */
+       __read_entry(entry, record);
+       unlock_entry(entry);
+       if (result) {
+               *result = entry;
+       } else {
+               hfs_cat_put(entry);
+       }
+       goto done;
+
+bail1:
+       entry->deleted = 1;
+       remove_hash(entry);
+       unlock_entry(entry);
+bail2:
+       hfs_cat_put(entry);
+done:
+       end_write(parent);
+       return error;
+}
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_cat_put()
+ *
+ * Release an entry we aren't using anymore.
+ *
+ * NOTE: We must be careful any time we sleep on a non-deleted
+ * entry that the entry is in a consistent state, since another
+ * process may get the entry while we sleep. That is why we
+ * 'goto repeat' after each operation that might sleep.
+ */
+void hfs_cat_put(struct hfs_cat_entry * entry)
+{
+       if (!entry) {
+               return;
+       }
+       wait_on_entry(entry);
+       if (!entry->count) {
+               hfs_warn("hfs_cat_put: trying to free free entry\n");
+               return;
+       }
+repeat:
+       if (entry->count > 1) {
+               entry->count--;
+               return;
+       }
+
+       if (!entry->cnid) {
+               clear_entry(entry);
+               return;
+       }
+
+       if (entry->type == HFS_CDR_FIL) {
+               if (entry->deleted) {
+                       /* free all extents */
+                       entry->u.file.data_fork.lsize = 0;
+                       hfs_extent_adj(&entry->u.file.data_fork);
+                       entry->u.file.rsrc_fork.lsize = 0;
+                       hfs_extent_adj(&entry->u.file.rsrc_fork);
+               } else {
+                       /* clear out any cached extents */
+                       if (entry->u.file.data_fork.first.next) {
+                               hfs_extent_free(&entry->u.file.data_fork);
+                               wait_on_entry(entry);
+                               goto repeat;
+                       }
+                       if (entry->u.file.rsrc_fork.first.next) {
+                               hfs_extent_free(&entry->u.file.rsrc_fork);
+                               wait_on_entry(entry);
+                               goto repeat;
+                       }
+               }
+       }
+
+       if (entry->deleted) {
+               clear_entry(entry);
+               return;
+       }
+
+       if (entry->dirt) {
+               write_entry(entry);
+               goto repeat;
+       }
+
+       entry->count--;
+       nr_free_entries++;
+
+       /* get_empty_entry() could be blocked waiting for more entries */
+       hfs_wake_up(&entry_wait);
+
+       return;
+}
+
+/*
+ * hfs_cat_get()
+ *
+ * Wrapper for get_entry() which always calls with ('read'==1).
+ * Used for access to get_entry() from outside this file.
+ */
+struct hfs_cat_entry *hfs_cat_get(struct hfs_mdb *mdb,
+                                 const struct hfs_cat_key *key)
+{
+       return get_entry(mdb, key, 1);
+}
+
+/*
+ * hfs_cat_invalidate()
+ *
+ * Called by hfs_mdb_put() to remove all the entries
+ * in the cache which are associated with a given MDB.
+ */
+void hfs_cat_invalidate(struct hfs_mdb *mdb)
+{
+       struct hfs_cat_entry * entry, * next;
+       int i;
+
+       next = first_entry;
+       for (i = nr_entries ; i > 0 ; i--) {
+               entry = next;
+               next = entry->next;     /* clear_entry() changes the queues.. */
+               if (entry->mdb != mdb) {
+                       continue;
+               }
+               if (entry->count || entry->dirt || entry->lock) {
+                       hfs_warn("hfs_fs: entry busy on removed device %s.\n",
+                                hfs_mdb_name(entry->mdb->sys_mdb));
+                       continue;
+               }
+               clear_entry(entry);
+       }
+}
+
+/*
+ * hfs_cat_commit()
+ *
+ * Called by hfs_mdb_commit() to write dirty entries to the disk buffers.
+ */
+void hfs_cat_commit(struct hfs_mdb *mdb)
+{
+       int i;
+       struct hfs_cat_entry * entry;
+
+       entry = first_entry;
+       for(i = 0; i < nr_entries*2; i++, entry = entry->next) {
+               if (mdb && entry->mdb != mdb) {
+                       continue;
+               }
+               if (!entry->cnid || entry->deleted) {
+                       continue;
+               }
+               wait_on_entry(entry);
+               if (entry->dirt) {
+                       write_entry(entry);
+               }
+       }
+}
+
+/*
+ * hfs_cat_free()
+ *
+ * Releases all the memory allocated in grow_entries().
+ * Must call hfs_cat_invalidate() on all MDBs before calling this.
+ */
+void hfs_cat_free(void)
+{
+       struct allocation_unit *tmp;
+
+       while (allocation) {
+               tmp = allocation->next;
+               HFS_DELETE(allocation);
+               allocation = tmp;
+       }
+}
+
+/*
+ * hfs_cat_compare()
+ *
+ * Description:
+ *   This is the comparison function used for the catalog B-tree.  In
+ *   comparing catalog B-tree entries, the parent id is the most
+ *   significant field (compared as unsigned ints).  The name field is
+ *   the least significant (compared in "Macintosh lexical order",
+ *   see hfs_strcmp() in string.c)
+ * Input Variable(s):
+ *   struct hfs_cat_key *key1: pointer to the first key to compare
+ *   struct hfs_cat_key *key2: pointer to the second key to compare
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   int: negative if key1<key2, positive if key1>key2, and 0 if key1==key2
+ * Preconditions:
+ *   key1 and key2 point to "valid" (struct hfs_cat_key)s.
+ * Postconditions:
+ *   This function has no side-effects
+ */
+int hfs_cat_compare(const struct hfs_cat_key *key1,
+                   const struct hfs_cat_key *key2)
+{
+       unsigned int parents;
+       int retval;
+
+       parents = hfs_get_hl(key1->ParID) - hfs_get_hl(key2->ParID);
+       if (parents != 0) {
+               retval = (int)parents;
+       } else {
+               retval = hfs_strcmp(&key1->CName, &key2->CName);
+       }
+       return retval;
+}
+
+/*
+ * hfs_cat_build_key()
+ *
+ * Given the ID of the parent and the name build a search key.
+ */
+void hfs_cat_build_key(hfs_u32 parent, const struct hfs_name *cname,
+                      struct hfs_cat_key *key)
+{
+       hfs_put_nl(parent, key->ParID);
+
+       if (cname) {
+               key->KeyLen = 6 + cname->Len;
+               memcpy(&key->CName, cname, sizeof(*cname));
+       } else {
+               key->KeyLen = 6;
+               memset(&key->CName, 0, sizeof(*cname));
+       }
+}
+
+/*
+ * hfs_cat_open()
+ *
+ * Given a directory on an HFS filesystem get its thread and
+ * lock the directory against insertions and deletions.
+ * Return 0 on success or an error code on failure.
+ */
+int hfs_cat_open(struct hfs_cat_entry *dir, struct hfs_brec *brec)
+{
+       struct hfs_cat_key key;
+       int error;
+
+       if (dir->type != HFS_CDR_DIR) {
+               return -EINVAL;
+       }
+
+       /* Block writers */
+       start_read(dir);
+
+       /* Find the directory */
+       hfs_cat_build_key(dir->cnid, NULL, &key);
+       error = hfs_bfind(brec, dir->mdb->cat_tree,
+                         HFS_BKEY(&key), HFS_BFIND_READ_EQ);
+
+       if (error) {
+               end_read(dir);
+       }
+
+       return error;
+}
+
+/*
+ * hfs_cat_next()
+ *
+ * Given a catalog brec structure, replace it with the count'th next brec
+ * in the same directory.
+ * Return an error code if there is a problem, 0 if OK.
+ * Note that an error code of -ENOENT means there are no more entries
+ * in this directory.
+ * The directory is "closed" on an error.
+ */
+int hfs_cat_next(struct hfs_cat_entry *dir, struct hfs_brec *brec,
+                hfs_u16 count, hfs_u32 *cnid, hfs_u8 *type)
+{
+       int error;
+
+       if (!dir || !brec) {
+               return -EINVAL;
+       }
+
+       /* Get the count'th next catalog tree entry */
+       error = hfs_bsucc(brec, count);
+       if (!error) {
+               struct hfs_cat_key *key = (struct hfs_cat_key *)brec->key;
+               if (hfs_get_nl(key->ParID) != dir->cnid) {
+                       hfs_brec_relse(brec, NULL);
+                       error = -ENOENT;
+               }
+       }
+       if (!error) {
+               *type = ((struct hfs_cat_rec *)brec->data)->cdrType;
+               *cnid = brec_to_id(brec);
+       } else {
+               end_read(dir);
+       }
+       return error;
+}
+
+/*
+ * hfs_cat_close()
+ *
+ * Given a catalog brec structure, replace it with the count'th next brec
+ * in the same directory.
+ * Return an error code if there is a problem, 0 if OK.
+ * Note that an error code of -ENOENT means there are no more entries
+ * in this directory.
+ */
+void hfs_cat_close(struct hfs_cat_entry *dir, struct hfs_brec *brec)
+{
+       if (dir && brec) {
+               hfs_brec_relse(brec, NULL);
+               end_read(dir);
+       }
+}
+
+/*
+ * hfs_cat_parent()
+ *
+ * Given a catalog entry, return the entry for its parent.
+ * Uses catalog key for the entry to get its parent's ID
+ * and then uses the parent's thread record to locate the
+ * parent's actual catalog entry.
+ */
+struct hfs_cat_entry *hfs_cat_parent(struct hfs_cat_entry *entry)
+{
+       struct hfs_cat_entry *retval = NULL;
+       struct hfs_mdb *mdb = entry->mdb;
+       struct hfs_brec brec;
+       struct hfs_cat_key key;
+       int error;
+
+       lock_entry(entry);
+       if (!entry->deleted) {
+               hfs_cat_build_key(hfs_get_nl(entry->key.ParID), NULL, &key);
+               error = hfs_bfind(&brec, mdb->cat_tree,
+                                 HFS_BKEY(&key), HFS_BFIND_READ_EQ);
+               if (!error) {
+                       /* convert thread record to key */
+                       struct hfs_cat_rec *rec = brec.data;
+                       key.KeyLen = 6 + rec->u.thd.CName.Len;
+                       memcpy(&key.ParID, &rec->u.thd.ParID,
+                                      sizeof(hfs_u32) + sizeof(struct hfs_name));
+
+                       hfs_brec_relse(&brec, NULL);
+
+                       retval = hfs_cat_get(mdb, &key);
+               }
+       }
+       unlock_entry(entry);
+       return retval;
+}
+
+/*
+ * hfs_cat_create()
+ *
+ * Create a new file with the indicated name in the indicated directory.
+ * The file will have the indicated flags, type and creator.
+ * If successful an (struct hfs_cat_entry) is returned in '*result'.
+ */
+int hfs_cat_create(struct hfs_cat_entry *parent, struct hfs_cat_key *key,
+                  hfs_u8 flags, hfs_u32 type, hfs_u32 creator,
+                  struct hfs_cat_entry **result)
+{
+       struct hfs_cat_rec record;
+       hfs_u32 id = new_cnid(parent->mdb);
+       hfs_u32 mtime = hfs_time();
+
+       /* init some fields for the file record */
+       memset(&record, 0, sizeof(record));
+       record.cdrType = HFS_CDR_FIL;
+       record.u.fil.Flags = flags | HFS_FIL_USED;
+       hfs_put_nl(id,      record.u.fil.FlNum);
+       hfs_put_nl(mtime,   record.u.fil.CrDat);
+       hfs_put_nl(mtime,   record.u.fil.MdDat);
+       hfs_put_nl(0,       record.u.fil.BkDat);
+       hfs_put_nl(type,    record.u.fil.UsrWds.fdType);
+       hfs_put_nl(creator, record.u.fil.UsrWds.fdCreator);
+
+       return create_entry(parent, key, &record, 0, id, result);
+}
+
+/*
+ * hfs_cat_mkdir()
+ *
+ * Create a new directory with the indicated name in the indicated directory.
+ * If successful an (struct hfs_cat_entry) is returned in '*result'.
+ */
+int hfs_cat_mkdir(struct hfs_cat_entry *parent, struct hfs_cat_key *key,
+                 struct hfs_cat_entry **result)
+{
+       struct hfs_cat_rec record;
+       hfs_u32 id = new_cnid(parent->mdb);
+       hfs_u32 mtime = hfs_time();
+
+       /* init some fields for the directory record */
+       memset(&record, 0, sizeof(record));
+       record.cdrType = HFS_CDR_DIR;
+       hfs_put_nl(id,     record.u.dir.DirID);
+       hfs_put_nl(mtime, record.u.dir.CrDat);
+       hfs_put_nl(mtime, record.u.dir.MdDat);
+       hfs_put_nl(0,     record.u.dir.BkDat);
+       hfs_put_hs(0xff,  record.u.dir.UsrInfo.frView);
+
+       return create_entry(parent, key, &record, 1, id, result);
+}
+
+/*
+ * hfs_cat_delete()
+ *
+ * Delete the indicated file or directory.
+ * The associated thread is also removed unless ('with_thread'==0).
+ */
+int hfs_cat_delete(struct hfs_cat_entry *parent, struct hfs_cat_entry *entry,
+                  int with_thread)
+{
+       struct hfs_cat_key key;
+       struct hfs_mdb *mdb = parent->mdb;
+       int is_dir, error = 0;
+
+       if (parent->mdb != entry->mdb) {
+               return -EINVAL;
+       }
+
+       if (entry->type == HFS_CDR_FIL) {
+               with_thread = (entry->u.file.flags&HFS_FIL_THD) && with_thread;
+               is_dir = 0;
+       } else {
+               is_dir = 1;
+       }
+
+       /* keep readers from getting confused by changing dir size */
+       start_write(parent);
+
+       /* don't delete a busy directory */
+       if (entry->type == HFS_CDR_DIR) {
+               start_read(entry);
+
+               if (entry->u.dir.files || entry->u.dir.dirs) {
+                       error = -ENOTEMPTY;
+               }
+       }
+
+       /* try to delete the file or directory */
+       if (!error) {
+               lock_entry(entry);
+               if (entry->deleted) {
+                       /* somebody beat us to it */
+                       error = -ENOENT;
+               } else {
+                       error = hfs_bdelete(mdb->cat_tree,
+                                           HFS_BKEY(&entry->key));
+               }
+               unlock_entry(entry);
+       }
+
+       if (!error) {
+               /* Mark the entry deleted and remove it from the cache */
+               entry->deleted = 1;
+               remove_hash(entry);
+
+               /* try to delete the thread entry if it exists */
+               if (with_thread) {
+                       hfs_cat_build_key(entry->cnid, NULL, &key);
+                       (void)hfs_bdelete(mdb->cat_tree, HFS_BKEY(&key));
+               }
+
+               update_dir(mdb, parent, is_dir, -1);
+       }
+
+       if (entry->type == HFS_CDR_DIR) {
+               end_read(entry);
+       }
+       end_write(parent);
+       return error;
+}
+
+/*
+ * hfs_cat_move()
+ *
+ * Rename a file or directory, possibly to a new directory.
+ * If the destination exists it is removed and a
+ * (struct hfs_cat_entry) for it is returned in '*result'.
+ */
+int hfs_cat_move(struct hfs_cat_entry *old_dir, struct hfs_cat_entry *new_dir,
+                struct hfs_cat_entry *entry, struct hfs_cat_key *new_key,
+                struct hfs_cat_entry **removed)
+{
+       struct hfs_cat_entry *dest;
+       struct hfs_mdb *mdb;
+       int error = 0;
+       int is_dir, has_thread;
+
+       if (removed) {
+               *removed = NULL;
+       }
+
+       /* sanity checks */
+       if (!old_dir || !new_dir) {
+               return -EINVAL;
+       }
+       mdb = old_dir->mdb;
+       if (mdb != new_dir->mdb) {
+               return -EXDEV;
+       }
+
+       /* precompute a few things */
+       if (entry->type == HFS_CDR_DIR) {
+               is_dir = 1;
+               has_thread = 1;
+       } else if (entry->type == HFS_CDR_FIL) {
+               is_dir = 0;
+               has_thread = entry->u.file.flags & HFS_FIL_THD;
+       } else {
+               return -EINVAL;
+       }
+
+       while (mdb->rename_lock) {
+               hfs_sleep_on(&mdb->rename_wait);
+       }
+       mdb->rename_lock = 1;
+
+       /* keep readers from getting confused by changing dir size */
+       start_write(new_dir);
+       if (old_dir != new_dir) {
+               start_write(old_dir);
+       }
+
+       /* Don't move a directory inside itself */
+       if (is_dir) {
+               struct hfs_cat_key thd_key;
+               struct hfs_brec brec;
+
+               hfs_u32 id = new_dir->cnid;
+               while (id != htonl(HFS_ROOT_CNID)) {
+                       if (id == entry->cnid) {
+                               error = -EINVAL;
+                       } else {
+                               hfs_cat_build_key(id, NULL, &thd_key);
+                               error = hfs_bfind(&brec, mdb->cat_tree,
+                                                 HFS_BKEY(&thd_key),
+                                                 HFS_BFIND_READ_EQ);
+                       }
+                       if (error) {
+                               goto done;
+                       } else {
+                               struct hfs_cat_rec *rec = brec.data;
+                               id = hfs_get_nl(rec->u.thd.ParID);
+                               hfs_brec_relse(&brec, NULL);
+                       }
+               }
+       }
+
+restart:
+       /* see if the destination exists, getting it if it does */
+       dest = hfs_cat_get(mdb, new_key);
+
+       if (!dest) {
+               /* destination doesn't exist, so create it */
+               struct hfs_cat_rec new_record;
+
+               /* create a locked entry in the cache */
+               dest = get_entry(mdb, new_key, 0);
+               if (!dest) {
+                       error = -EIO;
+                       goto done;
+               }
+               if (dest->cnid) {
+                       /* The (unlocked) entry exists in the cache */
+                       goto have_distinct;
+               }
+
+               /* limit directory valence to signed 16-bit integer */
+               if ((new_dir->u.dir.dirs + new_dir->u.dir.files) >=
+                                                       HFS_MAX_VALENCE) {
+                       error = -ENOSPC;
+                       goto bail3;
+               }
+
+               /* build the new record */
+               new_record.cdrType = entry->type;
+               __write_entry(entry, &new_record);
+
+               /* insert the new record */
+               error = hfs_binsert(mdb->cat_tree, HFS_BKEY(new_key),
+                                   &new_record, is_dir ? 2 + sizeof(DIR_REC) :
+                                                         2 + sizeof(FIL_REC));
+               if (error == -EEXIST) {
+                       dest->deleted = 1;
+                       remove_hash(dest);
+                       unlock_entry(dest);
+                       hfs_cat_put(dest);
+                       goto restart;
+               } else if (error) {
+                       goto bail3;
+               }
+
+               /* update the destination directory */
+               update_dir(mdb, new_dir, is_dir, 1);
+       } else if (entry != dest) {
+have_distinct:
+               /* The destination exists and is not same as source */
+               lock_entry(dest);
+               if (dest->deleted) {
+                       unlock_entry(dest);
+                       hfs_cat_put(dest);
+                       goto restart;
+               }
+               if (dest->type != entry->type) {
+                       /* can't move a file on top
+                          of a dir nor vice versa. */
+                       error = is_dir ? -ENOTDIR : -EISDIR;
+               } else if (is_dir && (dest->u.dir.dirs || dest->u.dir.files)) {
+                       /* directory to replace is not empty */
+                       error = -ENOTEMPTY;
+               }
+
+               if (error) {
+                       goto bail2;
+               }
+       } else {
+               /* The destination exists but is same as source */
+               --entry->count;
+               dest = NULL;
+       }
+
+       /* lock the entry */
+       lock_entry(entry);
+       if (entry->deleted) {
+               error = -ENOENT;
+               goto bail1;
+       }
+
+       if (dest) {
+               /* remove the old entry */
+               error = hfs_bdelete(mdb->cat_tree, HFS_BKEY(&entry->key));
+
+               if (error) {
+                       /* We couldn't remove the entry for the
+                          original file, so nothing has changed. */
+                       goto bail1;
+               }
+               update_dir(mdb, old_dir, is_dir, -1);
+       }
+
+       /* update the thread of the dir/file we're moving */
+       if (has_thread) {
+               struct hfs_cat_key thd_key;
+               struct hfs_brec brec;
+
+               hfs_cat_build_key(entry->cnid, NULL, &thd_key);
+               error = hfs_bfind(&brec, mdb->cat_tree,
+                                 HFS_BKEY(&thd_key), HFS_BFIND_WRITE);
+               if (error == -ENOENT) {
+                       if (is_dir) {
+                               /* directory w/o a thread! */
+                               error = -EIO;
+                       } else {
+                               /* We were lied to! */
+                               entry->u.file.flags &= ~HFS_FIL_THD;
+                               entry->dirt = 1;
+                       }
+               }
+               if (!error) {
+                       struct hfs_cat_rec *rec = brec.data;
+                       memcpy(&rec->u.thd.ParID, &new_key->ParID,
+                              sizeof(hfs_u32) + sizeof(struct hfs_name));
+                       hfs_brec_relse(&brec, NULL);
+               } else if (error == -ENOENT) {
+                       error = 0;
+               } else if (!dest) {
+                       /* Nothing was changed */
+                       unlock_entry(entry);
+                       goto done;
+               } else {
+                       /* Something went seriously wrong.
+                          The dir/file has been deleted. */
+                       /* XXX try some recovery? */
+                       entry->deleted = 1;
+                       remove_hash(entry);
+                       goto bail1;
+               }
+       }
+
+       /* TRY to remove the thread for the pre-existing entry */
+       if (dest && dest->cnid &&
+           (is_dir || (dest->u.file.flags & HFS_FIL_THD))) {
+               struct hfs_cat_key thd_key;
+
+               hfs_cat_build_key(dest->cnid, NULL, &thd_key);
+               (void)hfs_bdelete(mdb->cat_tree, HFS_BKEY(&thd_key));
+       }
+
+       /* update directories */
+       new_dir->modify_date = hfs_time();
+       new_dir->dirt = 1;
+
+       /* update key */
+       remove_hash(entry);
+       memcpy(&entry->key, new_key, sizeof(*new_key));
+       entry->key_dirt = 1; /* Since case might differ */
+       entry->dirt = 1;
+       insert_hash(entry);
+       unlock_entry(entry);
+
+       /* delete any pre-existing or place-holder entry */
+       if (dest) {
+               dest->deleted = 1;
+               remove_hash(dest);
+               unlock_entry(dest);
+               if (removed && dest->cnid) {
+                       *removed = dest;
+               } else {
+                       hfs_cat_put(dest);
+               }
+       }
+       goto done;
+
+bail1:
+       unlock_entry(entry);
+bail2:
+       if (dest) {
+               if (!dest->cnid) {
+                       /* TRY to remove the new entry */
+                       (void)hfs_bdelete(mdb->cat_tree, HFS_BKEY(new_key));
+                       update_dir(mdb, new_dir, is_dir, -1);
+bail3:
+                       dest->deleted = 1;
+                       remove_hash(dest);
+               }
+               unlock_entry(dest);
+               hfs_cat_put(dest);
+       }
+done:
+       if (new_dir != old_dir) {
+               end_write(old_dir);
+       }
+       end_write(new_dir);
+       mdb->rename_lock = 0;
+       hfs_wake_up(&mdb->rename_wait);
+
+       return error;
+}
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/fs/hfs/dir.c linux-2.0.29/fs/hfs/dir.c
--- linux.vanilla/fs/hfs/dir.c  Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/fs/hfs/dir.c   Thu Apr 10 12:25:59 1997
@@ -0,0 +1,379 @@
+/*
+ * linux/fs/hfs/dir.c
+ *
+ * Copyright (C) 1995-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains directory-related functions independent of which
+ * scheme is being used to represent forks.
+ *
+ * Based on the minix file system code, (C) 1991, 1992 by Linus Torvalds
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include <linux/hfs.h>
+#include <linux/hfs_fs_sb.h>
+#include <linux/hfs_fs_i.h>
+#include <linux/hfs_fs.h>
+
+/*================ File-local functions ================*/
+
+/*
+ * build_key()
+ *
+ * Build a key for a file by the given name in the given directory.
+ * If the name matches one of the reserved names returns 1 otherwise 0.
+ */
+static int build_key(struct hfs_cat_key *key, struct inode *dir,
+                    const char *name, int len)
+{
+       struct hfs_name cname;
+       const struct hfs_name *reserved;
+
+       /* mangle the name */
+       hfs_nameout(dir, &cname, name, len);
+
+       /* check against reserved names */
+       reserved = HFS_SB(dir->i_sb)->s_reserved1;
+       while (reserved->Len) {
+               if (hfs_streq(reserved, &cname)) {
+                       return 1;
+               }
+               ++reserved;
+       }
+
+       /* check against the names reserved only in the root directory */
+       if (HFS_I(dir)->entry->cnid == htonl(HFS_ROOT_CNID)) {
+               reserved = HFS_SB(dir->i_sb)->s_reserved2;
+               while (reserved->Len) {
+                       if (hfs_streq(reserved, &cname)) {
+                               return 1;
+                       }
+                       ++reserved;
+               }
+       }
+
+       /* build the key */
+       hfs_cat_build_key(HFS_I(dir)->entry->cnid, &cname, key);
+
+       return 0;
+}
+
+/*
+ * update_dirs_plus()
+ *
+ * Update the fields 'i_size', 'i_nlink', 'i_ctime', 'i_mtime' and
+ * 'i_version' of the inodes associated with a directory that has
+ * had a file ('is_dir'==0) or directory ('is_dir'!=0) added to it.
+ */
+static inline void update_dirs_plus(struct hfs_cat_entry *dir, int is_dir)
+{
+       int i;
+
+       for (i = 0; i < 4; ++i) {
+               struct inode *tmp = dir->sys_entry[i];
+               if (tmp) {
+                       if (S_ISDIR(tmp->i_mode)) {
+                               if (is_dir &&
+                                   (i == HFS_ITYPE_TO_INT(HFS_ITYPE_NORM))) {
+                                       /* In "normal" directory only */
+                                       ++(tmp->i_nlink);
+                               }
+                               tmp->i_size += HFS_I(tmp)->dir_size;
+                               tmp->i_version = ++event;
+                       }
+                       tmp->i_ctime = tmp->i_mtime = CURRENT_TIME;
+               }
+       }
+}
+
+/*
+ * update_dirs_plus()
+ *
+ * Update the fields 'i_size', 'i_nlink', 'i_ctime', 'i_mtime' and
+ * 'i_version' of the inodes associated with a directory that has
+ * had a file ('is_dir'==0) or directory ('is_dir'!=0) removed.
+ */
+static inline void update_dirs_minus(struct hfs_cat_entry *dir, int is_dir)
+{
+       int i;
+
+       for (i = 0; i < 4; ++i) {
+               struct inode *tmp = dir->sys_entry[i];
+               if (tmp) {
+                       if (S_ISDIR(tmp->i_mode)) {
+                               if (is_dir &&
+                                   (i == HFS_ITYPE_TO_INT(HFS_ITYPE_NORM))) {
+                                       /* In "normal" directory only */
+                                       --(tmp->i_nlink);
+                               }
+                               tmp->i_size -= HFS_I(tmp)->dir_size;
+                               tmp->i_version = ++event;
+                       }
+                       tmp->i_ctime = tmp->i_mtime = CURRENT_TIME;
+               }
+       }
+}
+
+/*
+ * mark_inodes_deleted()
+ *
+ * Update inodes associated with a deleted entry to reflect its deletion.
+ */
+static inline void mark_inodes_deleted(struct hfs_cat_entry *entry, int is_dir)
+{
+       int i;
+
+       for (i = 0; i < 4; ++i) {
+               struct inode *tmp = entry->sys_entry[i];
+               if (tmp) {
+                       if (is_dir && S_ISDIR(tmp->i_mode)) {
+                               tmp->i_size = 0;
+                       }
+                       tmp->i_nlink = 0;
+                       tmp->i_ctime = CURRENT_TIME;
+               }
+       }
+}
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_dir_read()
+ *
+ * This is the read() entry in the file_operations structure for HFS
+ * directories.         It simply returns an error code, since reading is not
+ * supported.
+ */
+hfs_rwret_t hfs_dir_read(struct inode * inode, struct file * filp,
+                        char *buf, hfs_rwarg_t count)
+{
+       return -EISDIR;
+}
+
+/*
+ * hfs_create()
+ *
+ * This is the create() entry in the inode_operations structure for
+ * regular HFS directories.  The purpose is to create a new file in
+ * a directory and return a corresponding inode, given the inode for
+ * the directory and the name (and its length) of the new file.
+ */
+int hfs_create(struct inode * dir, const char * name, int len, int mode,
+              struct inode ** result)
+{
+       struct hfs_cat_entry *entry = HFS_I(dir)->entry;
+       struct hfs_cat_entry *new;
+       struct hfs_cat_key key;
+       int error;
+
+       *result = NULL;
+
+       /* build the key, checking against reserved names */
+       if (build_key(&key, dir, name, len)) {
+               error = -EEXIST;
+       } else {
+               /* try to create the file */
+               error = hfs_cat_create(entry, &key,
+                                      (mode & S_IWUSR) ? 0 : HFS_FIL_LOCK,
+                                      HFS_SB(dir->i_sb)->s_type,
+                                      HFS_SB(dir->i_sb)->s_creator, &new);
+       }
+
+       if (!error) {
+               update_dirs_plus(entry, 0);
+
+               /* create an inode for the new file */
+               *result = __hfs_iget(new, HFS_I(dir)->file_type, 0);
+               if (!(*result)) {
+                       /* XXX correct error? */
+                       error = -EIO;
+               }
+       }
+
+       iput(dir);
+       return error;
+}
+
+/*
+ * hfs_mkdir()
+ *
+ * This is the mkdir() entry in the inode_operations structure for
+ * regular HFS directories.  The purpose is to create a new directory
+ * in a directory, given the inode for the parent directory and the
+ * name (and its length) of the new directory.
+ */
+int hfs_mkdir(struct inode * parent, const char * name, int len, int mode)
+{
+       struct hfs_cat_entry *entry = HFS_I(parent)->entry;
+       struct hfs_cat_key key;
+       int error;
+
+       /* build the key, checking against reserved names */
+       if (build_key(&key, parent, name, len)) {
+               error = -EEXIST;
+       } else {
+               /* try to create the directory */
+               error = hfs_cat_mkdir(entry, &key, NULL);
+       }
+
+       if (!error) {
+               update_dirs_plus(entry, 1);
+       }
+
+       iput(parent);
+       return error;
+}
+
+/*
+ * hfs_mknod()
+ *
+ * This is the mknod() entry in the inode_operations structure for
+ * regular HFS directories.  The purpose is to create a new entry
+ * in a directory, given the inode for the parent directory and the
+ * name (and its length) and the mode of the new entry (and the device
+ * number if the entry is to be a device special file).
+ *
+ * HFS only supports regular files and directories and Linux disallows
+ * using mknod() to create directories.  Thus we just check the arguments
+ * and call hfs_create().
+ */
+int hfs_mknod(struct inode *dir, const char *name, int len, int mode, int rdev)
+{
+       int error;
+       struct inode *inode;
+
+       if (!dir) {
+               error = -ENOENT;
+       } else if (S_ISREG(mode)) {
+               error = hfs_create(dir, name, len, mode, &inode);
+               if (!error) {
+                       iput(inode);
+               }
+       } else {
+               error = -EPERM;
+       }
+       return error;
+}
+
+/*
+ * hfs_unlink()
+ *
+ * This is the unlink() entry in the inode_operations structure for
+ * regular HFS directories.  The purpose is to delete an existing
+ * file, given the inode for the parent directory and the name
+ * (and its length) of the existing file.
+ */
+int hfs_unlink(struct inode * dir, const char * name, int len)
+{
+       struct hfs_cat_entry *entry = HFS_I(dir)->entry;
+       struct hfs_cat_entry *victim = NULL;
+       struct hfs_cat_key key;
+       int error;
+
+       if (build_key(&key, dir, name, len)) {
+               error = -EPERM;
+       } else if (!(victim = hfs_cat_get(entry->mdb, &key))) {
+               error = -ENOENT;
+       } else if (victim->type != HFS_CDR_FIL) {
+               error = -EPERM;
+       } else if (!(error = hfs_cat_delete(entry, victim, 1))) {
+               mark_inodes_deleted(victim, 0);
+               update_dirs_minus(entry, 0);
+       }
+
+       hfs_cat_put(victim);    /* Note that hfs_cat_put(NULL) is safe. */
+       iput(dir);
+       return error;
+}
+
+/*
+ * hfs_rmdir()
+ *
+ * This is the rmdir() entry in the inode_operations structure for
+ * regular HFS directories.  The purpose is to delete an existing
+ * directory, given the inode for the parent directory and the name
+ * (and its length) of the existing directory.
+ */
+int hfs_rmdir(struct inode * parent, const char * name, int len)
+{
+       struct hfs_cat_entry *entry = HFS_I(parent)->entry;
+       struct hfs_cat_entry *victim = NULL;
+       struct hfs_cat_key key;
+       int error;
+
+       if (build_key(&key, parent, name, len)) {
+               error = -EPERM;
+       } else if (!(victim = hfs_cat_get(entry->mdb, &key))) {
+               error = -ENOENT;
+       } else if ((entry == victim) ||
+                  (entry->sys_entry[0] && entry->sys_entry[0]->i_mount) ||
+                  (entry->sys_entry[1] && entry->sys_entry[1]->i_mount) ||
+                  (entry->sys_entry[2] && entry->sys_entry[2]->i_mount) ||
+                  (entry->sys_entry[3] && entry->sys_entry[3]->i_mount)) {
+               error = -EPERM;
+       } else if (victim->type != HFS_CDR_DIR) {
+               error = -ENOTDIR;
+       } else if (!(error = hfs_cat_delete(entry, victim, 1))) {
+               mark_inodes_deleted(victim, 1);
+               update_dirs_minus(entry, 1);
+       }
+
+       hfs_cat_put(victim);    /* Note that hfs_cat_put(NULL) is safe. */
+       iput(parent);
+       return error;
+}
+
+/*
+ * hfs_rename()
+ *
+ * This is the rename() entry in the inode_operations structure for
+ * regular HFS directories.  The purpose is to rename an existing
+ * file or directory, given the inode for the current directory and
+ * the name (and its length) of the existing file/directory and the
+ * inode for the new directory and the name (and its length) of the
+ * new file/directory.
+ */
+int hfs_rename(struct inode *old_dir, const char * old_name, int old_len,
+              struct inode *new_dir, const char * new_name, int new_len,
+              int must_be_dir)
+{
+       struct hfs_cat_entry *old_parent = HFS_I(old_dir)->entry;
+       struct hfs_cat_entry *new_parent = HFS_I(new_dir)->entry;
+       struct hfs_cat_entry *victim = NULL;
+       struct hfs_cat_entry *deleted;
+       struct hfs_cat_key key;
+       int error;
+
+       if (build_key(&key, old_dir, old_name, old_len) ||
+           (HFS_ITYPE(old_dir->i_ino) != HFS_ITYPE(new_dir->i_ino))) {
+               error = -EPERM;
+       } else if (!(victim = hfs_cat_get(old_parent->mdb, &key))) {
+               error = -ENOENT;
+       } else if (must_be_dir && (victim->type != HFS_CDR_DIR)) {
+               error = -ENOENT;
+       } else if (build_key(&key, new_dir, new_name, new_len)) {
+               error = -EPERM;
+       } else if (!(error = hfs_cat_move(old_parent, new_parent,
+                                         victim, &key, &deleted))) {
+               int is_dir = (victim->type == HFS_CDR_DIR);
+
+               update_dirs_minus(old_parent, is_dir);
+               if (deleted) {
+                       mark_inodes_deleted(deleted, is_dir);
+                       hfs_cat_put(deleted);
+               } else {
+                       update_dirs_plus(new_parent, is_dir);
+               }
+       }
+
+       hfs_cat_put(victim);    /* Note that hfs_cat_put(NULL) is safe. */
+       iput(old_dir);
+       iput(new_dir);
+       return error;
+}
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/fs/hfs/dir_cap.c linux-2.0.29/fs/hfs/dir_cap.c
--- linux.vanilla/fs/hfs/dir_cap.c      Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/fs/hfs/dir_cap.c       Thu Apr 10 12:26:14 1997
@@ -0,0 +1,368 @@
+/*
+ * linux/fs/hfs/dir_cap.c
+ *
+ * Copyright (C) 1995-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the inode_operations and file_operations
+ * structures for HFS directories under the CAP scheme.
+ *
+ * Based on the minix file system code, (C) 1991, 1992 by Linus Torvalds
+ *
+ * The source code distribution of the Columbia AppleTalk Package for
+ * UNIX, version 6.0, (CAP) was used as a specification of the
+ * location and format of files used by CAP's Aufs.  No code from CAP
+ * appears in hfs_fs.  hfs_fs is not a work ``derived'' from CAP in
+ * the sense of intellectual property law.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include <linux/hfs.h>
+#include <linux/hfs_fs_sb.h>
+#include <linux/hfs_fs_i.h>
+#include <linux/hfs_fs.h>
+
+/*================ Forward declarations ================*/
+
+static int cap_lookup(struct inode *, const char *, int, struct inode **);
+static int cap_readdir(struct inode *, struct file *, void *, filldir_t);
+
+/*================ Global variables ================*/
+
+#define DOT_LEN                        1
+#define DOT_DOT_LEN            2
+#define DOT_RESOURCE_LEN       9
+#define DOT_FINDERINFO_LEN     11
+#define DOT_ROOTINFO_LEN       9
+
+const struct hfs_name hfs_cap_reserved1[] = {
+       {DOT_LEN,               "."},
+       {DOT_DOT_LEN,           ".."},
+       {DOT_RESOURCE_LEN,      ".resource"},
+       {DOT_FINDERINFO_LEN,    ".finderinfo"},
+       {0,                     ""},
+};
+
+const struct hfs_name hfs_cap_reserved2[] = {
+       {DOT_ROOTINFO_LEN,      ".rootinfo"},
+       {0,                     ""},
+};
+
+#define DOT            (&hfs_cap_reserved1[0])
+#define DOT_DOT                (&hfs_cap_reserved1[1])
+#define DOT_RESOURCE   (&hfs_cap_reserved1[2])
+#define DOT_FINDERINFO (&hfs_cap_reserved1[3])
+#define DOT_ROOTINFO   (&hfs_cap_reserved2[0])
+
+static struct file_operations hfs_cap_dir_operations = {
+       NULL,                   /* lseek - default */
+       hfs_dir_read,           /* read - invalid */
+       NULL,                   /* write - bad */
+       cap_readdir,            /* readdir */
+       NULL,                   /* select - default */
+       NULL,                   /* ioctl - default */
+       NULL,                   /* mmap - none */
+       NULL,                   /* no special open code */
+       NULL,                   /* no special release code */
+       file_fsync,             /* fsync - default */
+       NULL,                   /* fasync - default */
+       NULL,                   /* check_media_change - none */
+       NULL                    /* revalidate - none */
+};
+
+struct inode_operations hfs_cap_ndir_inode_operations = {
+       &hfs_cap_dir_operations,/* default directory file-ops */
+       hfs_create,             /* create */
+       cap_lookup,             /* lookup */
+       NULL,                   /* link */
+       hfs_unlink,             /* unlink */
+       NULL,                   /* symlink */
+       hfs_mkdir,              /* mkdir */
+       hfs_rmdir,              /* rmdir */
+       hfs_mknod,              /* mknod */
+       hfs_rename,             /* rename */
+       NULL,                   /* readlink */
+       NULL,                   /* follow_link */
+       NULL,                   /* readpage */
+       NULL,                   /* writepage */
+       NULL,                   /* bmap */
+       NULL,                   /* truncate */
+       NULL,                   /* permission */
+       NULL                    /* smap */
+};
+
+struct inode_operations hfs_cap_fdir_inode_operations = {
+       &hfs_cap_dir_operations,/* default directory file-ops */
+       NULL,                   /* create */
+       cap_lookup,             /* lookup */
+       NULL,                   /* link */
+       NULL,                   /* unlink */
+       NULL,                   /* symlink */
+       NULL,                   /* mkdir */
+       NULL,                   /* rmdir */
+       NULL,                   /* mknod */
+       NULL,                   /* rename */
+       NULL,                   /* readlink */
+       NULL,                   /* follow_link */
+       NULL,                   /* readpage */
+       NULL,                   /* writepage */
+       NULL,                   /* bmap */
+       NULL,                   /* truncate */
+       NULL,                   /* permission */
+       NULL                    /* smap */
+};
+
+struct inode_operations hfs_cap_rdir_inode_operations = {
+       &hfs_cap_dir_operations,/* default directory file-ops */
+       hfs_create,             /* create */
+       cap_lookup,             /* lookup */
+       NULL,                   /* link */
+       NULL,                   /* unlink */
+       NULL,                   /* symlink */
+       NULL,                   /* mkdir */
+       NULL,                   /* rmdir */
+       NULL,                   /* mknod */
+       NULL,                   /* rename */
+       NULL,                   /* readlink */
+       NULL,                   /* follow_link */
+       NULL,                   /* readpage */
+       NULL,                   /* writepage */
+       NULL,                   /* bmap */
+       NULL,                   /* truncate */
+       NULL,                   /* permission */
+       NULL                    /* smap */
+};
+
+/*================ File-local functions ================*/
+
+/*
+ * cap_lookup()
+ *
+ * This is the lookup() entry in the inode_operations structure for
+ * HFS directories in the CAP scheme.  The purpose is to generate the
+ * inode corresponding to an entry in a directory, given the inode for
+ * the directory and the name (and its length) of the entry.
+ */
+static int cap_lookup(struct inode * dir, const char * name,
+                     int len, struct inode ** result)
+{
+       ino_t dtype;
+       struct hfs_name cname;
+       struct hfs_cat_entry *entry;
+       struct hfs_cat_key key;
+       struct inode *inode = NULL;
+
+       if (!dir || !S_ISDIR(dir->i_mode)) {
+               goto done;
+       }
+
+       entry = HFS_I(dir)->entry;
+       dtype = HFS_ITYPE(dir->i_ino);
+
+       if (len && !name) {
+               *result = NULL;
+               iput(dir);
+               return -EINVAL;
+       }
+
+       /* Perform name-mangling */
+       hfs_nameout(dir, &cname, name, len);
+
+       /* Check for "." */
+       if (hfs_streq(&cname, DOT)) {
+               /* this little trick skips the iget and iput */
+               *result = dir;
+               return 0;
+       }
+
+       /* Check for "..". */
+       if (hfs_streq(&cname, DOT_DOT)) {
+               struct hfs_cat_entry *parent;
+
+               if (dtype != HFS_CAP_NDIR) {
+                       /* Case for ".." in ".finderinfo" or ".resource" */
+                       parent = entry;
+                       ++entry->count; /* __hfs_iget() eats one */
+               } else {
+                       /* Case for ".." in a normal directory */
+                       parent = hfs_cat_parent(entry);
+               }
+               inode = __hfs_iget(parent, HFS_CAP_NDIR, 0);
+               goto done;
+       }
+
+       /* Check for special directories if in a normal directory.
+          Note that cap_dupdir() does an iput(dir). */
+       if (dtype==HFS_CAP_NDIR) {
+               /* Check for ".resource", ".finderinfo" and ".rootinfo" */
+               if (hfs_streq(&cname, DOT_RESOURCE)) {
+                       ++entry->count; /* __hfs_iget() eats one */
+                       inode = __hfs_iget(entry, HFS_CAP_RDIR, 1);
+                       goto done;
+               } else if (hfs_streq(&cname, DOT_FINDERINFO)) {
+                       ++entry->count; /* __hfs_iget() eats one */
+                       inode = __hfs_iget(entry, HFS_CAP_FDIR, 1);
+                       goto done;
+               } else if ((entry->cnid == htonl(HFS_ROOT_CNID)) &&
+                          hfs_streq(&cname, DOT_ROOTINFO)) {
+                       ++entry->count; /* __hfs_iget() eats one */
+                       inode = __hfs_iget(entry, HFS_CAP_FNDR, 0);
+                       goto done;
+               }
+       }
+
+       /* Do an hfs_iget() on the mangled name. */
+       hfs_cat_build_key(entry->cnid, &cname, &key);
+       inode = hfs_iget(entry->mdb, &key, HFS_I(dir)->file_type);
+
+       /* Don't return a resource fork for a directory */
+       if (inode && (dtype == HFS_CAP_RDIR) &&
+           (HFS_I(inode)->entry->type == HFS_CDR_DIR)) {
+               iput(inode);
+               inode = NULL;
+       }
+
+done:
+       iput(dir);
+       *result = inode;
+       return inode ? 0 : -ENOENT;
+}
+
+/*
+ * cap_readdir()
+ *
+ * This is the readdir() entry in the file_operations structure for
+ * HFS directories in the CAP scheme.  The purpose is to enumerate the
+ * entries in a directory, given the inode of the directory and a
+ * (struct file *), the 'f_pos' field of which indicates the location
+ * in the directory.  The (struct file *) is updated so that the next
+ * call with the same 'dir' and 'filp' arguments will produce the next
+ * directory entry.  The entries are returned in 'dirent', which is
+ * "filled-in" by calling filldir().  This allows the same readdir()
+ * function be used for different dirent formats.  We try to read in
+ * as many entries as we can before filldir() refuses to take any more.
+ *
+ * XXX: In the future it may be a good idea to consider not generating
+ * metadata files for covered directories since the data doesn't
+ * correspond to the mounted directory.         However this requires an
+ * iget() for every directory which could be considered an excessive
+ * amount of overhead. Since the inode for a mount point is always
+ * in-core this is another argument for a call to get an inode if it
+ * is in-core or NULL if it is not.
+ */
+static int cap_readdir(struct inode * dir, struct file * filp,
+                      void * dirent, filldir_t filldir)
+{
+       ino_t type;
+       int skip_dirs;
+       struct hfs_brec brec;
+        struct hfs_cat_entry *entry;
+
+       if (!dir || !dir->i_sb || !S_ISDIR(dir->i_mode)) {
+               return -EBADF;
+       }
+
+        entry = HFS_I(dir)->entry;
+       type = HFS_ITYPE(dir->i_ino);
+       skip_dirs = (type == HFS_CAP_RDIR);
+
+       if (filp->f_pos == 0) {
+               /* Entry 0 is for "." */
+               if (filldir(dirent, DOT->Name, DOT_LEN, 0, dir->i_ino)) {
+                       return 0;
+               }
+               filp->f_pos = 1;
+       }
+
+       if (filp->f_pos == 1) {
+               /* Entry 1 is for ".." */
+               hfs_u32 cnid;
+
+               if (type == HFS_CAP_NDIR) {
+                       cnid = hfs_get_nl(entry->key.ParID);
+               } else {
+                       cnid = entry->cnid;
+               }
+
+               if (filldir(dirent, DOT_DOT->Name,
+                           DOT_DOT_LEN, 1, ntohl(cnid))) {
+                       return 0;
+               }
+               filp->f_pos = 2;
+       }
+
+       if (filp->f_pos < (dir->i_size - 3)) {
+               hfs_u32 cnid;
+               hfs_u8 type;
+
+               if (hfs_cat_open(entry, &brec) ||
+                   hfs_cat_next(entry, &brec, filp->f_pos - 2, &cnid, &type)) {
+                       return 0;
+               }
+               while (filp->f_pos < (dir->i_size - 3)) {
+                       if (hfs_cat_next(entry, &brec, 1, &cnid, &type)) {
+                               return 0;
+                       }
+                       if (!skip_dirs || (type != HFS_CDR_DIR)) {
+                               ino_t ino;
+                               unsigned int len;
+                               unsigned char tmp_name[HFS_NAMEMAX];
+
+                               ino = ntohl(cnid) | HFS_I(dir)->file_type;
+                               len = hfs_namein(dir, tmp_name,
+                                   &((struct hfs_cat_key *)brec.key)->CName);
+                               if (filldir(dirent, tmp_name, len,
+                                           filp->f_pos, ino)) {
+                                       hfs_cat_close(entry, &brec);
+                                       return 0;
+                               }
+                       }
+                       ++filp->f_pos;
+               }
+               hfs_cat_close(entry, &brec);
+       }
+
+       if (filp->f_pos == (dir->i_size - 3)) {
+               if ((entry->cnid == htonl(HFS_ROOT_CNID)) &&
+                   (type == HFS_CAP_NDIR)) {
+                       /* In root dir last-2 entry is for ".rootinfo" */
+                       if (filldir(dirent, DOT_ROOTINFO->Name,
+                                   DOT_ROOTINFO_LEN, filp->f_pos,
+                                   ntohl(entry->cnid) | HFS_CAP_FNDR)) {
+                               return 0;
+                       }
+               }
+               ++filp->f_pos;
+       }
+
+       if (filp->f_pos == (dir->i_size - 2)) {
+               if (type == HFS_CAP_NDIR) {
+                       /* In normal dirs last-1 entry is for ".finderinfo" */
+                       if (filldir(dirent, DOT_FINDERINFO->Name,
+                                   DOT_FINDERINFO_LEN, filp->f_pos,
+                                   ntohl(entry->cnid) | HFS_CAP_FDIR)) {
+                               return 0;
+                       }
+               }
+               ++filp->f_pos;
+       }
+
+       if (filp->f_pos == (dir->i_size - 1)) {
+               if (type == HFS_CAP_NDIR) {
+                       /* In normal dirs last entry is for ".resource" */
+                       if (filldir(dirent, DOT_RESOURCE->Name,
+                                   DOT_RESOURCE_LEN, filp->f_pos,
+                                   ntohl(entry->cnid) | HFS_CAP_RDIR)) {
+                               return 0;
+                       }
+               }
+               ++filp->f_pos;
+       }
+
+       return 0;
+}
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/fs/hfs/dir_dbl.c linux-2.0.29/fs/hfs/dir_dbl.c
--- linux.vanilla/fs/hfs/dir_dbl.c      Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/fs/hfs/dir_dbl.c       Thu Apr 10 12:26:25 1997
@@ -0,0 +1,440 @@
+/*
+ * linux/fs/hfs/dir_dbl.c
+ *
+ * Copyright (C) 1995-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the inode_operations and file_operations
+ * structures for HFS directories.
+ *
+ * Based on the minix file system code, (C) 1991, 1992 by Linus Torvalds
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include <linux/hfs.h>
+#include <linux/hfs_fs_sb.h>
+#include <linux/hfs_fs_i.h>
+#include <linux/hfs_fs.h>
+
+/*================ Forward declarations ================*/
+
+static int dbl_lookup(struct inode *, const char *, int, struct inode **);
+static int dbl_readdir(struct inode *, struct file *, void *, filldir_t);
+static int dbl_create(struct inode *, const char *, int, int, struct inode **);
+static int dbl_mkdir(struct inode *, const char *, int, int);
+static int dbl_mknod(struct inode *, const char *, int, int, int);
+static int dbl_unlink(struct inode *, const char *, int);
+static int dbl_rmdir(struct inode *, const char *, int);
+static int dbl_rename(struct inode *, const char *, int,
+                     struct inode *, const char *, int, int);
+
+/*================ Global variables ================*/
+
+#define DOT_LEN                        1
+#define DOT_DOT_LEN            2
+#define ROOTINFO_LEN           8
+#define PCNT_ROOTINFO_LEN      9
+
+const struct hfs_name hfs_dbl_reserved1[] = {
+       {DOT_LEN,               "."},
+       {DOT_DOT_LEN,           ".."},
+       {0,                     ""},
+};
+
+const struct hfs_name hfs_dbl_reserved2[] = {
+       {ROOTINFO_LEN,          "RootInfo"},
+       {PCNT_ROOTINFO_LEN,     "%RootInfo"},
+       {0,                     ""},
+};
+
+#define DOT            (&hfs_dbl_reserved1[0])
+#define DOT_DOT                (&hfs_dbl_reserved1[1])
+#define ROOTINFO       (&hfs_dbl_reserved2[0])
+#define PCNT_ROOTINFO  (&hfs_dbl_reserved2[1])
+
+static struct file_operations hfs_dbl_dir_operations = {
+       NULL,                   /* lseek - default */
+       hfs_dir_read,           /* read - invalid */
+       NULL,                   /* write - bad */
+       dbl_readdir,            /* readdir */
+       NULL,                   /* select - default */
+       NULL,                   /* ioctl - default */
+       NULL,                   /* mmap - none */
+       NULL,                   /* no special open code */
+       NULL,                   /* no special release code */
+       file_fsync,             /* fsync - default */
+        NULL,                  /* fasync - default */
+        NULL,                  /* check_media_change - none */
+        NULL                   /* revalidate - none */
+};
+
+struct inode_operations hfs_dbl_dir_inode_operations = {
+       &hfs_dbl_dir_operations,/* default directory file-ops */
+       dbl_create,             /* create */
+       dbl_lookup,             /* lookup */
+       NULL,                   /* link */
+       dbl_unlink,             /* unlink */
+       NULL,                   /* symlink */
+       dbl_mkdir,              /* mkdir */
+       dbl_rmdir,              /* rmdir */
+       dbl_mknod,              /* mknod */
+       dbl_rename,             /* rename */
+       NULL,                   /* readlink */
+       NULL,                   /* follow_link */
+       NULL,                   /* readpage */
+       NULL,                   /* writepage */
+       NULL,                   /* bmap */
+       NULL,                   /* truncate */
+       NULL,                   /* permission */
+       NULL                    /* smap */
+};
+
+/*================ File-local functions ================*/
+
+/*
+ * is_hdr()
+ */
+static int is_hdr(struct inode *dir, const char *name, int len)
+{
+       int retval = 0;
+
+       if (name[0] == '%') {
+               struct hfs_cat_entry *entry = HFS_I(dir)->entry;
+               struct hfs_cat_entry *victim;
+               struct hfs_name cname;
+               struct hfs_cat_key key;
+
+               hfs_nameout(dir, &cname, name+1, len-1);
+               hfs_cat_build_key(entry->cnid, &cname, &key);
+               if ((victim = hfs_cat_get(entry->mdb, &key))) {
+                       hfs_cat_put(victim);
+                       retval = 1;
+               }
+       }
+       return retval;
+}
+
+/*
+ * dbl_lookup()
+ *
+ * This is the lookup() entry in the inode_operations structure for
+ * HFS directories in the AppleDouble scheme.  The purpose is to
+ * generate the inode corresponding to an entry in a directory, given
+ * the inode for the directory and the name (and its length) of the
+ * entry.
+ */
+static int dbl_lookup(struct inode * dir, const char * name,
+                     int len, struct inode ** result)
+{
+       struct hfs_name cname;
+       struct hfs_cat_entry *entry;
+       struct hfs_cat_key key;
+       struct inode *inode = NULL;
+
+       if (!dir || !S_ISDIR(dir->i_mode)) {
+               goto done;
+       }
+
+       entry = HFS_I(dir)->entry;
+
+       if (len && !name) {
+               *result = NULL;
+               iput(dir);
+               return -EINVAL;
+       }
+
+       /* Perform name-mangling */
+       hfs_nameout(dir, &cname, name, len);
+
+       /* Check for "." */
+       if (hfs_streq(&cname, DOT)) {
+               /* this little trick skips the iget and iput */
+               *result = dir;
+               return 0;
+       }
+
+       /* Check for "..". */
+       if (hfs_streq(&cname, DOT_DOT)) {
+               inode = __hfs_iget(hfs_cat_parent(entry), HFS_DBL_DIR, 0);
+               goto done;
+       }
+
+       /* Check for "%RootInfo" if in the root directory. */
+       if ((entry->cnid == htonl(HFS_ROOT_CNID)) &&
+           hfs_streq(&cname, PCNT_ROOTINFO)) {
+               ++entry->count; /* __hfs_iget() eats one */
+               inode = __hfs_iget(entry, HFS_DBL_HDR, 0);
+               goto done;
+       }
+
+       /* Do an hfs_iget() on the mangled name. */
+       hfs_cat_build_key(entry->cnid, &cname, &key);
+       inode = hfs_iget(entry->mdb, &key, HFS_DBL_NORM);
+
+       /* Try as a header if not found and first character is '%' */
+       if (!inode && (name[0] == '%')) {
+               hfs_nameout(dir, &cname, name+1, len-1);
+               hfs_cat_build_key(entry->cnid, &cname, &key);
+               inode = hfs_iget(entry->mdb, &key, HFS_DBL_HDR);
+       }
+
+done:
+       iput(dir);
+       *result = inode;
+       return inode ? 0 : -ENOENT;
+}
+
+/*
+ * dbl_readdir()
+ *
+ * This is the readdir() entry in the file_operations structure for
+ * HFS directories in the AppleDouble scheme.  The purpose is to
+ * enumerate the entries in a directory, given the inode of the
+ * directory and a (struct file *), the 'f_pos' field of which
+ * indicates the location in the directory.  The (struct file *) is
+ * updated so that the next call with the same 'dir' and 'filp'
+ * arguments will produce the next directory entry.  The entries are
+ * returned in 'dirent', which is "filled-in" by calling filldir().
+ * This allows the same readdir() function be used for different
+ * formats.  We try to read in as many entries as we can before
+ * filldir() refuses to take any more.
+ *
+ * XXX: In the future it may be a good idea to consider not generating
+ * metadata files for covered directories since the data doesn't
+ * correspond to the mounted directory.         However this requires an
+ * iget() for every directory which could be considered an excessive
+ * amount of overhead. Since the inode for a mount point is always
+ * in-core this is another argument for a call to get an inode if it
+ * is in-core or NULL if it is not.
+ */
+static int dbl_readdir(struct inode * dir, struct file * filp,
+                      void * dirent, filldir_t filldir)
+{
+       struct hfs_brec brec;
+        struct hfs_cat_entry *entry;
+
+       if (!dir || !dir->i_sb || !S_ISDIR(dir->i_mode)) {
+               return -EBADF;
+       }
+
+        entry = HFS_I(dir)->entry;
+
+       if (filp->f_pos == 0) {
+               /* Entry 0 is for "." */
+               if (filldir(dirent, DOT->Name, DOT_LEN, 0, dir->i_ino)) {
+                       return 0;
+               }
+               filp->f_pos = 1;
+       }
+
+       if (filp->f_pos == 1) {
+               /* Entry 1 is for ".." */
+               if (filldir(dirent, DOT_DOT->Name, DOT_DOT_LEN, 1,
+                           hfs_get_hl(entry->key.ParID))) {
+                       return 0;
+               }
+               filp->f_pos = 2;
+       }
+
+       if (filp->f_pos < (dir->i_size - 1)) {
+                hfs_u32 cnid;
+                hfs_u8 type;
+
+               if (hfs_cat_open(entry, &brec) ||
+                   hfs_cat_next(entry, &brec, (filp->f_pos - 1) >> 1,
+                                &cnid, &type)) {
+                       return 0;
+               }
+
+               while (filp->f_pos < (dir->i_size - 1)) {
+                       ino_t ino;
+                       int is_hdr = (filp->f_pos & 1);
+                       unsigned int len;
+                       unsigned char tmp_name[HFS_NAMEMAX + 1];
+
+                       if (is_hdr) {
+                               ino = ntohl(cnid) | HFS_DBL_HDR;
+                               tmp_name[0] = '%';
+                               len = 1 + hfs_namein(dir, tmp_name + 1,
+                                   &((struct hfs_cat_key *)brec.key)->CName);
+                       } else {
+                               if (hfs_cat_next(entry, &brec, 1,
+                                                       &cnid, &type)) {
+                                       return 0;
+                               }
+                               ino = ntohl(cnid);
+                               len = hfs_namein(dir, tmp_name,
+                                   &((struct hfs_cat_key *)brec.key)->CName);
+                       }
+
+                       if (filldir(dirent, tmp_name, len, filp->f_pos, ino)) {
+                               hfs_cat_close(entry, &brec);
+                               return 0;
+                       }
+                       ++filp->f_pos;
+               }
+               hfs_cat_close(entry, &brec);
+       }
+
+       if (filp->f_pos == (dir->i_size - 1)) {
+               if (entry->cnid == htonl(HFS_ROOT_CNID)) {
+                       /* In root dir last entry is for "%RootInfo" */
+                       if (filldir(dirent, PCNT_ROOTINFO->Name,
+                                   PCNT_ROOTINFO_LEN, filp->f_pos,
+                                   ntohl(entry->cnid) | HFS_DBL_HDR)) {
+                               return 0;
+                       }
+               }
+               ++filp->f_pos;
+       }
+
+       return 0;
+}
+
+/*
+ * dbl_create()
+ *
+ * This is the create() entry in the inode_operations structure for
+ * AppleDouble directories.  The purpose is to create a new file in
+ * a directory and return a corresponding inode, given the inode for
+ * the directory and the name (and its length) of the new file.
+ */
+static int dbl_create(struct inode * dir, const char * name, int len,
+                     int mode, struct inode **result)
+{
+       int error;
+
+       *result = NULL;
+       if (is_hdr(dir, name, len)) {
+               iput(dir);
+               error = -EEXIST;
+       } else {
+               error = hfs_create(dir, name, len, mode, result);
+       }
+       return error;
+}
+
+/*
+ * dbl_mkdir()
+ *
+ * This is the mkdir() entry in the inode_operations structure for
+ * AppleDouble directories.  The purpose is to create a new directory
+ * in a directory, given the inode for the parent directory and the
+ * name (and its length) of the new directory.
+ */
+static int dbl_mkdir(struct inode * parent, const char * name,
+                    int len, int mode)
+{
+       int error;
+
+       if (is_hdr(parent, name, len)) {
+               iput(parent);
+               error = -EEXIST;
+       } else {
+               error = hfs_mkdir(parent, name, len, mode);
+       }
+       return error;
+}
+
+/*
+ * dbl_mknod()
+ *
+ * This is the mknod() entry in the inode_operations structure for
+ * regular HFS directories.  The purpose is to create a new entry
+ * in a directory, given the inode for the parent directory and the
+ * name (and its length) and the mode of the new entry (and the device
+ * number if the entry is to be a device special file).
+ */
+static int dbl_mknod(struct inode *dir, const char *name,
+                    int len, int mode, int rdev)
+{
+       int error;
+
+       if (is_hdr(dir, name, len)) {
+               iput(dir);
+               error = -EEXIST;
+       } else {
+               error = hfs_mknod(dir, name, len, mode, rdev);
+       }
+       return error;
+}
+
+/*
+ * dbl_unlink()
+ *
+ * This is the unlink() entry in the inode_operations structure for
+ * AppleDouble directories.  The purpose is to delete an existing
+ * file, given the inode for the parent directory and the name
+ * (and its length) of the existing file.
+ */
+static int dbl_unlink(struct inode * dir, const char * name, int len)
+{
+       int error;
+
+       ++dir->i_count;
+       error = hfs_unlink(dir, name, len);
+       if ((error == -ENOENT) && is_hdr(dir, name, len)) {
+               error = -EPERM;
+       }
+       iput(dir);
+       return error;
+}
+
+/*
+ * dbl_rmdir()
+ *
+ * This is the rmdir() entry in the inode_operations structure for
+ * AppleDouble directories.  The purpose is to delete an existing
+ * directory, given the inode for the parent directory and the name
+ * (and its length) of the existing directory.
+ */
+static int dbl_rmdir(struct inode * parent, const char * name, int len)
+{
+       int error;
+
+       ++parent->i_count;
+       error = hfs_rmdir(parent, name, len);
+       if ((error == -ENOENT) && is_hdr(parent, name, len)) {
+               error = -ENOTDIR;
+       }
+       iput(parent);
+       return error;
+}
+
+/*
+ * dbl_rename()
+ *
+ * This is the rename() entry in the inode_operations structure for
+ * AppleDouble directories.  The purpose is to rename an existing
+ * file or directory, given the inode for the current directory and
+ * the name (and its length) of the existing file/directory and the
+ * inode for the new directory and the name (and its length) of the
+ * new file/directory.
+ */
+static int dbl_rename(struct inode *old_dir, const char *old_name, int old_len,
+                     struct inode *new_dir, const char *new_name, int new_len,
+                     int must_be_dir)
+{
+       int error;
+
+       if (is_hdr(new_dir, new_name, new_len)) {
+               error = -EPERM;
+       } else {
+               ++old_dir->i_count;
+               ++new_dir->i_count;
+               error = hfs_rename(old_dir, old_name, old_len,
+                                  new_dir, new_name, new_len, must_be_dir);
+               if ((error == -ENOENT) && !must_be_dir &&
+                   is_hdr(old_dir, old_name, old_len)) {
+                       error = -EPERM;
+               }
+       }
+       iput(old_dir);
+       iput(new_dir);
+       return error;
+}
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/fs/hfs/dir_nat.c linux-2.0.29/fs/hfs/dir_nat.c
--- linux.vanilla/fs/hfs/dir_nat.c      Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/fs/hfs/dir_nat.c       Thu Apr 10 12:26:34 1997
@@ -0,0 +1,459 @@
+/*
+ * linux/fs/hfs/dir_nat.c
+ *
+ * Copyright (C) 1995-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the inode_operations and file_operations
+ * structures for HFS directories.
+ *
+ * Based on the minix file system code, (C) 1991, 1992 by Linus Torvalds
+ *
+ * The source code distributions of Netatalk, versions 1.3.3b2 and
+ * 1.4b2, were used as a specification of the location and format of
+ * files used by Netatalk's afpd.  No code from Netatalk appears in
+ * hfs_fs.  hfs_fs is not a work ``derived'' from Netatalk in the
+ * sense of intellectual property law.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include <linux/hfs.h>
+#include <linux/hfs_fs_sb.h>
+#include <linux/hfs_fs_i.h>
+#include <linux/hfs_fs.h>
+
+/*================ Forward declarations ================*/
+
+static int nat_lookup(struct inode *, const char *, int, struct inode **);
+static int nat_readdir(struct inode *, struct file *, void *, filldir_t);
+static int nat_rmdir(struct inode *, const char *, int);
+static int nat_hdr_unlink(struct inode *, const char *, int);
+static int nat_hdr_rename(struct inode *, const char *, int,
+                         struct inode *, const char *, int, int);
+
+/*================ Global variables ================*/
+
+#define DOT_LEN                        1
+#define DOT_DOT_LEN            2
+#define DOT_APPLEDOUBLE_LEN    12
+#define DOT_PARENT_LEN         7
+
+const struct hfs_name hfs_nat_reserved1[] = {
+       {DOT_LEN,               "."},
+       {DOT_DOT_LEN,           ".."},
+       {DOT_APPLEDOUBLE_LEN,   ".AppleDouble"},
+       {DOT_PARENT_LEN,        ".Parent"},
+       {0,                     ""},
+};
+
+const struct hfs_name hfs_nat_reserved2[] = {
+       {0,                     ""},
+};
+
+#define DOT            (&hfs_nat_reserved1[0])
+#define DOT_DOT                (&hfs_nat_reserved1[1])
+#define DOT_APPLEDOUBLE        (&hfs_nat_reserved1[2])
+#define DOT_PARENT     (&hfs_nat_reserved1[3])
+
+static struct file_operations hfs_nat_dir_operations = {
+       NULL,                   /* lseek - default */
+       hfs_dir_read,           /* read - invalid */
+       NULL,                   /* write - bad */
+       nat_readdir,            /* readdir */
+       NULL,                   /* select - default */
+       NULL,                   /* ioctl - default */
+       NULL,                   /* mmap - none */
+       NULL,                   /* no special open code */
+       NULL,                   /* no special release code */
+       file_fsync,             /* fsync - default */
+        NULL,                  /* fasync - default */
+        NULL,                  /* check_media_change - none */
+        NULL                   /* revalidate - none */
+};
+
+struct inode_operations hfs_nat_ndir_inode_operations = {
+       &hfs_nat_dir_operations,/* default directory file-ops */
+       hfs_create,             /* create */
+       nat_lookup,             /* lookup */
+       NULL,                   /* link */
+       hfs_unlink,             /* unlink */
+       NULL,                   /* symlink */
+       hfs_mkdir,              /* mkdir */
+       nat_rmdir,              /* rmdir */
+       hfs_mknod,              /* mknod */
+       hfs_rename,             /* rename */
+       NULL,                   /* readlink */
+       NULL,                   /* follow_link */
+       NULL,                   /* readpage */
+       NULL,                   /* writepage */
+       NULL,                   /* bmap */
+       NULL,                   /* truncate */
+       NULL,                   /* permission */
+       NULL                    /* smap */
+};
+
+struct inode_operations hfs_nat_hdir_inode_operations = {
+       &hfs_nat_dir_operations,/* default directory file-ops */
+       hfs_create,             /* create */
+       nat_lookup,             /* lookup */
+       NULL,                   /* link */
+       nat_hdr_unlink,         /* unlink */
+       NULL,                   /* symlink */
+       NULL,                   /* mkdir */
+       NULL,                   /* rmdir */
+       NULL,                   /* mknod */
+       nat_hdr_rename,         /* rename */
+       NULL,                   /* readlink */
+       NULL,                   /* follow_link */
+       NULL,                   /* readpage */
+       NULL,                   /* writepage */
+       NULL,                   /* bmap */
+       NULL,                   /* truncate */
+       NULL,                   /* permission */
+       NULL                    /* smap */
+};
+
+/*================ File-local functions ================*/
+
+/*
+ * nat_lookup()
+ *
+ * This is the lookup() entry in the inode_operations structure for
+ * HFS directories in the Netatalk scheme.  The purpose is to generate
+ * the inode corresponding to an entry in a directory, given the inode
+ * for the directory and the name (and its length) of the entry.
+ */
+static int nat_lookup(struct inode * dir, const char * name,
+                     int len, struct inode ** result)
+{
+       ino_t dtype;
+       struct hfs_name cname;
+       struct hfs_cat_entry *entry;
+       struct hfs_cat_key key;
+       struct inode *inode = NULL;
+
+       if (!dir || !S_ISDIR(dir->i_mode)) {
+               goto done;
+       }
+
+       entry = HFS_I(dir)->entry;
+       dtype = HFS_ITYPE(dir->i_ino);
+
+       if (len && !name) {
+               *result = NULL;
+               iput(dir);
+               return -EINVAL;
+       }
+
+       /* Perform name-mangling */
+       hfs_nameout(dir, &cname, name, len);
+
+       /* Check for "." */
+       if (hfs_streq(&cname, DOT)) {
+               /* this little trick skips the iget and iput */
+               *result = dir;
+               return 0;
+       }
+
+       /* Check for "..". */
+       if (hfs_streq(&cname, DOT_DOT)) {
+               struct hfs_cat_entry *parent;
+
+               if (dtype != HFS_NAT_NDIR) {
+                       /* Case for ".." in ".AppleDouble" */
+                       parent = entry;
+                       ++entry->count; /* __hfs_iget() eats one */
+               } else {
+                       /* Case for ".." in a normal directory */
+                       parent = hfs_cat_parent(entry);
+               }
+               inode = __hfs_iget(parent, HFS_NAT_NDIR, 0);
+               goto done;
+       }
+
+       /* Check for ".AppleDouble" if in a normal directory,
+          and for ".Parent" in ".AppleDouble". */
+       if (dtype==HFS_NAT_NDIR) {
+               /* Check for ".AppleDouble" */
+               if (hfs_streq(&cname, DOT_APPLEDOUBLE)) {
+                       ++entry->count; /* __hfs_iget() eats one */
+                       inode = __hfs_iget(entry, HFS_NAT_HDIR, 1);
+                       goto done;
+               }
+       } else if (dtype==HFS_NAT_HDIR) {
+               if (hfs_streq(&cname, DOT_PARENT)) {
+                       ++entry->count; /* __hfs_iget() eats one */
+                       inode = __hfs_iget(entry, HFS_NAT_HDR, 0);
+                       goto done;
+               }
+       }
+
+       /* Do an hfs_iget() on the mangled name. */
+       hfs_cat_build_key(entry->cnid, &cname, &key);
+       inode = hfs_iget(entry->mdb, &key, HFS_I(dir)->file_type);
+
+       /* Don't return a header file for a directory other than .Parent */
+       if (inode && (dtype == HFS_NAT_HDIR) &&
+           (HFS_I(inode)->entry != entry) &&
+           (HFS_I(inode)->entry->type == HFS_CDR_DIR)) {
+               iput(inode);
+               inode = NULL;
+       }
+
+done:
+       iput(dir);
+       *result = inode;
+       return inode ? 0 : -ENOENT;
+}
+
+/*
+ * nat_readdir()
+ *
+ * This is the readdir() entry in the file_operations structure for
+ * HFS directories in the netatalk scheme.  The purpose is to
+ * enumerate the entries in a directory, given the inode of the
+ * directory and a struct file which indicates the location in the
+ * directory.  The struct file is updated so that the next call with
+ * the same dir and filp will produce the next directory entry.         The
+ * entries are returned in dirent, which is "filled-in" by calling
+ * filldir().  This allows the same readdir() function be used for
+ * different dirent formats.  We try to read in as many entries as we
+ * can before filldir() refuses to take any more.
+ *
+ * Note that the Netatalk format doesn't have the problem with
+ * metadata for covered directories that exists in the other formats,
+ * since the metadata is contained within the directory.
+ */
+static int nat_readdir(struct inode * dir, struct file * filp,
+                      void * dirent, filldir_t filldir)
+{
+       ino_t type;
+       int skip_dirs;
+       struct hfs_brec brec;
+        struct hfs_cat_entry *entry;
+
+       if (!dir || !dir->i_sb || !S_ISDIR(dir->i_mode)) {
+               return -EBADF;
+       }
+
+        entry = HFS_I(dir)->entry;
+       type = HFS_ITYPE(dir->i_ino);
+       skip_dirs = (type == HFS_NAT_HDIR);
+
+       if (filp->f_pos == 0) {
+               /* Entry 0 is for "." */
+               if (filldir(dirent, DOT->Name, DOT_LEN, 0, dir->i_ino)) {
+                       return 0;
+               }
+               filp->f_pos = 1;
+       }
+
+       if (filp->f_pos == 1) {
+               /* Entry 1 is for ".." */
+               hfs_u32 cnid;
+
+               if (type == HFS_NAT_NDIR) {
+                       cnid = hfs_get_nl(entry->key.ParID);
+               } else {
+                       cnid = entry->cnid;
+               }
+
+               if (filldir(dirent, DOT_DOT->Name,
+                           DOT_DOT_LEN, 1, ntohl(cnid))) {
+                       return 0;
+               }
+               filp->f_pos = 2;
+       }
+
+       if (filp->f_pos < (dir->i_size - 1)) {
+               hfs_u32 cnid;
+               hfs_u8 type;
+
+               if (hfs_cat_open(entry, &brec) ||
+                   hfs_cat_next(entry, &brec, filp->f_pos - 2, &cnid, &type)) {
+                       return 0;
+               }
+               while (filp->f_pos < (dir->i_size - 1)) {
+                       if (hfs_cat_next(entry, &brec, 1, &cnid, &type)) {
+                               return 0;
+                       }
+                       if (!skip_dirs || (type != HFS_CDR_DIR)) {
+                               ino_t ino;
+                               unsigned int len;
+                               unsigned char tmp_name[HFS_NAMEMAX];
+
+                               ino = ntohl(cnid) | HFS_I(dir)->file_type;
+                               len = hfs_namein(dir, tmp_name,
+                                   &((struct hfs_cat_key *)brec.key)->CName);
+                               if (filldir(dirent, tmp_name, len,
+                                           filp->f_pos, ino)) {
+                                       hfs_cat_close(entry, &brec);
+                                       return 0;
+                               }
+                       }
+                       ++filp->f_pos;
+               }
+               hfs_cat_close(entry, &brec);
+       }
+
+       if (filp->f_pos == (dir->i_size - 1)) {
+               if (type == HFS_NAT_NDIR) {
+                       /* In normal dirs entry 2 is for ".AppleDouble" */
+                       if (filldir(dirent, DOT_APPLEDOUBLE->Name,
+                                   DOT_APPLEDOUBLE_LEN, filp->f_pos,
+                                   ntohl(entry->cnid) | HFS_NAT_HDIR)) {
+                               return 0;
+                       }
+               } else if (type == HFS_NAT_HDIR) {
+                       /* In .AppleDouble entry 2 is for ".Parent" */
+                       if (filldir(dirent, DOT_PARENT->Name,
+                                   DOT_PARENT_LEN, filp->f_pos,
+                                   ntohl(entry->cnid) | HFS_NAT_HDR)) {
+                               return 0;
+                       }
+               }
+               ++filp->f_pos;
+       }
+
+       return 0;
+}
+
+/*
+ * nat_rmdir()
+ *
+ * This is the rmdir() entry in the inode_operations structure for
+ * Netatalk directories.  The purpose is to delete an existing
+ * directory, given the inode for the parent directory and the name
+ * (and its length) of the existing directory.
+ *
+ * We handle .AppleDouble and call hfs_rmdir() for all other cases.
+ */
+static int nat_rmdir(struct inode *parent, const char *name, int len)
+{
+       struct hfs_cat_entry *entry = HFS_I(parent)->entry;
+       struct hfs_name cname;
+       int error;
+
+       hfs_nameout(parent, &cname, name, len);
+       if (hfs_streq(&cname, DOT_APPLEDOUBLE)) {
+               if (!HFS_SB(parent->i_sb)->s_afpd) {
+                       /* Not in AFPD compatibility mode */
+                       error = -EPERM;
+               } else if (entry->u.dir.files || entry->u.dir.dirs) {
+                       /* AFPD compatible, but the directory is not empty */
+                       error = -ENOTEMPTY;
+               } else {
+                       /* AFPD compatible, so pretend to succeed */
+                       error = 0;
+               }
+               iput(parent);
+       } else {
+               error = hfs_rmdir(parent, name, len);
+       }
+       return error;
+}
+
+/*
+ * nat_hdr_unlink()
+ *
+ * This is the unlink() entry in the inode_operations structure for
+ * Netatalk .AppleDouble directories.  The purpose is to delete an
+ * existing file, given the inode for the parent directory and the name
+ * (and its length) of the existing file.
+ *
+ * WE DON'T ACTUALLY DELETE HEADER THE FILE.
+ * In non-afpd-compatible mode:
+ *   We return -EPERM.
+ * In afpd-compatible mode:
+ *   We return success if the file exists or is .Parent.
+ *   Otherwise we return -ENOENT.
+ */
+static int nat_hdr_unlink(struct inode *dir, const char *name, int len)
+{
+       struct hfs_cat_entry *entry = HFS_I(dir)->entry;
+       int error = 0;
+
+       if (!HFS_SB(dir->i_sb)->s_afpd) {
+               /* Not in AFPD compatibility mode */
+               error = -EPERM;
+       } else {
+               struct hfs_name cname;
+
+               hfs_nameout(dir, &cname, name, len);
+               if (!hfs_streq(&cname, DOT_PARENT)) {
+                       struct hfs_cat_entry *victim;
+                       struct hfs_cat_key key;
+
+                       hfs_cat_build_key(entry->cnid, &cname, &key);
+                       victim = hfs_cat_get(entry->mdb, &key);
+
+                       if (victim) {
+                               hfs_cat_put(victim);
+                       } else {
+                               error = -ENOENT;
+                       }
+               }
+       }
+       iput(dir);
+       return error;
+}
+
+/*
+ * nat_hdr_rename()
+ *
+ * This is the rename() entry in the inode_operations structure for
+ * Netatalk header directories.  The purpose is to rename an existing
+ * file given the inode for the current directory and the name
+ * (and its length) of the existing file and the inode for the new
+ * directory and the name (and its length) of the new file/directory.
+ *
+ * WE NEVER MOVE ANYTHING.
+ * In non-afpd-compatible mode:
+ *   We return -EPERM.
+ * In afpd-compatible mode:
+ *   If the source header doesn't exist, we return -ENOENT.
+ *   If the destination is not a header directory we return -EPERM.
+ *   We return success if the destination is also a header directory
+ *    and the header exists or is ".Parent".
+ */
+static int nat_hdr_rename(struct inode *old_dir, const char *old_name,
+                         int old_len, struct inode *new_dir,
+                         const char *new_name, int new_len, int must_be_dir)
+{
+       struct hfs_cat_entry *entry = HFS_I(old_dir)->entry;
+       int error = 0;
+
+       if (!HFS_SB(old_dir->i_sb)->s_afpd) {
+               error = -EPERM;
+       } else {
+               struct hfs_name cname;
+
+               hfs_nameout(old_dir, &cname, old_name, old_len);
+               if (!hfs_streq(&cname, DOT_PARENT)) {
+                       struct hfs_cat_entry *victim;
+                       struct hfs_cat_key key;
+
+                       hfs_cat_build_key(entry->cnid, &cname, &key);
+                       victim = hfs_cat_get(entry->mdb, &key);
+
+
+                       if (victim) {
+                               hfs_cat_put(victim);
+                       } else {
+                               error = -ENOENT;
+                       }
+               }
+
+               if (!error && (HFS_ITYPE(new_dir->i_ino) != HFS_NAT_HDIR)) {
+                       error = -EPERM;
+               }
+       }
+       iput(old_dir);
+       iput(new_dir);
+       return error;
+}
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/fs/hfs/extent.c linux-2.0.29/fs/hfs/extent.c
--- linux.vanilla/fs/hfs/extent.c       Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/fs/hfs/extent.c        Thu Apr 10 12:29:02 1997
@@ -0,0 +1,811 @@
+/*
+ * linux/fs/hfs/extent.c
+ *
+ * Copyright (C) 1995-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the functions related to the extents B-tree.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include <linux/hfs.h>
+
+/*================ File-local data type ================*/
+
+/* An extent record on disk*/
+struct hfs_raw_extent {
+       hfs_word_t      block1;
+       hfs_word_t      length1;
+       hfs_word_t      block2;
+       hfs_word_t      length2;
+       hfs_word_t      block3;
+       hfs_word_t      length3;
+};
+
+/*================ File-local functions ================*/
+
+/*
+ * build_key
+ */
+static inline void build_key(struct hfs_ext_key *key,
+                            const struct hfs_fork *fork, hfs_u16 block)
+{
+       key->KeyLen = 7;
+       key->FkType = fork->fork;
+       hfs_put_nl(fork->entry->cnid, key->FNum);
+       hfs_put_hs(block,             key->FABN);
+}
+
+
+/*
+ * lock_bitmap()
+ *
+ * Get an exclusive lock on the B-tree bitmap.
+ */
+static inline void lock_bitmap(struct hfs_mdb *mdb) {
+       while (mdb->bitmap_lock) {
+               hfs_sleep_on(&mdb->bitmap_wait);
+       }
+       mdb->bitmap_lock = 1;
+}
+
+/*
+ * unlock_bitmap()
+ *
+ * Relinquish an exclusive lock on the B-tree bitmap.
+ */
+static inline void unlock_bitmap(struct hfs_mdb *mdb) {
+       mdb->bitmap_lock = 0;
+       hfs_wake_up(&mdb->bitmap_wait);
+}
+
+/*
+ * dump_ext()
+ *
+ * prints the content of a extent for debugging purposes.
+ */
+#if defined(DEBUG_EXTENTS) || defined(DEBUG_ALL)
+static void dump_ext(const char *msg, const struct hfs_extent *e) {
+       if (e) {
+               hfs_warn("%s (%d-%d) (%d-%d) (%d-%d)\n", msg,
+                        e->start,
+                        e->start + e->length[0] - 1,
+                        e->start + e->length[0],
+                        e->start + e->length[0] + e->length[1] - 1,
+                        e->start + e->length[0] + e->length[1],
+                        e->end);
+       } else {
+               hfs_warn("%s NULL\n", msg);
+       }
+}
+#else
+#define dump_ext(A,B) {}
+#endif
+
+/*
+ * read_extent()
+ *
+ * Initializes a (struct hfs_extent) from a (struct hfs_raw_extent) and
+ * the number of the starting block for the extent.
+ *
+ * Note that the callers must check that to,from != NULL
+ */
+static void read_extent(struct hfs_extent *to,
+                       const struct hfs_raw_extent *from,
+                       hfs_u16 start)
+{
+       to->start = start;
+       to->block[0]  = hfs_get_hs(from->block1);
+       to->length[0] = hfs_get_hs(from->length1);
+       to->block[1]  = hfs_get_hs(from->block2);
+       to->length[1] = hfs_get_hs(from->length2);
+       to->block[2]  = hfs_get_hs(from->block3);
+       to->length[2] = hfs_get_hs(from->length3);
+       to->end = start + to->length[0] + to->length[1] + to->length[2] - 1;
+       to->next = to->prev = NULL;
+       to->count = 0;
+}
+
+/*
+ * write_extent()
+ *
+ * Initializes a (struct hfs_raw_extent) from a (struct hfs_extent).
+ *
+ * Note that the callers must check that to,from != NULL
+ */
+static void write_extent(struct hfs_raw_extent *to,
+                        const struct hfs_extent *from)
+{
+       hfs_put_hs(from->block[0], to->block1);
+       hfs_put_hs(from->length[0], to->length1);
+       hfs_put_hs(from->block[1], to->block2);
+       hfs_put_hs(from->length[1], to->length2);
+       hfs_put_hs(from->block[2], to->block3);
+       hfs_put_hs(from->length[2], to->length3);
+}
+
+/*
+ * decode_extent()
+ *
+ * Given an extent record and allocation block offset into the file,
+ * return the number of the corresponding allocation block on disk,
+ * or -1 if the desired block is not mapped by the given extent.
+ *
+ * Note that callers must check that extent != NULL
+ */
+static int decode_extent(const struct hfs_extent * extent, int block)
+{
+       if (!extent || (block < extent->start) || (block > extent->end) ||
+           (extent->end == (hfs_u16)(extent->start - 1))) {
+               return -1;
+       }
+       block -= extent->start;
+       if (block < extent->length[0]) {
+               return block + extent->block[0];
+       }
+       block -= extent->length[0];
+       if (block < extent->length[1]) {
+               return block + extent->block[1];
+       }
+       return block + extent->block[2] - extent->length[1];
+}
+
+/*
+ * relse_ext()
+ *
+ * Reduce the reference count of an in-core extent record by one,
+ * removing it from memory if the count falls to zero.
+ */
+static void relse_ext(struct hfs_extent *ext)
+{
+       if (--ext->count || !ext->start) {
+               return;
+       }
+       ext->prev->next = ext->next;
+       if (ext->next) {
+               ext->next->prev = ext->prev;
+       }
+       HFS_DELETE(ext);
+}
+
+/*
+ * set_cache()
+ *
+ * Changes the 'cache' field of the fork.
+ */
+static inline void set_cache(struct hfs_fork *fork, struct hfs_extent *ext)
+{
+       struct hfs_extent *tmp = fork->cache;
+
+       ++ext->count;
+       fork->cache = ext;
+       relse_ext(tmp);
+}
+
+/*
+ * find_ext()
+ *
+ * Given a pointer to a (struct hfs_file) and an allocation block
+ * number in the file, find the extent record containing that block.
+ * Returns a pointer to the extent record on success or NULL on failure.
+ * The 'cache' field of 'fil' also points to the extent so it has a
+ * reference count of at least 2.
+ *
+ * Callers must check that fil != NULL
+ */
+static struct hfs_extent * find_ext(struct hfs_fork *fork, int alloc_block)
+{
+        struct hfs_cat_entry *entry = fork->entry;
+       struct hfs_btree *tr= entry->mdb->ext_tree;
+       struct hfs_ext_key target, *key;
+       struct hfs_brec brec;
+       struct hfs_extent *ext, *ptr;
+       int tmp;
+
+       if (alloc_block < 0) {
+               ext = &fork->first;
+               goto found;
+       }
+
+       ext = fork->cache;
+       if (!ext || (alloc_block < ext->start)) {
+               ext = &fork->first;
+       }
+       while (ext->next && (alloc_block > ext->end)) {
+               ext = ext->next;
+       }
+       if ((alloc_block <= ext->end) && (alloc_block >= ext->start)) {
+               goto found;
+       }
+
+       /* time to read more extents */
+       if (!HFS_NEW(ext)) {
+               goto bail3;
+       }
+
+       build_key(&target, fork, alloc_block);
+
+       tmp = hfs_bfind(&brec, tr, HFS_BKEY(&target), HFS_BFIND_READ_LE);
+       if (tmp < 0) {
+               goto bail2;
+       }
+
+       key = (struct hfs_ext_key *)brec.key;
+       if ((hfs_get_nl(key->FNum) != hfs_get_nl(target.FNum)) ||
+           (key->FkType != fork->fork)) {
+               goto bail1;
+       }
+
+       read_extent(ext, brec.data, hfs_get_hs(key->FABN));
+       hfs_brec_relse(&brec, NULL);
+
+       if ((alloc_block > ext->end) && (alloc_block < ext->start)) {
+               /* something strange happened */
+               goto bail2;
+       }
+
+       ptr = fork->cache;
+       if (!ptr || (alloc_block < ptr->start)) {
+               ptr = &fork->first;
+       }
+       while (ptr->next && (alloc_block > ptr->end)) {
+               ptr = ptr->next;
+       }
+       if (ext->start == ptr->start) {
+               /* somebody beat us to it. */
+               HFS_DELETE(ext);
+               ext = ptr;
+       } else if (ext->start < ptr->start) {
+               /* insert just before ptr */
+               ptr->prev->next = ext;
+               ext->prev = ptr->prev;
+               ext->next = ptr;
+               ptr->prev = ext;
+       } else {
+               /* insert at end */
+               ptr->next = ext;
+               ext->prev = ptr;
+       }
+ found:
+       ++ext->count; /* for return value */
+       set_cache(fork, ext);
+       return ext;
+
+ bail1:
+       hfs_brec_relse(&brec, NULL);
+ bail2:
+       HFS_DELETE(ext);
+ bail3:
+       return NULL;
+}
+
+/*
+ * delete_extent()
+ *
+ * Description:
+ *   Deletes an extent record from a fork, reducing its physical length.
+ * Input Variable(s):
+ *   struct hfs_fork *fork: the fork
+ *   struct hfs_extent *ext: the current last extent for 'fork'
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   void
+ * Preconditions:
+ *   'fork' points to a valid (struct hfs_fork)
+ *   'ext' point to a valid (struct hfs_extent) which is the last in 'fork'
+ *    and which is not also the first extent in 'fork'.
+ * Postconditions:
+ *   The extent record has been removed if possible, and a warning has been
+ *   printed otherwise.
+ */
+static void delete_extent(struct hfs_fork *fork, struct hfs_extent *ext)
+{
+       struct hfs_mdb *mdb = fork->entry->mdb;
+       struct hfs_ext_key key;
+       int error;
+
+       if (fork->cache == ext) {
+               set_cache(fork, ext->prev);
+       }
+       ext->prev->next = NULL;
+       if (ext->count != 1) {
+               hfs_warn("hfs_truncate: extent has count %d.\n", ext->count);
+       }
+
+       lock_bitmap(mdb);
+       error = hfs_clear_vbm_bits(mdb, ext->block[2], ext->length[2]);
+       if (error) {
+               hfs_warn("hfs_truncate: error %d freeing blocks.\n", error);
+       }
+       error = hfs_clear_vbm_bits(mdb, ext->block[1], ext->length[1]);
+       if (error) {
+               hfs_warn("hfs_truncate: error %d freeing blocks.\n", error);
+       }
+       error = hfs_clear_vbm_bits(mdb, ext->block[0], ext->length[0]);
+       if (error) {
+               hfs_warn("hfs_truncate: error %d freeing blocks.\n", error);
+       }
+       unlock_bitmap(mdb);
+
+       build_key(&key, fork, ext->start);
+
+       error = hfs_bdelete(mdb->ext_tree, HFS_BKEY(&key));
+       if (error) {
+               hfs_warn("hfs_truncate: error %d deleting an extent.\n", error);
+       }
+       HFS_DELETE(ext);
+}
+
+/*
+ * new_extent()
+ *
+ * Description:
+ *   Adds a new extent record to a fork, extending its physical length.
+ * Input Variable(s):
+ *   struct hfs_fork *fork: the fork to extend
+ *   struct hfs_extent *ext: the current last extent for 'fork'
+ *   hfs_u16 ablock: the number of allocation blocks in 'fork'.
+ *   hfs_u16 start: first allocation block to add to 'fork'.
+ *   hfs_u16 len: the number of allocation blocks to add to 'fork'.
+ *   hfs_u16 ablksz: number of sectors in an allocation block.
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   (struct hfs_extent *) the new extent or NULL
+ * Preconditions:
+ *   'fork' points to a valid (struct hfs_fork)
+ *   'ext' point to a valid (struct hfs_extent) which is the last in 'fork'
+ *   'ablock', 'start', 'len' and 'ablksz' are what they claim to be.
+ * Postconditions:
+ *   If NULL is returned then no changes have been made to 'fork'.
+ *   If the return value is non-NULL that it is the extent that has been
+ *   added to 'fork' both in memory and on disk.  The 'psize' field of
+ *   'fork' has been updated to reflect the new physical size.
+ */
+static struct hfs_extent *new_extent(struct hfs_fork *fork,
+                                    struct hfs_extent *ext,
+                                    hfs_u16 ablock, hfs_u16 start,
+                                    hfs_u16 len, hfs_u16 ablksz)
+{
+       struct hfs_raw_extent raw;
+       struct hfs_ext_key key;
+       int error;
+
+       if (fork->entry->cnid == htonl(HFS_EXT_CNID)) {
+               /* Limit extents tree to the record in the MDB */
+               return NULL;
+       }
+
+       if (!HFS_NEW(ext->next)) {
+               return NULL;
+       }
+       ext->next->prev = ext;
+       ext->next->next = NULL;
+       ext = ext->next;
+       relse_ext(ext->prev);
+
+       ext->start = ablock;
+       ext->block[0] = start;
+       ext->length[0] = len;
+       ext->block[1] = 0;
+       ext->length[1] = 0;
+       ext->block[2] = 0;
+       ext->length[2] = 0;
+       ext->end = ablock + len - 1;
+       ext->count = 1;
+
+       write_extent(&raw, ext);
+
+       build_key(&key, fork, ablock);
+
+       error = hfs_binsert(fork->entry->mdb->ext_tree,
+                           HFS_BKEY(&key), &raw, sizeof(raw));
+       if (error) {
+               ext->prev->next = NULL;
+               HFS_DELETE(ext);
+               return NULL;
+       }
+       set_cache(fork, ext);
+       return ext;
+}
+
+/*
+ * update_ext()
+ *
+ * Given a (struct hfs_fork) write an extent record back to disk.
+ */
+static void update_ext(struct hfs_fork *fork, struct hfs_extent *ext)
+{
+       struct hfs_ext_key target;
+       struct hfs_brec brec;
+
+       if (ext->start) {
+               build_key(&target, fork, ext->start);
+
+               if (!hfs_bfind(&brec, fork->entry->mdb->ext_tree,
+                              HFS_BKEY(&target), HFS_BFIND_WRITE)) {
+                       write_extent(brec.data, ext);
+                       hfs_brec_relse(&brec, NULL);
+               }
+       }
+}
+
+/*
+ * zero_blocks()
+ *
+ * Zeros-out 'num' allocation blocks beginning with 'start'.
+ */
+static int zero_blocks(struct hfs_mdb *mdb, int start, int num) {
+       hfs_buffer buf;
+       int end;
+       int j;
+
+       start = mdb->fs_start + start * mdb->alloc_blksz;
+       end = start + num * mdb->alloc_blksz;
+
+       for (j=start; j<end; ++j) {
+               if (hfs_buffer_ok(buf = hfs_buffer_get(mdb->sys_mdb, j, 0))) {
+                       memset(hfs_buffer_data(buf), 0, HFS_SECTOR_SIZE);
+                       hfs_buffer_dirty(buf);
+                       hfs_buffer_put(buf);
+               }
+       }
+       return 0;
+}
+
+/*
+ * shrink_fork()
+ *
+ * Try to remove enough allocation blocks from 'fork'
+ * so that it is 'ablocks' allocation blocks long.
+ */
+static void shrink_fork(struct hfs_fork *fork, int ablocks)
+{
+       struct hfs_mdb *mdb = fork->entry->mdb;
+       struct hfs_extent *ext;
+       int i, error, next, count;
+       hfs_u16 ablksz = mdb->alloc_blksz;
+
+       next =  (fork->psize / ablksz) - 1;
+       ext = find_ext(fork, next);
+       while (ext && ext->start && (ext->start >= ablocks)) {
+               next = ext->start - 1;
+               delete_extent(fork, ext);
+               ext = find_ext(fork, next);
+       }
+       if (!ext) {
+               fork->psize = (next + 1) * ablksz;
+               return;
+       }
+
+       if ((count = next + 1 - ablocks) > 0) {
+               for (i=2; (i>=0) && !ext->length[i]; --i) {};
+               while (count && (ext->length[i] <= count)) {
+                       ext->end -= ext->length[i];
+                       count -= ext->length[i];
+                       error = hfs_clear_vbm_bits(mdb, ext->block[i],
+                                                  ext->length[i]);
+                       if (error) {
+                               hfs_warn("hfs_truncate: error %d freeing "
+                                      "blocks.\n", error);
+                       }
+                       ext->block[i] = ext->length[i] = 0;
+                       --i;
+               }
+               if (count) {
+                       ext->end -= count;
+                       ext->length[i] -= count;
+                       error = hfs_clear_vbm_bits(mdb, ext->block[i] +
+                                                      ext->length[i], count);
+                       if (error) {
+                               hfs_warn("hfs_truncate: error %d freeing "
+                                      "blocks.\n", error);
+                       }
+               }
+               update_ext(fork, ext);
+       }
+
+       fork->psize = ablocks * ablksz;
+}
+
+/*
+ * grow_fork()
+ *
+ * Try to add enough allocation blocks to 'fork'
+ * so that it is 'ablock' allocation blocks long.
+ */
+static void grow_fork(struct hfs_fork *fork, int ablocks)
+{
+       struct hfs_cat_entry *entry = fork->entry;
+       struct hfs_mdb *mdb = entry->mdb;
+       struct hfs_extent *ext;
+       int i, start, err;
+       hfs_u16 need, len=0;
+       hfs_u16 ablksz = mdb->alloc_blksz;
+       hfs_u32 blocks, clumpablks;
+
+       blocks = fork->psize;
+       need = ablocks - blocks/ablksz;
+       if (need < 1) {
+               return;
+       }
+
+       /* round up to clumpsize */
+       if (entry->u.file.clumpablks) {
+               clumpablks = entry->u.file.clumpablks;
+       } else {
+               clumpablks = mdb->clumpablks;
+       }
+       need = ((need + clumpablks - 1) / clumpablks) * clumpablks;
+
+       /* find last extent record and try to extend it */
+       if (!(ext = find_ext(fork, blocks/ablksz - 1))) {
+               /* somehow we couldn't find the end of the file! */
+               return;
+       }
+
+       /* determine which is the last used extent in the record */
+       /* then try to allocate the blocks immediately following it */
+       for (i=2; (i>=0) && !ext->length[i]; --i) {};
+       if (i>=0) {
+               /* try to extend the last extent */
+               start = ext->block[i] + ext->length[i];
+
+               err = 0;
+               lock_bitmap(mdb);
+               len = hfs_vbm_count_free(mdb, start);
+               if (!len) {
+                       unlock_bitmap(mdb);
+                       goto more_extents;
+               }
+               if (need < len) {
+                       len = need;
+               }
+               err = hfs_set_vbm_bits(mdb, start, len);
+               unlock_bitmap(mdb);
+               if (err) {
+                       relse_ext(ext);
+                       return;
+               }
+
+               zero_blocks(mdb, start, len);
+
+               ext->length[i] += len;
+               ext->end += len;
+               blocks = (fork->psize += len * ablksz);
+               need -= len;
+               update_ext(fork, ext);
+       }
+
+more_extents:
+       /* add some more extents */
+       while (need) {
+               len = need;
+               err = 0;
+               lock_bitmap(mdb);
+               start = hfs_vbm_search_free(mdb, &len);
+               if (need < len) {
+                       len = need;
+               }
+               err = hfs_set_vbm_bits(mdb, start, len);
+               unlock_bitmap(mdb);
+               if (!len || err) {
+                       relse_ext(ext);
+                       return;
+               }
+               zero_blocks(mdb, start, len);
+
+               /* determine which is the first free extent in the record */
+               for (i=0; (i<3) && ext->length[i]; ++i) {};
+               if (i < 3) {
+                       ext->block[i] = start;
+                       ext->length[i] = len;
+                       ext->end += len;
+                       update_ext(fork, ext);
+               } else {
+                       if (!(ext = new_extent(fork, ext, blocks/ablksz,
+                                              start, len, ablksz))) {
+                               hfs_clear_vbm_bits(mdb, start, len);
+                               return;
+                       }
+               }
+               blocks = (fork->psize += len * ablksz);
+               need -= len;
+       }
+       set_cache(fork, ext);
+       relse_ext(ext);
+       return;
+}
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_ext_compare()
+ *
+ * Description:
+ *   This is the comparison function used for the extents B-tree.  In
+ *   comparing extent B-tree entries, the file id is the most
+ *   significant field (compared as unsigned ints); the fork type is
+ *   the second most significant field (compared as unsigned chars);
+ *   and the allocation block number field is the least significant
+ *   (compared as unsigned ints).
+ * Input Variable(s):
+ *   struct hfs_ext_key *key1: pointer to the first key to compare
+ *   struct hfs_ext_key *key2: pointer to the second key to compare
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   int: negative if key1<key2, positive if key1>key2, and 0 if key1==key2
+ * Preconditions:
+ *   key1 and key2 point to "valid" (struct hfs_ext_key)s.
+ * Postconditions:
+ *   This function has no side-effects */
+int hfs_ext_compare(const struct hfs_ext_key *key1,
+                   const struct hfs_ext_key *key2)
+{
+       unsigned int tmp;
+       int retval;
+
+       tmp = hfs_get_hl(key1->FNum) - hfs_get_hl(key2->FNum);
+       if (tmp != 0) {
+               retval = (int)tmp;
+       } else {
+               tmp = (unsigned char)key1->FkType - (unsigned char)key2->FkType;
+               if (tmp != 0) {
+                       retval = (int)tmp;
+               } else {
+                       retval = (int)(hfs_get_hs(key1->FABN)
+                                      - hfs_get_hs(key2->FABN));
+               }
+       }
+       return retval;
+}
+
+/*
+ * hfs_extent_adj()
+ *
+ * Given an hfs_fork shrink or grow the fork to hold the
+ * forks logical size.
+ */
+void hfs_extent_adj(struct hfs_fork *fork)
+{
+       if (fork) {
+               hfs_u32 blks, ablocks;
+               hfs_u16 ablksz;
+
+               if (fork->lsize > HFS_FORK_MAX) {
+                       fork->lsize = HFS_FORK_MAX;
+               }
+
+               blks = (fork->lsize+HFS_SECTOR_SIZE-1) >> HFS_SECTOR_SIZE_BITS;
+               ablksz = fork->entry->mdb->alloc_blksz;
+               ablocks = (blks + ablksz - 1) / ablksz;
+
+               if (blks > fork->psize) {
+                       grow_fork(fork, ablocks);
+                       if (blks > fork->psize) {
+                               fork->lsize =
+                                       fork->psize >> HFS_SECTOR_SIZE_BITS;
+                       }
+                       fork->entry->dirt = 1;
+               } else if (blks < fork->psize) {
+                       shrink_fork(fork, ablocks);
+                       fork->entry->dirt = 1;
+               }
+       }
+}
+
+/*
+ * hfs_extent_map()
+ *
+ * Given an hfs_fork and a block number within the fork, return the
+ * number of the corresponding physical block on disk, or zero on
+ * error.
+ */
+int hfs_extent_map(struct hfs_fork *fork, int block, int create)
+{
+       int ablksz, ablock, offset, tmp;
+       struct hfs_extent *ext;
+
+       if (!fork || !fork->entry || !fork->entry->mdb) {
+               return 0;
+       }
+
+#if defined(DEBUG_EXTENTS) || defined(DEBUG_ALL)
+       hfs_warn("hfs_extent_map: ablock %d of file %d, fork %d\n",
+                block, fork->entry->cnid, fork->fork);
+#endif
+
+       if (block < 0) {
+               hfs_warn("hfs_extent_map: block < 0\n");
+               return 0;
+       }
+       if (block > (HFS_FORK_MAX >> HFS_SECTOR_SIZE_BITS)) {
+               hfs_warn("hfs_extent_map: block(0x%08x) > big; cnid=%d "
+                        "fork=%d\n", block, fork->entry->cnid, fork->fork);
+               return 0;
+       }
+       ablksz = fork->entry->mdb->alloc_blksz;
+       offset = fork->entry->mdb->fs_start + (block % ablksz);
+       ablock = block / ablksz;
+
+       if (block >= fork->psize) {
+               if (create) {
+                       grow_fork(fork, ablock + 1);
+                       fork->entry->dirt = 1;
+               } else {
+                       return 0;
+               }
+       }
+
+#if defined(DEBUG_EXTENTS) || defined(DEBUG_ALL)
+       hfs_warn("(lblock %d offset %d)\n", ablock, offset);
+#endif
+
+       if ((ext = find_ext(fork, ablock))) {
+               dump_ext("trying new: ", ext);
+               tmp = decode_extent(ext, ablock);
+               relse_ext(ext);
+               if (tmp >= 0) {
+                       return tmp*ablksz + offset;
+               }
+       }
+
+       return 0;
+}
+
+/*
+ * hfs_extent_out()
+ *
+ * Copy the first extent record from a (struct hfs_fork) to a (struct
+ * raw_extent), record (normally the one in the catalog entry).
+ */
+void hfs_extent_out(const struct hfs_fork *fork, hfs_byte_t dummy[12])
+{
+       struct hfs_raw_extent *ext = (struct hfs_raw_extent *)dummy;
+
+       if (fork && ext) {
+               write_extent(ext, &fork->first);
+               dump_ext("extent out: ", &fork->first);
+       }
+}
+
+/*
+ * hfs_extent_in()
+ *
+ * Copy an raw_extent to the 'first' and 'cache' fields of an hfs_fork.
+ */
+void hfs_extent_in(struct hfs_fork *fork, const hfs_byte_t dummy[12])
+{
+       const struct hfs_raw_extent *ext =
+               (const struct hfs_raw_extent *)dummy;
+
+       if (fork && ext) {
+               read_extent(&fork->first, ext, 0);
+               fork->cache = &fork->first;
+               fork->first.count = 2;
+               dump_ext("extent in: ", &fork->first);
+       }
+}
+
+/*
+ * hfs_extent_free()
+ *
+ * Removes from memory all extents associated with 'fil'.
+ */
+void hfs_extent_free(struct hfs_fork *fork)
+{
+       if (fork) {
+               set_cache(fork, &fork->first);
+
+               if (fork->first.next) {
+                       hfs_warn("hfs_extent_free: extents in use!\n");
+               }
+       }
+}
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/fs/hfs/file.c linux-2.0.29/fs/hfs/file.c
--- linux.vanilla/fs/hfs/file.c Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/fs/hfs/file.c  Thu Apr 10 12:29:13 1997
@@ -0,0 +1,519 @@
+/*
+ * linux/fs/hfs/file.c
+ *
+ * Copyright (C) 1995, 1996  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the file-related functions which are independent of
+ * which scheme is being used to represent forks.
+ *
+ * Based on the minix file system code, (C) 1991, 1992 by Linus Torvalds
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include <linux/hfs.h>
+#include <linux/hfs_fs_sb.h>
+#include <linux/hfs_fs_i.h>
+#include <linux/hfs_fs.h>
+
+/*================ Forward declarations ================*/
+
+static hfs_rwret_t hfs_file_read(struct inode *, struct file *, char *,
+                                hfs_rwarg_t);
+static hfs_rwret_t hfs_file_write(struct inode *, struct file *, const char *,
+                                 hfs_rwarg_t);
+static void hfs_file_truncate(struct inode *);
+static int hfs_bmap(struct inode *, int);
+
+/*================ Global variables ================*/
+
+static struct file_operations hfs_file_operations = {
+       NULL,                   /* lseek - default */
+       hfs_file_read,          /* read */
+       hfs_file_write,         /* write */
+       NULL,                   /* readdir - bad */
+       NULL,                   /* select - default */
+       NULL,                   /* ioctl - default */
+       generic_file_mmap,      /* mmap */
+       NULL,                   /* open */
+       NULL,                   /* release */
+       file_fsync,             /* fsync - default */
+        NULL,                  /* fasync - default */
+        NULL,                  /* check_media_change - none */
+        NULL                   /* revalidate - none */
+};
+
+struct inode_operations hfs_file_inode_operations = {
+       &hfs_file_operations,   /* default file operations */
+       NULL,                   /* create */
+       NULL,                   /* lookup */
+       NULL,                   /* link */
+       NULL,                   /* unlink */
+       NULL,                   /* symlink */
+       NULL,                   /* mkdir */
+       NULL,                   /* rmdir */
+       NULL,                   /* mknod */
+       NULL,                   /* rename */
+       NULL,                   /* readlink */
+       NULL,                   /* follow_link */
+       generic_readpage,       /* readpage */
+       NULL,                   /* writepage */
+       hfs_bmap,               /* bmap */
+       hfs_file_truncate,      /* truncate */
+       NULL,                   /* permission */
+       NULL                    /* smap */
+};
+
+/*================ Variable-like macros ================*/
+
+/* maximum number of blocks to try to read in at once */
+#define NBUF 32
+
+/*================ File-local functions ================*/
+
+/*
+ * hfs_getblk()
+ *
+ * Given an hfs_fork and a block number return the buffer_head for
+ * that block from the fork.  If 'create' is non-zero then allocate
+ * the necessary block(s) to the fork.
+ */
+struct buffer_head *hfs_getblk(struct hfs_fork *fork, int block, int create)
+{
+       int tmp;
+       kdev_t dev = fork->entry->mdb->sys_mdb->s_dev;
+
+       tmp = hfs_extent_map(fork, block, create);
+
+       if (create) {
+               /* If writing the block, then we have exclusive access
+                  to the file until we return, so it can't have moved.
+               */
+               return tmp ? getblk(dev, tmp, HFS_SECTOR_SIZE) : NULL;
+       } else {
+               /* If reading the block, then retry since the
+                  location on disk could have changed while
+                  we waited on the I/O in getblk to complete.
+               */
+               do {
+                       struct buffer_head *bh =
+                                       getblk(dev, tmp, HFS_SECTOR_SIZE);
+                       int tmp2 = hfs_extent_map(fork, block, 0);
+
+                       if (tmp2 == tmp) {
+                               return bh;
+                       } else {
+                               /* The block moved or no longer exists. */
+                               brelse(bh);
+                               tmp = tmp2;
+                       }
+               } while (tmp != 0);
+
+               /* The block no longer exists. */
+               return NULL;
+       }
+}
+
+/*
+ * hfs_bmap()
+ *
+ * This is the bmap() field in the inode_operations structure for
+ * "regular" (non-header) files.  The purpose is to translate an inode
+ * and a block number within the corresponding file into a physical
+ * block number.  This function just calls hfs_extent_map() to do the
+ * real work.
+ */
+static int hfs_bmap(struct inode * inode, int block)
+{
+       return hfs_extent_map(HFS_I(inode)->fork, block, 0);
+}
+
+/*
+ * hfs_file_read()
+ *
+ * This is the read field in the inode_operations structure for
+ * "regular" (non-header) files.  The purpose is to transfer up to
+ * 'count' bytes from the file corresponding to 'inode', beginning at
+ * 'filp->offset' bytes into the file. The data is transfered to
+ * user-space at the address 'buf'.  Returns the number of bytes
+ * successfully transfered.  This function checks the arguments, does
+ * some setup and then calls hfs_do_read() to do the actual transfer.
+ */
+static hfs_rwret_t hfs_file_read(struct inode * inode, struct file * filp,
+                                char * buf, hfs_rwarg_t count)
+{
+       hfs_s32 read, left, pos, size;
+
+       if (!S_ISREG(inode->i_mode)) {
+               hfs_warn("hfs_file_read: mode = %07o\n",inode->i_mode);
+               return -EINVAL;
+       }
+       if (filp->f_pos >= HFS_FORK_MAX) {
+               return 0;
+       }
+       pos = filp->f_pos;
+       size = inode->i_size;
+       if (pos > size) {
+               left = 0;
+       } else {
+               left = size - pos;
+       }
+       if (left > count) {
+               left = count;
+       }
+       if (left <= 0) {
+               return 0;
+       }
+       read = hfs_do_read(inode, HFS_I(inode)->fork, pos,
+                          buf, left, filp->f_reada != 0);
+       if (read > 0) {
+               filp->f_pos += read;
+               filp->f_reada = 1;
+       }
+       return read;
+}
+
+/*
+ * hfs_file_write()
+ *
+ * This is the write() entry in the file_operations structure for
+ * "regular" files.  The purpose is to transfer up to 'count' bytes
+ * to the file corresponding to 'inode' beginning at offset
+ * 'file->f_pos' from user-space at the address 'buf'.  The return
+ * value is the number of bytes actually transferred.
+ */
+static hfs_rwret_t hfs_file_write(struct inode * inode, struct file * filp,
+                                 const char * buf, hfs_rwarg_t count)
+{
+       struct hfs_fork *fork = HFS_I(inode)->fork;
+       hfs_s32 written;
+
+       if (!S_ISREG(inode->i_mode)) {
+               hfs_warn("hfs_file_write: mode = %07o\n", inode->i_mode);
+               return -EINVAL;
+       }
+       if (filp->f_flags & O_APPEND) {
+               filp->f_pos = inode->i_size;
+       }
+       if (filp->f_pos >= HFS_FORK_MAX) {
+               return 0;
+       }
+       if (count > HFS_FORK_MAX) {
+               count = HFS_FORK_MAX;
+       }
+       written = hfs_do_write(inode, fork, filp->f_pos, buf, count);
+       if (written > 0) {
+               filp->f_pos += written;
+               if (filp->f_pos > inode->i_size) {
+                       inode->i_size = filp->f_pos;
+               }
+       }
+       return written;
+}
+
+/*
+ * hfs_file_truncate()
+ *
+ * This is the truncate() entry in the file_operations structure for
+ * "regular" files.  The purpose is to change the length of the file
+ * corresponding to the given inode.  Changes can either lengthen or
+ * shorten the file.
+ */
+static void hfs_file_truncate(struct inode * inode)
+{
+       struct hfs_fork *fork = HFS_I(inode)->fork;
+
+       fork->lsize = inode->i_size;
+       hfs_extent_adj(fork);
+
+       inode->i_size = fork->lsize;
+       inode->i_blocks = fork->psize;
+}
+
+/*
+ * xlate_to_user()
+ *
+ * Like copy_to_user() while translating CR->NL.
+ */
+static inline void xlate_to_user(char *buf, const char *data, int count)
+{
+       char ch;
+
+       while (count--) {
+               ch = *(data++);
+               put_user((ch == '\r') ? '\n' : ch, buf++);
+       }
+}
+
+/*
+ * xlate_from_user()
+ *
+ * Like copy_from_user() while translating NL->CR;
+ */
+static inline void xlate_from_user(char *data, const char *buf, int count)
+{
+       copy_from_user(data, buf, count);
+       while (count--) {
+               if (*data == '\n') {
+                       *data = '\r';
+               }
+               ++data;
+       }
+}
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_do_read()
+ *
+ * This function transfers actual data from disk to user-space memory,
+ * returning the number of bytes successfully transfered.  'fork' tells
+ * which file on the disk to read from.  'pos' gives the offset into
+ * the Linux file at which to begin the transfer.  Note that this will
+ * differ from 'filp->offset' in the case of an AppleDouble header file
+ * due to the block of metadata at the beginning of the file, which has
+ * no corresponding place in the HFS file.  'count' tells how many
+ * bytes to transfer.  'buf' gives an address in user-space to transfer
+ * the data to.
+ *
+ * This is based on Linus's minix_file_read().
+ * It has been changed to take into account that HFS files have no holes.
+ */
+hfs_s32 hfs_do_read(struct inode *inode, struct hfs_fork * fork, hfs_u32 pos,
+                   char * buf, hfs_u32 count, int reada)
+{
+       kdev_t dev = inode->i_dev;
+       hfs_s32 size, chars, offset, block, blocks, read = 0;
+       int bhrequest, uptodate;
+       int convert = HFS_I(inode)->convert;
+       struct buffer_head ** bhb, ** bhe;
+       struct buffer_head * bhreq[NBUF];
+       struct buffer_head * buflist[NBUF];
+
+       /* split 'pos' in to block and (byte) offset components */
+       block = pos >> HFS_SECTOR_SIZE_BITS;
+       offset = pos & (HFS_SECTOR_SIZE-1);
+
+       /* compute the logical size of the fork in blocks */
+       size = (fork->lsize + (HFS_SECTOR_SIZE-1)) >> HFS_SECTOR_SIZE_BITS;
+
+       /* compute the number of physical blocks to be transferred */
+       blocks = (count+offset+HFS_SECTOR_SIZE-1) >> HFS_SECTOR_SIZE_BITS;
+
+       bhb = bhe = buflist;
+       if (reada) {
+               if (blocks < read_ahead[MAJOR(dev)] / (HFS_SECTOR_SIZE>>9)) {
+                       blocks = read_ahead[MAJOR(dev)] / (HFS_SECTOR_SIZE>>9);
+               }
+               if (block + blocks > size) {
+                       blocks = size - block;
+               }
+       }
+
+       /* We do this in a two stage process.  We first try and
+          request as many blocks as we can, then we wait for the
+          first one to complete, and then we try and wrap up as many
+          as are actually done.
+
+          This routine is optimized to make maximum use of the
+          various buffers and caches. */
+
+       do {
+               bhrequest = 0;
+               uptodate = 1;
+               while (blocks) {
+                       --blocks;
+                       *bhb = hfs_getblk(fork, block++, 0);
+
+                       if (!(*bhb)) {
+                               /* Since there are no holes in HFS files
+                                  we must have encountered an error.
+                                  So, stop adding blocks to the queue. */
+                               blocks = 0;
+                               break;
+                       }
+
+                       if (!buffer_uptodate(*bhb)) {
+                               uptodate = 0;
+                               bhreq[bhrequest++] = *bhb;
+                       }
+
+                       if (++bhb == &buflist[NBUF]) {
+                               bhb = buflist;
+                       }
+
+                       /* If the block we have on hand is uptodate,
+                          go ahead and complete processing. */
+                       if (uptodate) {
+                               break;
+                       }
+                       if (bhb == bhe) {
+                               break;
+                       }
+               }
+
+               /* If the only block in the queue is bad then quit */
+               if (!(*bhe)) {
+                       break;
+               }
+
+               /* Now request them all */
+               if (bhrequest) {
+                       ll_rw_block(READ, bhrequest, bhreq);
+               }
+
+               do {  /* Finish off all I/O that has actually completed */
+                       char *p;
+
+                       wait_on_buffer(*bhe);
+
+                       if (!buffer_uptodate(*bhe)) {
+                               /* read error? */
+                               brelse(*bhe);
+                               if (++bhe == &buflist[NBUF]) {
+                                       bhe = buflist;
+                               }
+                               count = 0;
+                               break;
+                       }
+
+                       if (count < HFS_SECTOR_SIZE - offset) {
+                               chars = count;
+                       } else {
+                               chars = HFS_SECTOR_SIZE - offset;
+                       }
+                       count -= chars;
+                       read += chars;
+                       p = (*bhe)->b_data + offset;
+                       if (convert) {
+                               xlate_to_user(buf, p, chars);
+                       } else {
+                               copy_to_user(buf, p, chars);
+                       }
+                       brelse(*bhe);
+                       buf += chars;
+                       offset = 0;
+                       if (++bhe == &buflist[NBUF]) {
+                               bhe = buflist;
+                       }
+               } while (count && (bhe != bhb) && !buffer_locked(*bhe));
+       } while (count);
+
+       /* Release the read-ahead blocks */
+       while (bhe != bhb) {
+               brelse(*bhe);
+               if (++bhe == &buflist[NBUF]) {
+                       bhe = buflist;
+               }
+       }
+       if (!read) {
+               return -EIO;
+       }
+       return read;
+}
+
+/*
+ * hfs_do_write()
+ *
+ * This function transfers actual data from user-space memory to disk,
+ * returning the number of bytes successfully transfered.  'fork' tells
+ * which file on the disk to write to.  'pos' gives the offset into
+ * the Linux file at which to begin the transfer.  Note that this will
+ * differ from 'filp->offset' in the case of an AppleDouble header file
+ * due to the block of metadata at the beginning of the file, which has
+ * no corresponding place in the HFS file.  'count' tells how many
+ * bytes to transfer.  'buf' gives an address in user-space to transfer
+ * the data from.
+ *
+ * This is just a minor edit of Linus's minix_file_write().
+ */
+hfs_s32 hfs_do_write(struct inode *inode, struct hfs_fork * fork, hfs_u32 pos,
+                    const char * buf, hfs_u32 count)
+{
+       hfs_s32 written, c;
+       struct buffer_head * bh;
+       char * p;
+       int convert = HFS_I(inode)->convert;
+
+       written = 0;
+       while (written < count) {
+               bh = hfs_getblk(fork, pos/HFS_SECTOR_SIZE, 1);
+               if (!bh) {
+                       if (!written) {
+                               written = -ENOSPC;
+                       }
+                       break;
+               }
+               c = HFS_SECTOR_SIZE - (pos % HFS_SECTOR_SIZE);
+               if (c > count - written) {
+                       c = count - written;
+               }
+               if (c != HFS_SECTOR_SIZE && !buffer_uptodate(bh)) {
+                       ll_rw_block(READ, 1, &bh);
+                       wait_on_buffer(bh);
+                       if (!buffer_uptodate(bh)) {
+                               brelse(bh);
+                               if (!written) {
+                                       written = -EIO;
+                               }
+                               break;
+                       }
+               }
+               p = (pos % HFS_SECTOR_SIZE) + bh->b_data;
+               if (convert) {
+                       xlate_from_user(p, buf, c);
+               } else {
+                       copy_from_user(p, buf, c);
+               }
+               update_vm_cache(inode,pos,p,c);
+               pos += c;
+               written += c;
+               buf += c;
+               mark_buffer_uptodate(bh, 1);
+               mark_buffer_dirty(bh, 0);
+               brelse(bh);
+       }
+       if (written > 0) {
+               struct hfs_cat_entry *entry = fork->entry;
+
+               inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+               if (pos > fork->lsize) {
+                       fork->lsize = pos;
+               }
+               entry->modify_date = hfs_u_to_mtime(CURRENT_TIME);
+               entry->dirt = 1;
+       }
+       return written;
+}
+
+/*
+ * hfs_file_fix_mode()
+ *
+ * Fixes up the permissions on a file after changing the write-inhibit bit.
+ */
+void hfs_file_fix_mode(struct hfs_cat_entry *entry)
+{
+       struct inode **inodes = entry->sys_entry;
+       int i;
+
+       if (entry->u.file.flags & HFS_FIL_LOCK) {
+               for (i = 0; i < 4; ++i) {
+                       if (inodes[i]) {
+                               inodes[i]->i_mode &= ~S_IWUGO;
+                       }
+               }
+       } else {
+               for (i = 0; i < 4; ++i) {
+                       if (inodes[i]) {
+                               inodes[i]->i_mode |= S_IWUGO;
+                               inodes[i]->i_mode &=
+                                       ~HFS_SB(inodes[i]->i_sb)->s_umask;
+                       }
+               }
+       }
+}
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/fs/hfs/file_cap.c linux-2.0.29/fs/hfs/file_cap.c
--- linux.vanilla/fs/hfs/file_cap.c     Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/fs/hfs/file_cap.c      Thu Apr 10 12:29:24 1997
@@ -0,0 +1,293 @@
+/*
+ * linux/fs/hfs/file_cap.c
+ *
+ * Copyright (C) 1995-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the file_ops and inode_ops for the metadata
+ * files under the CAP representation.
+ *
+ * The source code distribution of the Columbia AppleTalk Package for
+ * UNIX, version 6.0, (CAP) was used as a specification of the
+ * location and format of files used by CAP's Aufs.  No code from CAP
+ * appears in hfs_fs.  hfs_fs is not a work ``derived'' from CAP in
+ * the sense of intellectual property law.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include <linux/hfs.h>
+#include <linux/hfs_fs_sb.h>
+#include <linux/hfs_fs_i.h>
+#include <linux/hfs_fs.h>
+
+/*================ Forward declarations ================*/
+
+static hfs_rwret_t cap_info_read(struct inode *, struct file *, char *,
+                                hfs_rwarg_t);
+static hfs_rwret_t cap_info_write(struct inode *, struct file *, const char *,
+                                 hfs_rwarg_t);
+static void cap_info_truncate(struct inode *);
+
+/*================ Function-like macros ================*/
+
+/*
+ * OVERLAPS()
+ *
+ * Determines if a given range overlaps the specified structure member
+ */
+#define OVERLAPS(START, END, TYPE, MEMB) \
+       ((END > offsetof(TYPE, MEMB)) && \
+        (START < offsetof(TYPE, MEMB) + sizeof(((TYPE *)0)->MEMB)))
+
+/*================ Global variables ================*/
+
+static struct file_operations hfs_cap_info_operations = {
+       NULL,                   /* lseek - default */
+       cap_info_read,          /* read */
+       cap_info_write,         /* write */
+       NULL,                   /* readdir - bad */
+       NULL,                   /* select - default */
+       NULL,                   /* ioctl - default */
+       NULL,                   /* mmap - not yet */
+       NULL,                   /* no special open code */
+       NULL,                   /* no special release code */
+       file_fsync,             /* fsync - default */
+       NULL,                   /* fasync - default */
+       NULL,                   /* check_media_change - none */
+       NULL                    /* revalidate - none */
+};
+
+struct inode_operations hfs_cap_info_inode_operations = {
+       &hfs_cap_info_operations,       /* default file operations */
+       NULL,                           /* create */
+       NULL,                           /* lookup */
+       NULL,                           /* link */
+       NULL,                           /* unlink */
+       NULL,                           /* symlink */
+       NULL,                           /* mkdir */
+       NULL,                           /* rmdir */
+       NULL,                           /* mknod */
+       NULL,                           /* rename */
+       NULL,                           /* readlink */
+       NULL,                           /* follow_link */
+       NULL,                           /* readpage */
+       NULL,                           /* writepage */
+       NULL,                           /* bmap - none */
+       cap_info_truncate,              /* truncate */
+       NULL,                           /* permission */
+       NULL                            /* smap */
+};
+
+/*================ File-local functions ================*/
+
+/*
+ * cap_build_meta()
+ *
+ * Build the metadata structure.
+ */
+static void cap_build_meta(struct hfs_cap_info *meta,
+                          struct hfs_cat_entry *entry)
+{
+       memset(meta, 0, sizeof(*meta));
+       memcpy(meta->fi_fndr, &entry->info, 32);
+       if ((entry->type == HFS_CDR_FIL) &&
+           (entry->u.file.flags & HFS_FIL_LOCK)) {
+               /* Couple the locked bit of the file to the
+                  AFP {write,rename,delete} inhibit bits. */
+               hfs_put_hs(HFS_AFP_RDONLY, meta->fi_attr);
+       }
+       meta->fi_magic1 = HFS_CAP_MAGIC1;
+       meta->fi_version = HFS_CAP_VERSION;
+       meta->fi_magic = HFS_CAP_MAGIC;
+       meta->fi_bitmap = HFS_CAP_LONGNAME;
+       memcpy(meta->fi_macfilename, entry->key.CName.Name,
+              entry->key.CName.Len);
+       meta->fi_datemagic = HFS_CAP_DMAGIC;
+       meta->fi_datevalid = HFS_CAP_MDATE | HFS_CAP_CDATE;
+       hfs_put_nl(hfs_m_to_htime(entry->create_date), meta->fi_ctime);
+       hfs_put_nl(hfs_m_to_htime(entry->modify_date), meta->fi_mtime);
+       hfs_put_nl(CURRENT_TIME,                       meta->fi_utime);
+}
+
+/*
+ * cap_info_read()
+ *
+ * This is the read() entry in the file_operations structure for CAP
+ * metadata files.  The purpose is to transfer up to 'count' bytes
+ * from the file corresponding to 'inode' beginning at offset
+ * 'file->f_pos' to user-space at the address 'buf'.  The return value
+ * is the number of bytes actually transferred.
+ */
+static hfs_rwret_t cap_info_read(struct inode *inode, struct file *filp,
+                                char *buf, hfs_rwarg_t count)
+{
+       struct hfs_cat_entry *entry = HFS_I(inode)->entry;
+       hfs_s32 left, size, read = 0;
+       hfs_u32 pos;
+
+       if (!S_ISREG(inode->i_mode)) {
+               hfs_warn("hfs_cap_info_read: mode = %07o\n", inode->i_mode);
+               return -EINVAL;
+       }
+
+       if (filp->f_pos > HFS_FORK_MAX) {
+               return 0;
+       }
+       pos = filp->f_pos;
+       size = inode->i_size;
+       if (pos > size) {
+               left = 0;
+       } else {
+               left = size - pos;
+       }
+       if (left > count) {
+               left = count;
+       }
+       if (left <= 0) {
+               return 0;
+       }
+
+       if (pos < sizeof(struct hfs_cap_info)) {
+               int memcount = sizeof(struct hfs_cap_info) - pos;
+               struct hfs_cap_info meta;
+
+               if (memcount > left) {
+                       memcount = left;
+               }
+               cap_build_meta(&meta, entry);
+               copy_to_user(buf, ((char *)&meta) + pos, memcount);
+               left -= memcount;
+               read += memcount;
+               pos += memcount;
+               buf += memcount;
+       }
+
+       if (left > 0) {
+               read += left;
+               while (left--) {
+                       put_user(0, buf++);
+               }
+       }
+
+       if (read) {
+               inode->i_atime = CURRENT_TIME;
+               filp->f_pos += read;
+       }
+       return read;
+}
+
+/*
+ * cap_info_write()
+ *
+ * This is the write() entry in the file_operations structure for CAP
+ * metadata files.  The purpose is to transfer up to 'count' bytes
+ * to the file corresponding to 'inode' beginning at offset
+ * 'file->f_pos' from user-space at the address 'buf'.
+ * The return value is the number of bytes actually transferred.
+ */
+static hfs_rwret_t cap_info_write(struct inode *inode, struct file *filp,
+                                 const char *buf, hfs_rwarg_t count)
+{
+       hfs_u32 pos;
+
+       if (!S_ISREG(inode->i_mode)) {
+               hfs_warn("hfs_file_write: mode = %07o\n", inode->i_mode);
+               return -EINVAL;
+       }
+       if (count <= 0) {
+               return 0;
+       }
+       if (filp->f_flags & O_APPEND) {
+               filp->f_pos = inode->i_size;
+       }
+       if (filp->f_pos > HFS_FORK_MAX) {
+               return 0;
+       }
+       pos = filp->f_pos;
+
+       filp->f_pos += count;
+       if (filp->f_pos > HFS_FORK_MAX) {
+               filp->f_pos = HFS_FORK_MAX;
+               count = HFS_FORK_MAX - pos;
+       }
+       if (filp->f_pos > inode->i_size) {
+               inode->i_size = filp->f_pos;
+       }
+
+       /* Only deal with the part we store in memory */
+       if (pos < sizeof(struct hfs_cap_info)) {
+               int end, mem_count;
+               struct hfs_cat_entry *entry = HFS_I(inode)->entry;
+               struct hfs_cap_info meta;
+
+               mem_count = sizeof(struct hfs_cap_info) - pos;
+               if (mem_count > count) {
+                       mem_count = count;
+               }
+               end = pos + mem_count;
+
+               cap_build_meta(&meta, entry);
+               copy_from_user(((char *)&meta) + pos, buf, mem_count);
+
+               /* Update finder attributes if changed */
+               if (OVERLAPS(pos, end, struct hfs_cap_info, fi_fndr)) {
+                       memcpy(&entry->info, meta.fi_fndr, 32);
+                       entry->dirt = 1;
+               }
+
+               /* Update file flags if changed */
+               if (OVERLAPS(pos, end, struct hfs_cap_info, fi_attr) &&
+                   (entry->type == HFS_CDR_FIL)) {
+                       int locked = hfs_get_ns(&meta.fi_attr) &
+                                                       htons(HFS_AFP_WRI);
+                       hfs_u8 new_flags;
+
+                       if (locked) {
+                               new_flags = entry->u.file.flags | HFS_FIL_LOCK;
+                       } else {
+                               new_flags = entry->u.file.flags & ~HFS_FIL_LOCK;
+                       }
+
+                       if (new_flags != entry->u.file.flags) {
+                               entry->u.file.flags = new_flags;
+                               entry->dirt = 1;
+                               hfs_file_fix_mode(entry);
+                       }
+               }
+
+               /* Update CrDat if changed */
+               if (OVERLAPS(pos, end, struct hfs_cap_info, fi_ctime)) {
+                       entry->create_date =
+                               hfs_h_to_mtime(hfs_get_nl(meta.fi_ctime));
+                       entry->dirt = 1;
+               }
+
+               /* Update MdDat if changed */
+               if (OVERLAPS(pos, end, struct hfs_cap_info, fi_mtime)) {
+                       entry->modify_date =
+                               hfs_h_to_mtime(hfs_get_nl(meta.fi_mtime));
+                       entry->dirt = 1;
+               }
+       }
+
+       inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+       return count;
+}
+
+/*
+ * cap_info_truncate()
+ *
+ * This is the truncate field in the inode_operations structure for
+ * CAP metadata files.
+ */
+static void cap_info_truncate(struct inode *inode)
+{
+       if (inode->i_size > HFS_FORK_MAX) {
+               inode->i_size = HFS_FORK_MAX;
+       }
+}
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/fs/hfs/file_hdr.c linux-2.0.29/fs/hfs/file_hdr.c
--- linux.vanilla/fs/hfs/file_hdr.c     Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/fs/hfs/file_hdr.c      Thu Apr 10 12:29:37 1997
@@ -0,0 +1,925 @@
+/*
+ * linux/fs/hfs/file_hdr.c
+ *
+ * Copyright (C) 1995-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the file_ops and inode_ops for the metadata
+ * files under the AppleDouble and Netatalk representations.
+ *
+ * The source code distributions of Netatalk, versions 1.3.3b2 and
+ * 1.4b2, were used as a specification of the location and format of
+ * files used by Netatalk's afpd.  No code from Netatalk appears in
+ * hfs_fs.  hfs_fs is not a work ``derived'' from Netatalk in the
+ * sense of intellectual property law.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ *
+ * XXX: Note the reason that there is not bmap() for AppleDouble
+ * header files is that dynamic nature of their structure make it
+ * very difficult to safely mmap them.  Maybe in the distant future
+ * I'll get bored enough to implement it.
+ */
+
+#include <linux/hfs.h>
+#include <linux/hfs_fs_sb.h>
+#include <linux/hfs_fs_i.h>
+#include <linux/hfs_fs.h>
+
+/*================ Forward declarations ================*/
+
+static hfs_rwret_t hdr_read(struct inode *, struct file *, char *, hfs_rwarg_t);
+static hfs_rwret_t hdr_write(struct inode *, struct file *, const char *,
+                            hfs_rwarg_t);
+static void hdr_truncate(struct inode *);
+
+/*================ Global variables ================*/
+
+static struct file_operations hfs_hdr_operations = {
+       NULL,                   /* lseek - default */
+       hdr_read,               /* read */
+       hdr_write,              /* write */
+       NULL,                   /* readdir - bad */
+       NULL,                   /* select - default */
+       NULL,                   /* ioctl - default */
+       NULL,                   /* mmap - XXX: not yet */
+       NULL,                   /* no special open code */
+       NULL,                   /* no special release code */
+       file_fsync,             /* fsync - default */
+        NULL,                  /* fasync - default */
+        NULL,                  /* check_media_change - none */
+        NULL                   /* revalidate - none */
+};
+
+struct inode_operations hfs_hdr_inode_operations = {
+       &hfs_hdr_operations,    /* default file operations */
+       NULL,                   /* create */
+       NULL,                   /* lookup */
+       NULL,                   /* link */
+       NULL,                   /* unlink */
+       NULL,                   /* symlink */
+       NULL,                   /* mkdir */
+       NULL,                   /* rmdir */
+       NULL,                   /* mknod */
+       NULL,                   /* rename */
+       NULL,                   /* readlink */
+       NULL,                   /* follow_link */
+       NULL,                   /* readpage */
+       NULL,                   /* writepage */
+       NULL,                   /* bmap - XXX: not available since
+                                  header part has no disk block */
+       hdr_truncate,           /* truncate */
+       NULL,                   /* permission */
+       NULL                    /* smap */
+};
+
+const struct hfs_hdr_layout hfs_dbl_fil_hdr_layout = {
+       __constant_htonl(HFS_DBL_MAGIC),        /* magic   */
+       __constant_htonl(HFS_HDR_VERSION_2),    /* version */
+       5,                                      /* entries */
+       {                                       /* descr[] */
+               {HFS_HDR_DATES, offsetof(struct hfs_dbl_hdr, create_time), 16},
+               {HFS_HDR_FINFO, offsetof(struct hfs_dbl_hdr, finderinfo),  32},
+               {HFS_HDR_MACI,  offsetof(struct hfs_dbl_hdr, fileinfo),    4},
+               {HFS_HDR_FNAME, offsetof(struct hfs_dbl_hdr, real_name),   ~0},
+               {HFS_HDR_RSRC,  HFS_DBL_HDR_LEN,                           ~0},
+       },
+       {                                       /* order[] */
+               (struct hfs_hdr_descr *)&hfs_dbl_fil_hdr_layout.descr[0],
+               (struct hfs_hdr_descr *)&hfs_dbl_fil_hdr_layout.descr[1],
+               (struct hfs_hdr_descr *)&hfs_dbl_fil_hdr_layout.descr[2],
+               (struct hfs_hdr_descr *)&hfs_dbl_fil_hdr_layout.descr[3],
+               (struct hfs_hdr_descr *)&hfs_dbl_fil_hdr_layout.descr[4],
+       }
+};
+
+const struct hfs_hdr_layout hfs_dbl_dir_hdr_layout = {
+       __constant_htonl(HFS_DBL_MAGIC),        /* magic   */
+       __constant_htonl(HFS_HDR_VERSION_2),    /* version */
+       4,                                      /* entries */
+       {                                       /* descr[] */
+               {HFS_HDR_DATES, offsetof(struct hfs_dbl_hdr, create_time), 16},
+               {HFS_HDR_FINFO, offsetof(struct hfs_dbl_hdr, finderinfo),  32},
+               {HFS_HDR_MACI,  offsetof(struct hfs_dbl_hdr, fileinfo),    4},
+               {HFS_HDR_FNAME, offsetof(struct hfs_dbl_hdr, real_name),   ~0},
+       },
+       {                                       /* order[] */
+               (struct hfs_hdr_descr *)&hfs_dbl_dir_hdr_layout.descr[0],
+               (struct hfs_hdr_descr *)&hfs_dbl_dir_hdr_layout.descr[1],
+               (struct hfs_hdr_descr *)&hfs_dbl_dir_hdr_layout.descr[2],
+               (struct hfs_hdr_descr *)&hfs_dbl_dir_hdr_layout.descr[3],
+       }
+};
+
+const struct hfs_hdr_layout hfs_nat_hdr_layout = {
+       __constant_htonl(HFS_DBL_MAGIC),        /* magic   */
+       __constant_htonl(HFS_HDR_VERSION_1),    /* version */
+       5,                                      /* entries */
+       {                                       /* descr[] */
+               {HFS_HDR_RSRC,  HFS_NAT_HDR_LEN,                           ~0},
+               {HFS_HDR_FNAME, offsetof(struct hfs_nat_hdr, real_name),   ~0},
+               {HFS_HDR_COMNT, offsetof(struct hfs_nat_hdr, comment),     0},
+               {HFS_HDR_OLDI,  offsetof(struct hfs_nat_hdr, create_time), 16},
+               {HFS_HDR_FINFO, offsetof(struct hfs_nat_hdr, finderinfo),  32},
+       },
+       {                                       /* order[] */
+               (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[1],
+               (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[2],
+               (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[3],
+               (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[4],
+               (struct hfs_hdr_descr *)&hfs_nat_hdr_layout.descr[0],
+       }
+};
+
+/*================ File-local variables ================*/
+
+static const char fstype[16] =
+       {'M','a','c','i','n','t','o','s','h',' ',' ',' ',' ',' ',' ',' '};
+
+/*================ File-local data types ================*/
+
+struct hdr_hdr {
+        hfs_lword_t    magic;
+        hfs_lword_t    version;
+        hfs_byte_t     filler[16];
+        hfs_word_t     entries;
+        hfs_byte_t     descrs[12*HFS_HDR_MAX];
+};
+
+/*================ File-local functions ================*/
+
+/*
+ * dlength()
+ */
+static int dlength(const struct hfs_hdr_descr *descr,
+                  const struct hfs_cat_entry *entry)
+{
+       hfs_u32 length = descr->length;
+
+       /* handle auto-sized entries */
+       if (length == ~0) {
+               switch (descr->id) {
+               case HFS_HDR_DATA:
+                       if (entry->type == HFS_CDR_FIL) {
+                               length = entry->u.file.data_fork.lsize;
+                       } else {
+                               length = 0;
+                       }
+                       break;
+
+               case HFS_HDR_RSRC:
+                       if (entry->type == HFS_CDR_FIL) {
+                               length = entry->u.file.rsrc_fork.lsize;
+                       } else {
+                               length = 0;
+                       }
+                       break;
+
+               case HFS_HDR_FNAME:
+                       length = entry->key.CName.Len;
+                       break;
+
+               default:
+                       length = 0;
+               }
+       }
+       return length;
+}
+
+/*
+ * hdr_build_meta()
+ */
+static void hdr_build_meta(struct hdr_hdr *meta,
+                          const struct hfs_hdr_layout *layout,
+                          const struct hfs_cat_entry *entry)
+{
+       const struct hfs_hdr_descr *descr;
+       hfs_byte_t *ptr;
+       int lcv;
+
+       hfs_put_nl(layout->magic,   meta->magic);
+       hfs_put_nl(layout->version, meta->version);
+       if (layout->version == htonl(HFS_HDR_VERSION_1)) {
+               memcpy(meta->filler, fstype, 16);
+       } else {
+               memset(meta->filler, 0, 16);
+       }
+       hfs_put_hs(layout->entries, meta->entries);
+       memset(meta->descrs, 0, sizeof(meta->descrs));
+       for (lcv = 0, descr = layout->descr, ptr = meta->descrs;
+            lcv < layout->entries; ++lcv, ++descr, ptr += 12) {
+               hfs_put_hl(descr->id,             ptr);
+               hfs_put_hl(descr->offset,         ptr + 4);
+               hfs_put_hl(dlength(descr, entry), ptr + 8);
+       }
+}
+
+/*
+ * dup_layout ()
+ */
+static struct hfs_hdr_layout *dup_layout(const struct hfs_hdr_layout *old)
+{
+       struct hfs_hdr_layout *new;
+       int lcv;
+
+       if (HFS_NEW(new)) {
+               memcpy(new, old, sizeof(*new));
+               for (lcv = 0; lcv < new->entries; ++lcv) {
+                       (char *)(new->order[lcv]) += (char *)new - (char *)old;
+               }
+       }
+       return new;
+}
+
+/*
+ * init_layout()
+ */
+static inline void init_layout(struct hfs_hdr_layout *layout,
+                              const hfs_byte_t *descrs)
+{
+       struct hfs_hdr_descr **base, **p, **q, *tmp;
+       int lcv, entries = layout->entries;
+
+       for (lcv = 0; lcv < entries; ++lcv, descrs += 12) {
+               layout->order[lcv] = &layout->descr[lcv];
+               layout->descr[lcv].id     = hfs_get_hl(descrs);
+               layout->descr[lcv].offset = hfs_get_hl(descrs + 4);
+               layout->descr[lcv].length = hfs_get_hl(descrs + 8);
+       }
+       for (lcv = layout->entries; lcv < HFS_HDR_MAX; ++lcv) {
+               layout->order[lcv] = NULL;
+               layout->descr[lcv].id     = 0;
+               layout->descr[lcv].offset = 0;
+               layout->descr[lcv].length = 0;
+       }
+
+       /* Sort the 'order' array using an insertion sort */
+       base = &layout->order[0];
+       for (p = (base+1); p < (base+entries); ++p) {
+               q=p;
+               while ((*q)->offset < (*(q-1))->offset) {
+                       tmp = *q;
+                       *q = *(q-1);
+                       *(--q) = tmp;
+                       if (q == base) break;
+               }
+       }
+}
+
+/*
+ * adjust_forks()
+ */
+static inline void adjust_forks(struct hfs_cat_entry *entry,
+                               const struct hfs_hdr_layout *layout)
+{
+       int lcv;
+
+       for (lcv = 0; lcv < layout->entries; ++lcv) {
+               const struct hfs_hdr_descr *descr = &layout->descr[lcv];
+
+               if ((descr->id == HFS_HDR_DATA) &&
+                   (descr->length != entry->u.file.data_fork.lsize)) {
+                       entry->u.file.data_fork.lsize = descr->length;
+                       hfs_extent_adj(&entry->u.file.data_fork);
+               } else if ((descr->id == HFS_HDR_RSRC) &&
+                          (descr->length != entry->u.file.rsrc_fork.lsize)) {
+                       entry->u.file.rsrc_fork.lsize = descr->length;
+                       hfs_extent_adj(&entry->u.file.rsrc_fork);
+               }
+       }
+}
+
+/*
+ * get_dates()
+ */
+static void get_dates(const struct hfs_cat_entry *entry,
+                     const struct inode *inode,  hfs_u32 dates[3])
+{
+       if (HFS_SB(inode->i_sb)->s_afpd) {
+               dates[0] = htonl(hfs_m_to_utime(entry->create_date));
+               dates[1] = htonl(hfs_m_to_utime(entry->modify_date));
+               dates[2] = htonl(hfs_m_to_utime(entry->backup_date));
+       } else {
+               dates[0] = hfs_m_to_htime(entry->create_date);
+               dates[1] = hfs_m_to_htime(entry->modify_date);
+               dates[2] = hfs_m_to_htime(entry->backup_date);
+       }
+}
+
+/*
+ * set_dates()
+ */
+static void set_dates(struct hfs_cat_entry *entry, struct inode *inode,
+                     const hfs_u32 *dates)
+{
+       hfs_u32 tmp;
+       if (HFS_SB(inode->i_sb)->s_afpd) {
+               tmp = hfs_u_to_mtime(ntohl(dates[0]));
+               if (entry->create_date != tmp) {
+                       entry->create_date = tmp;
+                       entry->dirt = 1;
+               }
+               tmp = hfs_u_to_mtime(ntohl(dates[1]));
+               if (entry->modify_date != tmp) {
+                       entry->modify_date = tmp;
+                       inode->i_ctime = inode->i_atime = inode->i_mtime =
+                               ntohl(dates[1]);
+                       entry->dirt = 1;
+               }
+               tmp = hfs_u_to_mtime(ntohl(dates[2]));
+               if (entry->backup_date != tmp) {
+                       entry->backup_date = tmp;
+                       entry->dirt = 1;
+               }
+       } else {
+               tmp = hfs_h_to_mtime(dates[0]);
+               if (entry->create_date != tmp) {
+                       entry->create_date = tmp;
+                       entry->dirt = 1;
+               }
+               tmp = hfs_h_to_mtime(dates[1]);
+               if (entry->modify_date != tmp) {
+                       entry->modify_date = tmp;
+                       inode->i_ctime = inode->i_atime = inode->i_mtime =
+                               hfs_h_to_utime(dates[1]);
+                       entry->dirt = 1;
+               }
+               tmp = hfs_h_to_mtime(dates[2]);
+               if (entry->backup_date != tmp) {
+                       entry->backup_date = tmp;
+                       entry->dirt = 1;
+               }
+       }
+}
+
+/*
+ * hdr_read()
+ *
+ * This is the read field in the inode_operations structure for
+ * header files.  The purpose is to transfer up to 'count' bytes
+ * from the file corresponding to 'inode', beginning at
+ * 'filp->offset' bytes into the file. The data is transfered to
+ * user-space at the address 'buf'.  Returns the number of bytes
+ * successfully transfered.
+ */
+/* XXX: what about the entry count changing on us? */
+static hfs_rwret_t hdr_read(struct inode * inode, struct file * filp,
+                           char * buf, hfs_rwarg_t count)
+{
+       struct hfs_cat_entry *entry = HFS_I(inode)->entry;
+       const struct hfs_hdr_layout *layout;
+       off_t start, length, offset;
+       off_t pos = filp->f_pos;
+       int left, lcv, read = 0;
+
+       if (!S_ISREG(inode->i_mode)) {
+               hfs_warn("hfs_hdr_read: mode = %07o\n",inode->i_mode);
+               return -EINVAL;
+       }
+
+       if (HFS_I(inode)->layout) {
+               layout = HFS_I(inode)->layout;
+       } else {
+               layout = HFS_I(inode)->default_layout;
+       }
+
+       /* Adjust count to fit within the bounds of the file */
+       if ((pos >= inode->i_size) || (count <= 0)) {
+               return 0;
+       } else if (count > inode->i_size - pos) {
+               count = inode->i_size - pos;
+       }
+
+       /* Handle the fixed-location portion */
+       length = sizeof(hfs_u32) + sizeof(hfs_u32) + 16 +
+                sizeof(hfs_u16) + layout->entries * (3 * sizeof(hfs_u32));
+       if (pos < length) {
+               struct hdr_hdr meta;
+
+               left = length - pos;
+               if (left > count) {
+                       left = count;
+               }
+
+               hdr_build_meta(&meta, layout, entry);
+               copy_to_user(buf, ((char *)&meta) + pos, left);
+               count -= left;
+               read += left;
+               pos += left;
+               buf += left;
+       }
+       if (!count) {
+               goto done;
+       }
+
+       /* Handle the actual data */
+       for (lcv = 0; count && (lcv < layout->entries); ++lcv) {
+               const struct hfs_hdr_descr *descr = layout->order[lcv];
+               struct hfs_fork *fork;
+               char tmp[16], *p;
+               off_t limit;
+
+               /* stop reading if we run out of descriptors early */
+               if (!descr) {
+                       break;
+               }
+
+               /* find start and length of this entry */
+               start = descr->offset;
+               length = dlength(descr, entry);
+
+               /* Skip to next entry if this one is empty or isn't needed */
+               if (!length || (pos >= start + length)) {
+                       continue;
+               }
+
+               /* Pad with zeros to the start of this entry if needed */
+               if (pos < start) {
+                       left = start - pos;
+                       if (left > count) {
+                               left = count;
+                       }
+                       clear_user(buf, left);
+                       count -= left;
+                       read += left;
+                       pos += left;
+                       buf += left;
+               }
+               if (!count) {
+                       goto done;
+               }
+
+               /* locate and/or construct the data for this entry */
+               fork = NULL;
+               p = NULL;
+               switch (descr->id) {
+               case HFS_HDR_DATA:
+                       fork = &entry->u.file.data_fork;
+                       limit = fork->lsize;
+                       break;
+
+               case HFS_HDR_RSRC:
+                       fork = &entry->u.file.rsrc_fork;
+                       limit = fork->lsize;
+                       break;
+
+               case HFS_HDR_FNAME:
+                       p = entry->key.CName.Name;
+                       limit = entry->key.CName.Len;
+                       break;
+
+               case HFS_HDR_OLDI:
+               case HFS_HDR_DATES:
+                       get_dates(entry, inode, (hfs_u32 *)tmp);
+                       if (descr->id == HFS_HDR_DATES) {
+                               memcpy(tmp + 12, tmp + 4, 4);
+                       } else if ((entry->type == HFS_CDR_FIL) &&
+                                  (entry->u.file.flags & HFS_FIL_LOCK)) {
+                               hfs_put_hl(HFS_AFP_RDONLY, tmp + 12);
+                       } else {
+                               hfs_put_nl(0, tmp + 12);
+                       }
+                       p = tmp;
+                       limit = 16;
+                       break;
+
+               case HFS_HDR_FINFO:
+                       p = (char *)&entry->info;
+                       limit = 32;
+                       break;
+
+               case HFS_HDR_MACI:
+                       hfs_put_ns(0, tmp);
+                       if (entry->type == HFS_CDR_FIL) {
+                               hfs_put_hs(entry->u.file.flags, tmp + 2);
+                       } else {
+                               hfs_put_ns(entry->u.dir.flags, tmp + 2);
+                       }
+                       p = tmp;
+                       limit = 4;
+                       break;
+
+               default:
+                       limit = 0;
+               }
+
+               /* limit the transfer to the available data
+                  of to the stated length of the entry. */
+               if (length > limit) {
+                       length = limit;
+               }
+               offset = pos - start;
+               left = length - offset;
+               if (left > count) {
+                       left = count;
+               }
+               if (left <= 0) {
+                       continue;
+               }
+
+               /* transfer the data */
+               if (p) {
+                       copy_to_user(buf, p + offset, left);
+               } else if (fork) {
+                       left = hfs_do_read(inode, fork, offset, buf, left,
+                                          filp->f_reada != 0);
+                       if (left > 0) {
+                               filp->f_reada = 1;
+                       } else if (!read) {
+                               return left;
+                       } else {
+                               goto done;
+                       }
+               }
+               count -= left;
+               read += left;
+               pos += left;
+               buf += left;
+       }
+
+       /* Pad the file out with zeros */
+       if (count) {
+               clear_user(buf, count);
+               read += count;
+       }
+
+done:
+       if (read) {
+               inode->i_atime = CURRENT_TIME;
+               filp->f_pos += read;
+       }
+       return read;
+}
+
+/*
+ * hdr_write()
+ *
+ * This is the write() entry in the file_operations structure for
+ * header files.  The purpose is to transfer up to 'count' bytes
+ * to the file corresponding to 'inode' beginning at offset
+ * 'file->f_pos' from user-space at the address 'buf'.
+ * The return value is the number of bytes actually transferred.
+ */
+static hfs_rwret_t hdr_write(struct inode *inode, struct file *filp,
+                            const char *buf, hfs_rwarg_t count)
+{
+        struct hfs_cat_entry *entry = HFS_I(inode)->entry;
+        struct hfs_hdr_layout *layout;
+        off_t start, length, offset;
+        int left, lcv, written = 0;
+       struct hdr_hdr meta;
+       int built_meta = 0;
+        off_t pos;
+
+       if (!S_ISREG(inode->i_mode)) {
+               hfs_warn("hfs_hdr_write: mode = %07o\n", inode->i_mode);
+               return -EINVAL;
+       }
+       if (count <= 0) {
+               return 0;
+       }
+       if (filp->f_flags & O_APPEND) {
+               filp->f_pos = inode->i_size;
+       }
+       pos = filp->f_pos;
+
+       if (!HFS_I(inode)->layout) {
+               HFS_I(inode)->layout = dup_layout(HFS_I(inode)->default_layout);
+       }
+       layout = HFS_I(inode)->layout;
+
+       /* Handle the 'magic', 'version', 'filler' and 'entries' fields */
+       length = sizeof(hfs_u32) + sizeof(hfs_u32) + 16 + sizeof(hfs_u16);
+       if (pos < length) {
+               hdr_build_meta(&meta, layout, entry);
+               built_meta = 1;
+
+               left = length - pos;
+               if (left > count) {
+                       left = count;
+               }
+
+               copy_from_user(((char *)&meta) + pos, buf, left);
+               layout->magic   = hfs_get_nl(meta.magic);
+               layout->version = hfs_get_nl(meta.version);
+               layout->entries = hfs_get_hs(meta.entries);
+               if (layout->entries > HFS_HDR_MAX) {
+                       /* XXX: should allocate slots dynamically */
+                       hfs_warn("hfs_hdr_write: TRUNCATING TO %d "
+                                "DESCRIPTORS\n", HFS_HDR_MAX);
+                       layout->entries = HFS_HDR_MAX;
+               }
+
+               count -= left;
+               written += left;
+               pos += left;
+               buf += left;
+       }
+       if (!count) {
+               goto done;
+       }
+
+       /* We know for certain how many entries we have, so process them */
+       length += layout->entries * 3 * sizeof(hfs_u32);
+       if (pos < length) {
+               if (!built_meta) {
+                       hdr_build_meta(&meta, layout, entry);
+               }
+
+               left = length - pos;
+               if (left > count) {
+                       left = count;
+               }
+
+               copy_from_user(((char *)&meta) + pos, buf, left);
+               init_layout(layout, meta.descrs);
+
+               count -= left;
+               written += left;
+               pos += left;
+               buf += left;
+
+               /* Handle possible size changes for the forks */
+               if (entry->type == HFS_CDR_FIL) {
+                       adjust_forks(entry, layout);
+               }
+       }
+
+       /* Handle the actual data */
+       for (lcv = 0; count && (lcv < layout->entries); ++lcv) {
+               struct hfs_hdr_descr *descr = layout->order[lcv];
+               struct hfs_fork *fork;
+               char tmp[16], *p;
+               off_t limit;
+
+               /* stop writing if we run out of descriptors early */
+               if (!descr) {
+                       break;
+               }
+
+               /* find start and length of this entry */
+               start = descr->offset;
+               if ((descr->id == HFS_HDR_DATA) ||
+                   (descr->id == HFS_HDR_RSRC)) {
+                       if (entry->type == HFS_CDR_FIL) {
+                               length = 0x7fffffff - start;
+                       } else {
+                               continue;
+                       }
+               } else {
+                       length = dlength(descr, entry);
+               }
+
+               /* Trim length to avoid overlap with the next entry */
+               if (layout->order[lcv+1] &&
+                   ((start + length) > layout->order[lcv+1]->offset)) {
+                       length = layout->order[lcv+1]->offset - start;
+               }
+
+               /* Skip to next entry if this one is empty or isn't needed */
+               if (!length || (pos >= start + length)) {
+                       continue;
+               }
+
+               /* Skip any padding that may exist between entries */
+               if (pos < start) {
+                       left = start - pos;
+                       if (left > count) {
+                               left = count;
+                       }
+                       count -= left;
+                       written += left;
+                       pos += left;
+                       buf += left;
+               }
+               if (!count) {
+                       goto done;
+               }
+
+               /* locate and/or construct the data for this entry */
+               fork = NULL;
+               p = NULL;
+               switch (descr->id) {
+               case HFS_HDR_DATA:
+#if 0
+/* Can't yet write to the data fork via a header file, since there is the
+ * possibility to write via the data file, and the only locking is at the
+ * inode level.
+ */
+                       fork = &entry->u.file.data_fork;
+                       limit = length;
+#else
+                       limit = 0;
+#endif
+                       break;
+
+               case HFS_HDR_RSRC:
+                       fork = &entry->u.file.rsrc_fork;
+                       limit = length;
+                       break;
+
+               case HFS_HDR_OLDI:
+               case HFS_HDR_DATES:
+                       get_dates(entry, inode, (hfs_u32 *)tmp);
+                       if (descr->id == HFS_HDR_DATES) {
+                               memcpy(tmp + 12, tmp + 4, 4);
+                       } else if ((entry->type == HFS_CDR_FIL) &&
+                                  (entry->u.file.flags & HFS_FIL_LOCK)) {
+                               hfs_put_hl(HFS_AFP_RDONLY, tmp + 12);
+                       } else {
+                               hfs_put_nl(0, tmp + 12);
+                       }
+                       p = tmp;
+                       limit = 16;
+                       break;
+
+               case HFS_HDR_FINFO:
+                       p = (char *)&entry->info;
+                       limit = 32;
+                       break;
+
+               case HFS_HDR_MACI:
+                       hfs_put_ns(0, tmp);
+                       if (entry->type == HFS_CDR_FIL) {
+                               hfs_put_hs(entry->u.file.flags, tmp + 2);
+                       } else {
+                               hfs_put_ns(entry->u.dir.flags, tmp + 2);
+                       }
+                       p = tmp;
+                       limit = 4;
+                       break;
+
+               case HFS_HDR_FNAME:     /* Can't rename a file this way */
+               default:
+                       limit = 0;
+               }
+
+               /* limit the transfer to the available data
+                  of to the stated length of the entry. */
+               if (length > limit) {
+                       length = limit;
+               }
+               offset = pos - start;
+               left = length - offset;
+               if (left > count) {
+                       left = count;
+               }
+               if (left <= 0) {
+                       continue;
+               }
+
+               /* transfer the data from user space */
+               if (p) {
+                       copy_from_user(p + offset, buf, left);
+               } else if (fork) {
+                       left = hfs_do_write(inode, fork, offset, buf, left);
+               }
+
+               /* process the data */
+               switch (descr->id) {
+               case HFS_HDR_OLDI:
+                       set_dates(entry, inode, (hfs_u32 *)tmp);
+                       if (entry->type == HFS_CDR_FIL) {
+                               hfs_u8 new_flags = entry->u.file.flags;
+
+                               if (hfs_get_nl(tmp+12) & htonl(HFS_AFP_WRI)) {
+                                       new_flags |= HFS_FIL_LOCK;
+                               } else {
+                                       new_flags &= ~HFS_FIL_LOCK;
+                               }
+
+                               if (new_flags != entry->u.file.flags) {
+                                       entry->u.file.flags = new_flags;
+                                       entry->dirt = 1;
+                                       hfs_file_fix_mode(entry);
+                               }
+                       }
+                       break;
+
+               case HFS_HDR_DATES:
+                       set_dates(entry, inode, (hfs_u32 *)tmp);
+                       break;
+
+               case HFS_HDR_FINFO:
+                       entry->dirt = 1;
+                       break;
+
+               case HFS_HDR_MACI:
+                       if (entry->type == HFS_CDR_DIR) {
+                               hfs_u16 new_flags = hfs_get_ns(tmp + 2);
+
+                               if (entry->u.dir.flags != new_flags) {
+                                       entry->u.dir.flags = new_flags;
+                                       entry->dirt = 1;
+                               }
+                       } else {
+                               hfs_u8 new_flags = tmp[3];
+                               hfs_u8 changed = entry->u.file.flags^new_flags;
+
+                               if (changed) {
+                                       entry->u.file.flags = new_flags;
+                                       entry->dirt = 1;
+                                       if (changed & HFS_FIL_LOCK) {
+                                               hfs_file_fix_mode(entry);
+                                       }
+                               }
+                       }
+                       break;
+
+               case HFS_HDR_DATA:
+               case HFS_HDR_RSRC:
+                       if (left <= 0) {
+                               if (!written) {
+                                       return left;
+                               } else {
+                                       goto done;
+                               }
+                       } else if (fork->lsize > descr->length) {
+                               descr->length = fork->lsize;
+                       }
+                       break;
+
+               case HFS_HDR_FNAME:     /* Can't rename a file this way */
+               default:
+                       break;
+               }
+
+               count -= left;
+               written += left;
+               pos += left;
+               buf += left;
+       }
+
+       /* Skip any padding at the end */
+       if (count) {
+               written += count;
+       }
+
+done:
+       if (written > 0) {
+               inode->i_mtime = inode->i_atime = CURRENT_TIME;
+               filp->f_pos += written;
+               if (filp->f_pos > inode->i_size) {
+                       inode->i_size = filp->f_pos;
+               }
+       }
+       return written;
+}
+
+/*
+ * hdr_truncate()
+ *
+ * This is the truncate field in the inode_operations structure for
+ * header files.  The purpose is to allocate or release blocks as needed
+ * to satisfy a change in file length.
+ */
+static void hdr_truncate(struct inode *inode)
+{
+       struct hfs_hdr_layout *layout;
+       size_t size = inode->i_size;
+       int lcv, last;
+
+       if (!HFS_I(inode)->layout) {
+               HFS_I(inode)->layout = dup_layout(HFS_I(inode)->default_layout);
+       }
+       layout = HFS_I(inode)->layout;
+
+       last = layout->entries - 1;
+       for (lcv = 0; lcv <= last; ++lcv) {
+               struct hfs_hdr_descr *descr = layout->order[lcv];
+               struct hfs_fork *fork;
+               hfs_u32 offset;
+
+               if (!descr) {
+                       break;
+               }
+
+               if (descr->id == HFS_HDR_DATA) {
+                       fork = &HFS_I(inode)->entry->u.file.data_fork;
+               } else if (descr->id == HFS_HDR_RSRC) {
+                       fork = &HFS_I(inode)->entry->u.file.rsrc_fork;
+               } else {
+                       continue;
+               }
+
+               offset = descr->offset;
+
+               if ((lcv != last) && ((offset + descr->length) <= size)) {
+                       continue;
+               }
+
+               if (offset < size) {
+                       descr->length = size - offset;
+               } else {
+                       descr->length = 0;
+               }
+               if (fork->lsize != descr->length) {
+                       fork->lsize = descr->length;
+                       hfs_extent_adj(fork);
+               }
+       }
+}
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/fs/hfs/inode.c linux-2.0.29/fs/hfs/inode.c
--- linux.vanilla/fs/hfs/inode.c        Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/fs/hfs/inode.c Thu Apr 10 12:32:07 1997
@@ -0,0 +1,425 @@
+/*
+ * linux/fs/hfs/inode.c
+ *
+ * Copyright (C) 1995-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains inode-related functions which do not depend on
+ * which scheme is being used to represent forks.
+ *
+ * Based on the minix file system code, (C) 1991, 1992 by Linus Torvalds
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include <linux/hfs.h>
+#include <linux/hfs_fs_sb.h>
+#include <linux/hfs_fs_i.h>
+#include <linux/hfs_fs.h>
+
+/*================ Variable-like macros ================*/
+
+#define HFS_VALID_MODE_BITS  (S_IFREG | S_IFDIR | S_IRWXUGO)
+
+/*================ File-local functions ================*/
+
+/*
+ * init_file_inode()
+ *
+ * Given an HFS catalog entry initialize an inode for a file.
+ */
+static void init_file_inode(struct inode *inode, hfs_u8 fork)
+{
+       struct hfs_fork *fk;
+       struct hfs_cat_entry *entry = HFS_I(inode)->entry;
+
+       if (!IS_NOEXEC(inode) && (fork == HFS_FK_DATA)) {
+               inode->i_mode = S_IRWXUGO | S_IFREG;
+       } else {
+               inode->i_mode = S_IRUGO | S_IWUGO | S_IFREG;
+       }
+
+       if (fork == HFS_FK_DATA) {
+               hfs_u32 type = hfs_get_nl(entry->info.file.finfo.fdType);
+
+               fk = &entry->u.file.data_fork;
+               HFS_I(inode)->convert =
+                       ((HFS_SB(inode->i_sb)->s_conv == 't') ||
+                        ((HFS_SB(inode->i_sb)->s_conv == 'a') &&
+                         ((type == htonl(0x54455854)) ||   /* "TEXT" */
+                          (type == htonl(0x7474726f)))));  /* "ttro" */
+       } else {
+               fk = &entry->u.file.rsrc_fork;
+               HFS_I(inode)->convert = 0;
+       }
+       HFS_I(inode)->fork = fk;
+       inode->i_size = fk->lsize;
+       inode->i_blocks = fk->psize;
+       inode->i_nlink = 1;
+}
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_put_inode()
+ *
+ * This is the put_inode() entry in the super_operations for HFS
+ * filesystems.  The purpose is to perform any filesystem-dependent
+ * cleanup necessary when the use-count of an inode falls to zero.
+ */
+void hfs_put_inode(struct inode * inode)
+{
+       struct hfs_cat_entry *entry = HFS_I(inode)->entry;
+
+       entry->sys_entry[HFS_ITYPE_TO_INT(HFS_ITYPE(inode->i_ino))] = NULL;
+       hfs_cat_put(entry);
+
+       if (inode->i_count == 1) {
+               struct hfs_hdr_layout *tmp = HFS_I(inode)->layout;
+               if (tmp) {
+                       HFS_I(inode)->layout = NULL;
+                       HFS_DELETE(tmp);
+               }
+       }
+
+       if (inode->i_count == 1) {
+               clear_inode(inode);
+       }
+}
+
+/*
+ * hfs_notify_change()
+ *
+ * Based very closely on fs/msdos/inode.c by Werner Almesberger
+ *
+ * This is the notify_change() field in the super_operations structure
+ * for HFS file systems.  The purpose is to take that changes made to
+ * an inode and apply then in a filesystem-dependent manner.  In this
+ * case the process has a few of tasks to do:
+ *  1) prevent changes to the i_uid and i_gid fields.
+ *  2) map file permissions to the closest allowable permissions
+ *  3) Since multiple Linux files can share the same on-disk inode under
+ *     HFS (for instance the data and resource forks of a file) a change
+ *     to permissions must be applied to all other in-core inodes which
+ *     correspond to the same HFS file.
+ */
+int hfs_notify_change(struct inode * inode, struct iattr * attr)
+{
+       struct hfs_cat_entry *entry = HFS_I(inode)->entry;
+       struct inode **inodes = entry->sys_entry;
+       struct hfs_sb_info *hsb = HFS_SB(inode->i_sb);
+       int error, i;
+
+       error = inode_change_ok(inode, attr); /* basic permission checks */
+       if (error) {
+               /* Let netatalk's afpd think chmod() always succeeds */
+               if (hsb->s_afpd &&
+                   (attr->ia_valid == (ATTR_MODE | ATTR_CTIME))) {
+                       return 0;
+               } else {
+                       return error;
+               }
+       }
+
+       /* no uig/gid changes and limit which mode bits can be set */
+       if (((attr->ia_valid & ATTR_UID) &&
+            (attr->ia_uid != hsb->s_uid)) ||
+           ((attr->ia_valid & ATTR_GID) &&
+            (attr->ia_gid != hsb->s_gid)) ||
+           ((attr->ia_valid & ATTR_MODE) &&
+            (((entry->type == HFS_CDR_DIR) &&
+              (attr->ia_mode != inode->i_mode))||
+             (attr->ia_mode & ~HFS_VALID_MODE_BITS)))) {
+               return hsb->s_quiet ? 0 : error;
+       }
+
+       if (entry->type == HFS_CDR_DIR) {
+               attr->ia_valid &= ~ATTR_MODE;
+       } else if (attr->ia_valid & ATTR_MODE) {
+               /* Only the 'w' bits can ever change and only all together. */
+               if (attr->ia_mode & S_IWUSR) {
+                       attr->ia_mode = inode->i_mode | S_IWUGO;
+               } else {
+                       attr->ia_mode = inode->i_mode & ~S_IWUGO;
+               }
+               attr->ia_mode &= ~hsb->s_umask;
+       }
+
+       inode_setattr(inode, attr);
+       inode->i_dirt = 0;
+
+       /* We wouldn't want to mess with the sizes of the other fork */
+       attr->ia_valid &= ~ATTR_SIZE;
+
+       /* We must change all in-core inodes corresponding to this file. */
+       for (i = 0; i < 4; ++i) {
+               if (inodes[i] && (inodes[i] != inode)) {
+                       inode_setattr(inodes[i], attr);
+                       inodes[i]->i_dirt = 0;
+               }
+       }
+
+       /* Change the catalog entry if needed */
+       if (attr->ia_valid & ATTR_MTIME) {
+               entry->modify_date = hfs_u_to_mtime(inode->i_mtime);
+               entry->dirt = 1;
+       }
+       if (attr->ia_valid & ATTR_MODE) {
+               hfs_u8 new_flags;
+
+               if (inode->i_mode & S_IWUSR) {
+                       new_flags = entry->u.file.flags & ~HFS_FIL_LOCK;
+               } else {
+                       new_flags = entry->u.file.flags | HFS_FIL_LOCK;
+               }
+
+               if (new_flags != entry->u.file.flags) {
+                       entry->u.file.flags = new_flags;
+                       entry->dirt = 1;
+               }
+       }
+       /* size changes handled in hfs_extent_adj() */
+
+       return 0;
+}
+
+/*
+ * __hfs_iget()
+ *
+ * Given the MDB for a HFS filesystem, a 'key' and an 'entry' in
+ * the catalog B-tree and the 'type' of the desired file return the
+ * inode for that file/directory or NULL.  Note that 'type' indicates
+ * whether we want the actual file or directory, or the corresponding
+ * metadata (AppleDouble header file or CAP metadata file).
+ *
+ * In an ideal world we could call iget() and would not need this
+ * function.  However, since there is no way to even know the inode
+ * number until we've found the file/directory in the catalog B-tree
+ * that simply won't happen.
+ *
+ * The main idea here is to look in the catalog B-tree to get the
+ * vital info about the file or directory (including the file id which
+ * becomes the inode number) and then to call iget() and return the
+ * inode if it is complete.  If it is not then we use the catalog
+ * entry to fill in the missing info, by calling the appropriate
+ * 'fillin' function.  Note that these fillin functions are
+ * essentially hfs_*_read_inode() functions, but since there is no way
+ * to pass the catalog entry through iget() to such a read_inode()
+ * function, we have to call them after iget() returns an incomplete
+ * inode to us.         This is pretty much the same problem faced in the NFS
+ * code, and pretty much the same solution. The SMB filesystem deals
+ * with this in a different way: by using the address of the
+ * kmalloc()'d space which holds the data as the inode number.
+ *
+ * XXX: Both this function and NFS's corresponding nfs_fhget() would
+ * benefit from a way to pass an additional (void *) through iget() to
+ * the VFS read_inode() function.
+ */
+struct inode *__hfs_iget(struct hfs_cat_entry *entry, ino_t type, int crsmnt)
+{
+       struct inode **sys_entry;
+       struct super_block *sb;
+       struct inode *inode;
+
+       if (!entry) {
+               return NULL;
+       }
+
+       /* If there are several processes all calling __iget() for
+          the same inode then they will all get the same one back.
+          The first one to return from __iget() will notice that the
+          i_mode field of the inode is blank and KNOW that it is
+          the first to return.  Therefore, it will set the appropriate
+          'sys_entry' field in the entry and initialize the inode.
+          All the initialization must be done without sleeping,
+          or else other processes could end up using a partially
+          initialized inode.                           */
+
+       sb = entry->mdb->sys_mdb;
+       sys_entry = &entry->sys_entry[HFS_ITYPE_TO_INT(type)];
+
+       if ((inode = *sys_entry)) {
+               /* There is an existing inode for this file/dir.  Use it. */
+               ++inode->i_count;
+               hfs_cat_put(entry);
+       } else if (!(inode = __iget(sb, ntohl(entry->cnid) | type, crsmnt)) ||
+                  (inode->i_dev != sb->s_dev)) {
+               /* Something went wrong or the inode is on another fs. */
+               hfs_cat_put(entry);
+       } else if (inode->i_mode) {
+               /* The inode has been initialized by another process.
+                  Note that if hfs_put_inode() is sleeping in hfs_cat_put()
+                  then we still need to attach it to the entry. */
+               if (*sys_entry) {
+                       hfs_cat_put(entry);
+               } else {
+                       *sys_entry = inode;
+               }
+       } else {
+               /* Initialize the inode */
+               struct hfs_sb_info *hsb = HFS_SB(sb);
+
+               inode->i_rdev = 0;
+               inode->i_ctime = inode->i_atime = inode->i_mtime =
+                                       hfs_m_to_utime(entry->modify_date);
+               inode->i_blksize = HFS_SECTOR_SIZE;
+               inode->i_uid = hsb->s_uid;
+               inode->i_gid = hsb->s_gid;
+
+               memset(HFS_I(inode), 0, sizeof(struct hfs_inode_info));
+               HFS_I(inode)->magic = HFS_INO_MAGIC;
+               HFS_I(inode)->entry = entry;
+
+               hsb->s_ifill(inode, type);
+               if (!hsb->s_afpd && (entry->type == HFS_CDR_FIL) &&
+                   (entry->u.file.flags & HFS_FIL_LOCK)) {
+                       inode->i_mode &= ~S_IWUGO;
+               }
+               inode->i_mode &= ~hsb->s_umask;
+
+               if (!inode->i_mode) {
+                       clear_inode(inode);
+                       inode = NULL;
+               }
+
+               *sys_entry = inode;
+       }
+       return inode;
+}
+
+/*================ Scheme-specific functions ================*/
+
+/*
+ * hfs_cap_ifill()
+ *
+ * This function serves the same purpose as a read_inode() function does
+ * in other filesystems.  It is called by __hfs_iget() to fill in
+ * the missing fields of an uninitialized inode under the CAP scheme.
+ */
+void hfs_cap_ifill(struct inode * inode, ino_t type)
+{
+       struct hfs_cat_entry *entry = HFS_I(inode)->entry;
+
+       if (type == HFS_CAP_FNDR) {
+               inode->i_size = sizeof(struct hfs_cap_info);
+               inode->i_blocks = 0;
+               inode->i_nlink = 1;
+               inode->i_mode = S_IRUGO | S_IWUGO | S_IFREG;
+               inode->i_op = &hfs_cap_info_inode_operations;
+       } else if (entry->type == HFS_CDR_FIL) {
+               init_file_inode(inode, (type == HFS_CAP_DATA) ?
+                                               HFS_FK_DATA : HFS_FK_RSRC);
+               inode->i_op = &hfs_file_inode_operations;
+       } else { /* Directory */
+               struct hfs_dir *hdir = &entry->u.dir;
+
+               inode->i_blocks = 0;
+               inode->i_size = hdir->files + hdir->dirs + 5;
+               HFS_I(inode)->dir_size = 1;
+               if (type == HFS_CAP_NDIR) {
+                       inode->i_mode = S_IRWXUGO | S_IFDIR;
+                       inode->i_nlink = hdir->dirs + 4;
+                       inode->i_op = &hfs_cap_ndir_inode_operations;
+                       HFS_I(inode)->file_type = HFS_CAP_NORM;
+               } else if (type == HFS_CAP_FDIR) {
+                       inode->i_mode = S_IRUGO | S_IXUGO | S_IFDIR;
+                       inode->i_nlink = 2;
+                       inode->i_op = &hfs_cap_fdir_inode_operations;
+                       HFS_I(inode)->file_type = HFS_CAP_FNDR;
+               } else if (type == HFS_CAP_RDIR) {
+                       inode->i_mode = S_IRUGO | S_IXUGO | S_IFDIR;
+                       inode->i_nlink = 2;
+                       inode->i_op = &hfs_cap_rdir_inode_operations;
+                       HFS_I(inode)->file_type = HFS_CAP_RSRC;
+               }
+       }
+}
+
+/*
+ * hfs_dbl_ifill()
+ *
+ * This function serves the same purpose as a read_inode() function does
+ * in other filesystems.  It is called by __hfs_iget() to fill in
+ * the missing fields of an uninitialized inode under the AppleDouble
+ * scheme.
+ */
+void hfs_dbl_ifill(struct inode * inode, ino_t type)
+{
+       struct hfs_cat_entry *entry = HFS_I(inode)->entry;
+
+       if (type == HFS_DBL_HDR) {
+               if (entry->type == HFS_CDR_FIL) {
+                       init_file_inode(inode, HFS_FK_RSRC);
+                       inode->i_size += HFS_DBL_HDR_LEN;
+                       HFS_I(inode)->default_layout = &hfs_dbl_fil_hdr_layout;
+               } else {
+                       inode->i_size = HFS_DBL_HDR_LEN;
+                       inode->i_mode = S_IRUGO | S_IWUGO | S_IFREG;
+                       inode->i_nlink = 1;
+                       HFS_I(inode)->default_layout = &hfs_dbl_dir_hdr_layout;
+               }
+               inode->i_op = &hfs_hdr_inode_operations;
+       } else if (entry->type == HFS_CDR_FIL) {
+               init_file_inode(inode, HFS_FK_DATA);
+               inode->i_op = &hfs_file_inode_operations;
+       } else { /* Directory */
+               struct hfs_dir *hdir = &entry->u.dir;
+
+               inode->i_blocks = 0;
+               inode->i_nlink = hdir->dirs + 2;
+               inode->i_size = 3 + 2 * (hdir->dirs + hdir->files);
+               inode->i_mode = S_IRWXUGO | S_IFDIR;
+               inode->i_op = &hfs_dbl_dir_inode_operations;
+               HFS_I(inode)->file_type = HFS_DBL_NORM;
+               HFS_I(inode)->dir_size = 2;
+       }
+}
+
+/*
+ * hfs_nat_ifill()
+ *
+ * This function serves the same purpose as a read_inode() function does
+ * in other filesystems.  It is called by __hfs_iget() to fill in
+ * the missing fields of an uninitialized inode under the Netatalk
+ * scheme.
+ */
+void hfs_nat_ifill(struct inode * inode, ino_t type)
+{
+       struct hfs_cat_entry *entry = HFS_I(inode)->entry;
+
+       if (type == HFS_NAT_HDR) {
+               if (entry->type == HFS_CDR_FIL) {
+                       init_file_inode(inode, HFS_FK_RSRC);
+                       inode->i_size += HFS_NAT_HDR_LEN;
+               } else {
+                       inode->i_size = HFS_NAT_HDR_LEN;
+                       inode->i_mode = S_IRUGO | S_IWUGO | S_IFREG;
+                       inode->i_nlink = 1;
+               }
+               inode->i_op = &hfs_hdr_inode_operations;
+               HFS_I(inode)->default_layout = &hfs_nat_hdr_layout;
+       } else if (entry->type == HFS_CDR_FIL) {
+               init_file_inode(inode, HFS_FK_DATA);
+               inode->i_op = &hfs_file_inode_operations;
+       } else { /* Directory */
+               struct hfs_dir *hdir = &entry->u.dir;
+
+               inode->i_blocks = 0;
+               inode->i_size = hdir->files + hdir->dirs + 3;
+               inode->i_mode = S_IRWXUGO | S_IFDIR;
+               HFS_I(inode)->dir_size = 1;
+               if (type == HFS_NAT_NDIR) {
+                       inode->i_nlink = hdir->dirs + 3;
+                       inode->i_op = &hfs_nat_ndir_inode_operations;
+                       HFS_I(inode)->file_type = HFS_NAT_NORM;
+               } else if (type == HFS_NAT_HDIR) {
+                       inode->i_nlink = 2;
+                       inode->i_op = &hfs_nat_hdir_inode_operations;
+                       HFS_I(inode)->file_type = HFS_NAT_HDR;
+               }
+       }
+}
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/fs/hfs/mdb.c linux-2.0.29/fs/hfs/mdb.c
--- linux.vanilla/fs/hfs/mdb.c  Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/fs/hfs/mdb.c   Thu Apr 10 12:32:17 1997
@@ -0,0 +1,300 @@
+/*
+ * linux/fs/hfs/mdb.c
+ *
+ * Copyright (C) 1995-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains functions for reading/writing the MDB.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ *
+ * The code in this file initializes some structures which contain
+ * pointers by calling memset(&foo, 0, sizeof(foo)).
+ * This produces the desired behavior only due to the non-ANSI
+ * assumption that the machine representation of NULL is all zeros.
+ */
+
+#include <linux/hfs.h>
+
+/*================ File-local data types ================*/
+
+/*
+ * The HFS Master Directory Block (MDB).
+ *
+ * Also known as the Volume Information Block (VIB), this structure is
+ * the HFS equivalent of a superblock.
+ *
+ * Reference: _Inside Macintosh: Files_ pages 2-59 through 2-62
+ */
+struct raw_mdb {
+       hfs_word_t      drSigWord;      /* Signature word indicating fs type */
+       hfs_lword_t     drCrDate;       /* fs creation date/time */
+       hfs_lword_t     drLsMod;        /* fs modification date/time */
+       hfs_word_t      drAtrb;         /* fs attributes */
+       hfs_word_t      drNmFls;        /* number of files in root directory */
+       hfs_word_t      drVBMSt;        /* location (in 512-byte blocks)
+                                          of the volume bitmap */
+       hfs_word_t      drAllocPtr;     /* location (in allocation blocks)
+                                          to begin next allocation search */
+       hfs_word_t      drNmAlBlks;     /* number of allocation blocks */
+       hfs_lword_t     drAlBlkSiz;     /* bytes in an allocation block */
+       hfs_lword_t     drClpSiz;       /* clumpsize, the number of bytes to
+                                          allocate when extending a file */
+       hfs_word_t      drAlBlSt;       /* location (in 512-byte blocks)
+                                          of the first allocation block */
+       hfs_lword_t     drNxtCNID;      /* CNID to assign to the next
+                                          file or directory created */
+       hfs_word_t      drFreeBks;      /* number of free allocation blocks */
+       hfs_byte_t      drVN[28];       /* the volume label */
+       hfs_lword_t     drVolBkUp;      /* fs backup date/time */
+       hfs_word_t      drVSeqNum;      /* backup sequence number */
+       hfs_lword_t     drWrCnt;        /* fs write count */
+       hfs_lword_t     drXTClpSiz;     /* clumpsize for the extents B-tree */
+       hfs_lword_t     drCTClpSiz;     /* clumpsize for the catalog B-tree */
+       hfs_word_t      drNmRtDirs;     /* number of directories in
+                                          the root directory */
+       hfs_lword_t     drFilCnt;       /* number of files in the fs */
+       hfs_lword_t     drDirCnt;       /* number of directories in the fs */
+       hfs_byte_t      drFndrInfo[32]; /* data used by the Finder */
+       hfs_word_t      drVCSize;       /* MacOS caching parameter */
+       hfs_word_t      drVCBMSize;     /* MacOS caching parameter */
+       hfs_word_t      drCtlCSize;     /* MacOS caching parameter */
+       hfs_lword_t     drXTFlSize;     /* bytes in the extents B-tree */
+       hfs_byte_t      drXTExtRec[12]; /* extents B-tree's first 3 extents */
+       hfs_lword_t     drCTFlSize;     /* bytes in the catalog B-tree */
+       hfs_byte_t      drCTExtRec[12]; /* catalog B-tree's first 3 extents */
+};
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_mdb_get()
+ *
+ * Build the in-core MDB for a filesystem, including
+ * the B-trees and the volume bitmap.
+ */
+struct hfs_mdb *hfs_mdb_get(hfs_sysmdb sys_mdb,
+                           hfs_s32 dev_size, hfs_s32 part_start)
+{
+       struct hfs_mdb *mdb;
+       hfs_buffer buf;
+       struct raw_mdb *raw;
+       unsigned int bs, block;
+       int lcv;
+       hfs_buffer *bmbuf;
+
+       if (!HFS_NEW(mdb)) {
+               hfs_warn("hfs_fs: out of memory\n");
+               return NULL;
+       }
+
+       memset(mdb, 0, sizeof(*mdb));
+       mdb->magic = HFS_MDB_MAGIC;
+       mdb->sys_mdb = sys_mdb;
+       mdb->dev_size = dev_size;
+
+       /* See if this is an HFS filesystem */
+       buf = hfs_buffer_get(sys_mdb, part_start + HFS_MDB_BLK, 1);
+       if (!hfs_buffer_ok(buf)) {
+               hfs_warn("hfs_fs: Unable to read superblock\n");
+               goto bail2;
+       }
+       raw = (struct raw_mdb *)hfs_buffer_data(buf);
+       if (hfs_get_ns(raw->drSigWord) != htons(HFS_SUPER_MAGIC)) {
+               hfs_buffer_put(buf);
+               goto bail2;
+       }
+       mdb->buf = buf;
+
+       /* TRY to get the alternate (backup) MDB */
+       buf = hfs_buffer_get(sys_mdb, part_start + dev_size - 2, 1);
+       if (hfs_buffer_ok(buf)) {
+               struct raw_mdb *tmp =
+                       (struct raw_mdb *)hfs_buffer_data(buf);
+
+               if (hfs_get_ns(tmp->drSigWord) == htons(HFS_SUPER_MAGIC)) {
+                       mdb->alt_buf = buf;
+               } else {
+                       hfs_buffer_put(buf);
+               }
+       }
+#if !defined(HFS_DEVSIZE_CORRECT)
+       /* The Linux kernel records the sizes of block devices in
+          units of 1024 bytes, so dev_size might not include the
+          final block.  Therefore, we also look for the alternate
+          MDB one block later than normal if dev_size is even.
+          XXX: Should get dev_size correct instead
+       */
+       if ((mdb->alt_buf == NULL) && !(dev_size & 1)) {
+               buf = hfs_buffer_get(sys_mdb, part_start + dev_size - 1, 1);
+               if (hfs_buffer_ok(buf)) {
+                       struct raw_mdb *tmp =
+                               (struct raw_mdb *)hfs_buffer_data(buf);
+
+                       if (hfs_get_ns(tmp->drSigWord) ==
+                                               htons(HFS_SUPER_MAGIC)) {
+                               mdb->alt_buf = buf;
+                               mdb->dev_size += 1;
+                       } else {
+                               hfs_buffer_put(buf);
+                       }
+               }
+       }
+#endif
+       if (mdb->alt_buf == NULL) {
+               hfs_warn("hfs_fs: unable to locate alternate MDB\n");
+               hfs_warn("hfs_fs: continuing without an alternate MDB\n");
+       }
+
+       bs = hfs_get_hl(raw->drAlBlkSiz);
+       if (!bs || bs > HFS_USHRT_MAX || (bs & (HFS_SECTOR_SIZE-1))) {
+               hfs_warn("hfs_fs: bad allocation block size %d != 512\n", bs);
+               goto bail1;
+       }
+       mdb->alloc_blksz = bs >> HFS_SECTOR_SIZE_BITS;
+
+       /* These parameters are read from the MDB, and never written */
+       mdb->create_date = hfs_get_hl(raw->drCrDate);
+       mdb->fs_ablocks  = hfs_get_hs(raw->drNmAlBlks);
+       mdb->fs_start    = hfs_get_hs(raw->drAlBlSt) + part_start;
+       mdb->backup_date = hfs_get_hl(raw->drVolBkUp);
+       mdb->clumpablks  = (hfs_get_hl(raw->drClpSiz) / mdb->alloc_blksz)
+                                                >> HFS_SECTOR_SIZE_BITS;
+       memcpy(mdb->vname, raw->drVN, 28);
+
+       /* These parameters are read from and written to the MDB */
+       mdb->modify_date  = hfs_get_nl(raw->drLsMod);
+       mdb->attrib       = hfs_get_ns(raw->drAtrb);
+       mdb->free_ablocks = hfs_get_hs(raw->drFreeBks);
+       mdb->next_id      = hfs_get_hl(raw->drNxtCNID);
+       mdb->write_count  = hfs_get_hl(raw->drWrCnt);
+       mdb->root_files   = hfs_get_hs(raw->drNmFls);
+       mdb->root_dirs    = hfs_get_hs(raw->drNmRtDirs);
+       mdb->file_count   = hfs_get_hl(raw->drFilCnt);
+       mdb->dir_count    = hfs_get_hl(raw->drDirCnt);
+
+       /* read in the bitmap */
+       block = hfs_get_hs(raw->drVBMSt) + part_start;
+       bmbuf = mdb->bitmap;
+       lcv = (mdb->fs_ablocks + 4095) / 4096;
+       for ( ; lcv; --lcv, ++bmbuf, ++block) {
+               if (!hfs_buffer_ok(*bmbuf =
+                                  hfs_buffer_get(sys_mdb, block, 1))) {
+                       hfs_warn("hfs_fs: unable to read volume bitmap\n");
+                       goto bail1;
+               }
+       }
+
+       if (!(mdb->ext_tree = hfs_btree_init(mdb, htonl(HFS_EXT_CNID),
+                                            raw->drXTExtRec,
+                                            hfs_get_hl(raw->drXTFlSize),
+                                            hfs_get_hl(raw->drXTClpSiz))) ||
+           !(mdb->cat_tree = hfs_btree_init(mdb, htonl(HFS_CAT_CNID),
+                                            raw->drCTExtRec,
+                                            hfs_get_hl(raw->drCTFlSize),
+                                            hfs_get_hl(raw->drCTClpSiz)))) {
+               hfs_warn("hfs_fs: unable to initialize data structures\n");
+               goto bail1;
+       }
+
+       return mdb;
+
+bail1:
+       hfs_mdb_put(mdb);
+bail2:
+       return NULL;
+}
+
+/*
+ * hfs_mdb_commit()
+ *
+ * Description:
+ *   This updates the MDB on disk (look also at hfs_write_super()).
+ *   It does not check, if the superblock has been modified, or
+ *   if the filesystem has been mounted read-only. It is mainly
+ *   called by hfs_write_super() and hfs_btree_extend().
+ * Input Variable(s):
+ *   struct hfs_mdb *mdb: Pointer to the hfs MDB
+ *   int backup;
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   void
+ * Preconditions:
+ *   'mdb' points to a "valid" (struct hfs_mdb).
+ * Postconditions:
+ *   The HFS MDB and on disk will be updated, by copying the possibly
+ *   modified fields from the in memory MDB (in native byte order) to
+ *   the disk block buffer.
+ *   If 'backup' is non-zero then the alternate MDB is also written
+ *   and the function doesn't return until it is actually on disk.
+ */
+void hfs_mdb_commit(struct hfs_mdb *mdb, int backup)
+{
+       struct raw_mdb *raw = (struct raw_mdb *)hfs_buffer_data(mdb->buf);
+
+       /* Commit catalog entries to buffers */
+       hfs_cat_commit(mdb);
+
+       /* Commit B-tree data to buffers */
+       hfs_btree_commit(mdb->cat_tree, raw->drCTExtRec, raw->drCTFlSize);
+       hfs_btree_commit(mdb->ext_tree, raw->drXTExtRec, raw->drXTFlSize);
+
+       /* Update write_count and modify_date */
+       ++mdb->write_count;
+       mdb->modify_date = hfs_time();
+
+       /* These parameters may have been modified, so write them back */
+       hfs_put_nl(mdb->modify_date,   raw->drLsMod);
+       hfs_put_ns(mdb->attrib,        raw->drAtrb);
+       hfs_put_hs(mdb->free_ablocks,  raw->drFreeBks);
+       hfs_put_hl(mdb->next_id,       raw->drNxtCNID);
+       hfs_put_hl(mdb->write_count,   raw->drWrCnt);
+       hfs_put_hs(mdb->root_files,    raw->drNmFls);
+       hfs_put_hs(mdb->root_dirs,     raw->drNmRtDirs);
+       hfs_put_hl(mdb->file_count,    raw->drFilCnt);
+       hfs_put_hl(mdb->dir_count,     raw->drDirCnt);
+
+       /* write MDB to disk */
+       hfs_buffer_dirty(mdb->buf);
+
+               /* write the backup MDB, not returning until it is written */
+        if (backup && hfs_buffer_ok(mdb->alt_buf)) {
+                memcpy(hfs_buffer_data(mdb->alt_buf),
+                      hfs_buffer_data(mdb->buf), HFS_SECTOR_SIZE);
+                hfs_buffer_dirty(mdb->alt_buf);
+                hfs_buffer_sync(mdb->alt_buf);
+        }
+}
+
+/*
+ * hfs_mdb_put()
+ *
+ * Release the resources associated with the in-core MDB.
+ */
+void hfs_mdb_put(struct hfs_mdb *mdb) {
+       int lcv;
+
+       /* invalidate cached catalog entries */
+       hfs_cat_invalidate(mdb);
+
+       /* free the B-trees */
+       hfs_btree_free(mdb->ext_tree);
+       hfs_btree_free(mdb->cat_tree);
+
+       /* free the volume bitmap */
+       for (lcv = 0; lcv < HFS_BM_MAXBLOCKS; ++lcv) {
+               hfs_buffer_put(mdb->bitmap[lcv]);
+       }
+
+       /* free the buffers holding the primary and alternate MDBs */
+       hfs_buffer_put(mdb->buf);
+       hfs_buffer_put(mdb->alt_buf);
+
+       /* free the MDB */
+       HFS_DELETE(mdb);
+}
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/fs/hfs/part_tbl.c linux-2.0.29/fs/hfs/part_tbl.c
--- linux.vanilla/fs/hfs/part_tbl.c     Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/fs/hfs/part_tbl.c      Thu Apr 10 12:32:30 1997
@@ -0,0 +1,244 @@
+/*
+ * linux/fs/hfs/part_tbl.c
+ *
+ * Copyright (C) 1996-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * Original code to handle the new style Mac partition table based on
+ * a patch contributed by Holger Schemel ([email protected]).
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ *
+ * The code in this file initializes some structures which contain
+ * pointers by calling memset(&foo, 0, sizeof(foo)).
+ * This produces the desired behavior only due to the non-ANSI
+ * assumption that the machine representation of NULL is all zeros.
+ */
+
+#include <linux/hfs.h>
+
+/*================ File-local data types ================*/
+
+/*
+ * The Macintosh Driver Descriptor Block
+ *
+ * On partitioned Macintosh media this is block 0.
+ * We really only need the "magic number" to check for partitioned media.
+ */
+struct hfs_drvr_desc {
+       hfs_word_t      ddSig;          /* The signature word */
+       /* a bunch more stuff we don't need */
+};
+
+/*
+ * The new style Mac partition map
+ *
+ * For each partition on the media there is a physical block (512-byte
+ * block) containing one of these structures.  These blocks are
+ * contiguous starting at block 1.
+ */
+struct new_pmap {
+       hfs_word_t      pmSig;          /* Signature bytes to verify
+                                          that this is a partition
+                                          map block */
+       hfs_word_t      reSigPad;       /* padding */
+       hfs_lword_t     pmMapBlkCnt;    /* (At least in block 1) this
+                                          is the number of partition
+                                          map blocks */
+       hfs_lword_t     pmPyPartStart;  /* The physical block number
+                                          of the first block in this
+                                          partition */
+       hfs_lword_t     pmPartBlkCnt;   /* The number of physical
+                                          blocks in this partition */
+       hfs_byte_t      pmPartName[32]; /* (null terminated?) string
+                                          giving the name of this
+                                          partition */
+       hfs_byte_t      pmPartType[32]; /* (null terminated?) string
+                                          giving the type of this
+                                          partition */
+       /* a bunch more stuff we don't need */
+};
+
+/*
+ * The old style Mac partition map
+ *
+ * The partition map consists for a 2-byte signature followed by an
+ * array of these structures.  The map is terminated with an all-zero
+ * one of these.
+ */
+struct old_pmap {
+       hfs_word_t              pdSig;  /* Signature bytes */
+       struct  old_pmap_entry {
+               hfs_lword_t     pdStart;
+               hfs_lword_t     pdSize;
+               hfs_lword_t     pdFSID;
+       }       pdEntry[42];
+};
+
+/*================ File-local functions ================*/
+
+/*
+ * parse_new_part_table()
+ *
+ * Parse a new style partition map looking for the
+ * start and length of the 'part'th HFS partition.
+ */
+static int parse_new_part_table(hfs_sysmdb sys_mdb, hfs_buffer buf,
+                               int part, hfs_s32 *size, hfs_s32 *start)
+{
+       struct new_pmap *pm = (struct new_pmap *)hfs_buffer_data(buf);
+       hfs_u32 pmap_entries = hfs_get_hl(pm->pmMapBlkCnt);
+       int hfs_part = 0;
+       int entry;
+
+       for (entry = 0; (entry < pmap_entries) && !(*start); ++entry) {
+               if (entry) {
+                       /* read the next partition map entry */
+                       buf = hfs_buffer_get(sys_mdb, HFS_PMAP_BLK + entry, 1);
+                       if (!hfs_buffer_ok(buf)) {
+                               hfs_warn("hfs_fs: unable to "
+                                        "read partition map.\n");
+                               goto bail;
+                       }
+                       pm = (struct new_pmap *)hfs_buffer_data(buf);
+                       if (hfs_get_ns(pm->pmSig) !=
+                                               htons(HFS_NEW_PMAP_MAGIC)) {
+                               hfs_warn("hfs_fs: invalid "
+                                        "entry in partition map\n");
+                               hfs_buffer_put(buf);
+                               goto bail;
+                       }
+               }
+
+               /* look for an HFS partition */
+               if (!memcmp(pm->pmPartType,"Apple_HFS",9) &&
+                   ((hfs_part++) == part)) {
+                       /* Found it! */
+                       *start = hfs_get_hl(pm->pmPyPartStart);
+                       *size = hfs_get_hl(pm->pmPartBlkCnt);
+               }
+
+               hfs_buffer_put(buf);
+       }
+
+       return 0;
+
+bail:
+       return 1;
+}
+
+/*
+ * parse_old_part_table()
+ *
+ * Parse a old style partition map looking for the
+ * start and length of the 'part'th HFS partition.
+ */
+static int parse_old_part_table(hfs_sysmdb sys_mdb, hfs_buffer buf,
+                               int part, hfs_s32 *size, hfs_s32 *start)
+{
+       struct old_pmap *pm = (struct old_pmap *)hfs_buffer_data(buf);
+       struct old_pmap_entry *p = &pm->pdEntry[0];
+       int hfs_part = 0;
+
+       while ((p->pdStart || p->pdSize || p->pdFSID) && !(*start)) {
+               /* look for an HFS partition */
+               if ((hfs_get_nl(p->pdFSID) == htonl(0x54465331)/*"TFS1"*/) &&
+                   ((hfs_part++) == part)) {
+                       /* Found it! */
+                       *start = hfs_get_hl(p->pdStart);
+                       *size = hfs_get_hl(p->pdSize);
+               }
+               ++p;
+       }
+       hfs_buffer_put(buf);
+
+       return 0;
+}
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_part_find()
+ *
+ * Parse the partition map looking for the
+ * start and length of the 'part'th HFS partition.
+ */
+int hfs_part_find(hfs_sysmdb sys_mdb, int part, int silent,
+                 hfs_s32 *size, hfs_s32 *start)
+{
+       hfs_buffer buf;
+       hfs_u16 sig;
+       int dd_found = 0;
+       int retval = 1;
+
+       /* Read block 0 to see if this media is partitioned */
+       buf = hfs_buffer_get(sys_mdb, HFS_DD_BLK, 1);
+       if (!hfs_buffer_ok(buf)) {
+               hfs_warn("hfs_fs: Unable to read block 0.\n");
+               goto done;
+       }
+       sig = hfs_get_ns(((struct hfs_drvr_desc *)hfs_buffer_data(buf))->ddSig);
+       hfs_buffer_put(buf);
+
+        if (sig == htons(HFS_DRVR_DESC_MAGIC)) {
+               /* We are definitely on partitioned media. */
+               dd_found = 1;
+       }
+
+       buf = hfs_buffer_get(sys_mdb, HFS_PMAP_BLK, 1);
+       if (!hfs_buffer_ok(buf)) {
+               hfs_warn("hfs_fs: Unable to read block 1.\n");
+               goto done;
+       }
+
+       *size = *start = 0;
+
+       switch (hfs_get_ns(hfs_buffer_data(buf))) {
+       case __constant_htons(HFS_OLD_PMAP_MAGIC):
+               retval = parse_old_part_table(sys_mdb, buf, part, size, start);
+               break;
+
+       case __constant_htons(HFS_NEW_PMAP_MAGIC):
+               retval = parse_new_part_table(sys_mdb, buf, part, size, start);
+               break;
+
+       default:
+               if (dd_found) {
+                       /* The media claimed to have a partition map */
+                       if (!silent) {
+                               hfs_warn("hfs_fs: This disk has an "
+                                        "unrecognized partition map type.\n");
+                       }
+               } else {
+                       /* Conclude that the media is not partitioned */
+                       retval = 0;
+               }
+               goto done;
+       }
+
+       if (!retval) {
+               if (*start == 0) {
+                       if (part) {
+                               hfs_warn("hfs_fs: unable to locate "
+                                        "HFS partition number %d.\n", part);
+                       } else {
+                               hfs_warn("hfs_fs: unable to locate any "
+                                        "HFS partitions.\n");
+                       }
+                       retval = 1;
+               } else if (*size < 0) {
+                       hfs_warn("hfs_fs: Partition size > 1 Terabyte.\n");
+                       retval = 1;
+               } else if (*start < 0) {
+                       hfs_warn("hfs_fs: Partition begins beyond 1 "
+                                "Terabyte.\n");
+                       retval = 1;
+               }
+       }
+done:
+       return retval;
+}
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/fs/hfs/string.c linux-2.0.29/fs/hfs/string.c
--- linux.vanilla/fs/hfs/string.c       Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/fs/hfs/string.c        Thu Apr 10 12:32:44 1997
@@ -0,0 +1,152 @@
+/*
+ * linux/fs/hfs/string.c
+ *
+ * Copyright (C) 1995-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the string comparison function for the
+ * Macintosh character set.
+ *
+ * The code in this file is derived from code which is copyright
+ * 1986, 1989, 1990 by Abacus Research and Development, Inc. (ARDI)
+ * It is used here by the permission of ARDI's president Cliff Matthews.
+ *
+ * If you discover bugs in this code please notify both the author of the
+ * Linux HFS file system: [email protected] (Paul H. Hargrove)
+ * and the author of ARDI's HFS code: [email protected] (Clifford T. Matthews)
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ */
+
+#include <linux/hfs.h>
+
+/*================ File-local variables ================*/
+
+/*
+ * unsigned char caseorder[]
+ *
+ * Defines the lexical ordering of characters on the Macintosh
+ *
+ * Composition of the 'casefold' and 'order' tables from ARDI's code
+ * with the entry for 0x20 changed to match that for 0xCA to remove
+ * special case for those two characters.
+ */
+static unsigned char caseorder[256] = {
+0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,
+0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,
+0x20,0x22,0x23,0x28,0x29,0x2A,0x2B,0x2C,0x2F,0x30,0x31,0x32,0x33,0x34,0x35,0x36,
+0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F,0x40,0x41,0x42,0x43,0x44,0x45,0x46,
+0x47,0x48,0x57,0x59,0x5D,0x5F,0x66,0x68,0x6A,0x6C,0x72,0x74,0x76,0x78,0x7A,0x7E,
+0x8C,0x8E,0x90,0x92,0x95,0x97,0x9E,0xA0,0xA2,0xA4,0xA7,0xA9,0xAA,0xAB,0xAC,0xAD,
+0x4E,0x48,0x57,0x59,0x5D,0x5F,0x66,0x68,0x6A,0x6C,0x72,0x74,0x76,0x78,0x7A,0x7E,
+0x8C,0x8E,0x90,0x92,0x95,0x97,0x9E,0xA0,0xA2,0xA4,0xA7,0xAF,0xB0,0xB1,0xB2,0xB3,
+0x4A,0x4C,0x5A,0x60,0x7B,0x7F,0x98,0x4F,0x49,0x51,0x4A,0x4B,0x4C,0x5A,0x60,0x63,
+0x64,0x65,0x6E,0x6F,0x70,0x71,0x7B,0x84,0x85,0x86,0x7F,0x80,0x9A,0x9B,0x9C,0x98,
+0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0x94,0xBB,0xBC,0xBD,0xBE,0xBF,0xC0,0x4D,0x81,
+0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0x55,0x8A,0xCC,0x4D,0x81,
+0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0x26,0x27,0xD4,0x20,0x49,0x4B,0x80,0x82,0x82,
+0xD5,0xD6,0x24,0x25,0x2D,0x2E,0xD7,0xD8,0xA6,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF,
+0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,
+0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF
+};
+
+/*
+ * unsigned char casefold[]
+ *
+ * Defines the mapping to lowercase characters on the Macintosh
+ *
+ * "Inverse" of the 'casefold' from ARDI's code.
+ */
+static unsigned char casefold[256] = {
+0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,
+0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,
+0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,
+0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F,
+0x40,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F,
+0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x5B,0x5C,0x5D,0x5E,0x5F,
+0x41,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F,
+0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F,
+0x8A,0x8C,0x8D,0x8E,0x96,0x9A,0x9F,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,
+0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F,
+0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xBE,0xBF,
+0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF,
+0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0x88,0x8B,0x9B,0xCF,0xCF,
+0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF,
+0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,
+0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF
+};
+
+/*================ Global functions ================*/
+
+/*
+ * Hash a string to an integer in a case-independent way
+ */
+unsigned int hfs_strhash(const struct hfs_name *cname)
+{
+       /* Currently just sum of the 'order' of first and last characters */
+       return ((unsigned int)caseorder[cname->Name[0]] +
+               (unsigned int)caseorder[cname->Name[cname->Len - 1]]);
+}
+
+/*
+ * Compare two strings in the HFS filename character ordering
+ * Returns positive, negative, or zero, not just 0 or (+/-)1
+ *
+ * Equivalent to ARDI's call:
+ *     ROMlib_RelString(s1+1, s2+1, true, false, (s1[0]<<16) | s2[0])
+ */
+int hfs_strcmp(const struct hfs_name *s1, const struct hfs_name *s2)
+{
+       int len, tmp;
+       const unsigned char *p1, *p2;
+
+       if (!s1 || !s2) {
+               return 0;
+       }
+
+       len = (s1->Len > s2->Len) ? s2->Len : s1->Len;
+       p1 = s1->Name;
+       p2 = s2->Name;
+
+       while (len--) {
+               if ((tmp = (int)caseorder[*(p1++)]-(int)caseorder[*(p2++)])) {
+                       return tmp;
+               }
+       }
+       return s1->Len - s2->Len;
+}
+
+/*
+ * Test for equality of two strings in the HFS filename character ordering.
+ */
+int hfs_streq(const struct hfs_name *s1, const struct hfs_name *s2)
+{
+       int len;
+       const unsigned char *p1, *p2;
+
+       if (!s1 || !s2 || (s1->Len != s2->Len)) {
+               return 0;
+       }
+
+       len = s1->Len;
+       p1 = s1->Name;
+       p2 = s2->Name;
+
+       while (len--) {
+               if (caseorder[*(p1++)] != caseorder[*(p2++)]) {
+                       return 0;
+               }
+       }
+       return 1;
+}
+
+/*
+ * Convert a string to the Macintosh version of lower case.
+ */
+void hfs_tolower(unsigned char *p, int len)
+{
+       while (len--) {
+               *p = casefold[*p];
+               ++p;
+       }
+}
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/fs/hfs/super.c linux-2.0.29/fs/hfs/super.c
--- linux.vanilla/fs/hfs/super.c        Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/fs/hfs/super.c Thu Apr 10 12:32:55 1997
@@ -0,0 +1,493 @@
+/*
+ * linux/fs/hfs/super.c
+ *
+ * Copyright (C) 1995-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains hfs_read_super() some of the the super_ops and
+ * init_module() and cleanup_module(). The remaining super_ops are in
+ * inode.c since they deal with inodes.
+ *
+ * Based on the minix file system code, (C) 1991, 1992 by Linus Torvalds
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ *
+ * The code in this file initializes some structures which contain
+ * pointers by calling memset(&foo, 0, sizeof(foo)).
+ * This produces the desired behavior only due to the non-ANSI
+ * assumption that the machine representation of NULL is all zeros.
+ */
+
+#include <linux/hfs.h>
+#include <linux/hfs_fs_sb.h>
+#include <linux/hfs_fs_i.h>
+#include <linux/hfs_fs.h>
+
+#include <linux/blkdev.h>
+#include <linux/module.h>
+
+/*================ Forward declarations ================*/
+
+static void hfs_put_super(struct super_block *);
+static void hfs_statfs(struct super_block *, struct statfs *, int);
+static void hfs_write_super(struct super_block *);
+
+/*================ Global variables ================*/
+
+static struct super_operations hfs_super_operations = {
+       NULL,                   /* read_inode */
+       hfs_notify_change,      /* notify_change - in inode.c */
+       NULL,                   /* write_inode */
+       hfs_put_inode,          /* put_inode     - in inode.c */
+       hfs_put_super,          /* put_super */
+       hfs_write_super,        /* write_super */
+       hfs_statfs,             /* statfs */
+       NULL                    /* remount_fs */
+};
+
+/*================ File-local variables ================*/
+
+static struct file_system_type hfs_fs = {hfs_read_super, "hfs", 1, NULL};
+
+/*================ File-local functions ================*/
+
+/*
+ * hfs_write_super()
+ *
+ * Description:
+ *   This function is called by the VFS only. When the filesystem
+ *   is mounted r/w it updates the MDB on disk.
+ * Input Variable(s):
+ *   struct super_block *sb: Pointer to the hfs superblock
+ * Output Variable(s):
+ *   NONE
+ * Returns:
+ *   void
+ * Preconditions:
+ *   'sb' points to a "valid" (struct super_block).
+ * Postconditions:
+ *   The MDB is marked 'unsuccessfully unmounted' by clearing bit 8 of drAtrb
+ *   (hfs_put_super() must set this flag!). Some MDB fields are updated
+ *   and the MDB buffer is written to disk by calling hfs_mdb_commit().
+ */
+static void hfs_write_super(struct super_block *sb)
+{
+       struct hfs_mdb *mdb = HFS_SB(sb)->s_mdb;
+
+       /* is this a valid hfs superblock? */
+       if (!sb || sb->s_magic != HFS_SUPER_MAGIC) {
+               return;
+       }
+
+       if (!(sb->s_flags & MS_RDONLY)) {
+               if (mdb->attrib & htons(HFS_SB_ATTRIB_CLEAN)) {
+                       mdb->attrib &= htons(~HFS_SB_ATTRIB_CLEAN);
+               }
+
+               /* sync everything to the buffers */
+               hfs_mdb_commit(mdb, 0);
+       }
+       sb->s_dirt = 0;
+}
+
+/*
+ * hfs_put_super()
+ *
+ * This is the put_super() entry in the super_operations structure for
+ * HFS filesystems.  The purpose is to release the resources
+ * associated with the superblock sb.
+ */
+static void hfs_put_super(struct super_block *sb)
+{
+       struct hfs_mdb *mdb = HFS_SB(sb)->s_mdb;
+
+       lock_super(sb);
+
+       /* If the filesystem is mounted r/w, set the
+        * 'successfully unmounted' flag of the drAtrb
+        * field of the MDB and write the MDB to disk
+        */
+       if (!(sb->s_flags & MS_RDONLY)) {
+               mdb->attrib |= htons(HFS_SB_ATTRIB_CLEAN);
+               hfs_mdb_commit(mdb, 0);
+               sb->s_dirt = 0;
+       }
+
+       /* release the MDB's resources */
+       hfs_mdb_put(mdb);
+
+       /* restore default blocksize for the device */
+       set_blocksize(sb->s_dev, BLOCK_SIZE);
+
+       /* invalidate the superblock */
+       sb->s_dev = 0;
+
+       MOD_DEC_USE_COUNT;
+
+       unlock_super(sb);
+       return;
+}
+
+/*
+ * hfs_statfs()
+ *
+ * This is the statfs() entry in the super_operations structure for
+ * HFS filesystems.  The purpose is to return various data about the
+ * filesystem.
+ */
+static void hfs_statfs(struct super_block *sb, struct statfs *buf, int len)
+{
+       struct hfs_mdb *mdb = HFS_SB(sb)->s_mdb;
+       struct statfs tmp;
+
+       tmp.f_type = HFS_SUPER_MAGIC;
+       tmp.f_bsize = HFS_SECTOR_SIZE;
+       tmp.f_blocks = mdb->alloc_blksz * mdb->fs_ablocks;
+       tmp.f_bfree = mdb->alloc_blksz * mdb->free_ablocks;
+       tmp.f_bavail = tmp.f_bfree;
+       tmp.f_files = -1; /* According to the statfs manual page, -1 is the  */
+       tmp.f_ffree = -1; /* correct value when the meaning is undefined.    */
+       tmp.f_namelen = HFS_NAMELEN;
+
+       copy_to_user(buf, &tmp, len);
+}
+
+/*
+ * parse_options()
+ *
+ * adapted from linux/fs/msdos/inode.c written 1992,93 by Werner Almesberger
+ * This function is called by hfs_read_super() to parse the mount options.
+ */
+static int parse_options(char *options, struct hfs_sb_info *hsb, int *part)
+{
+       char *this_char, *value;
+       char names, fork;
+
+       /* initialize the sb with defaults */
+       memset(hsb, 0, sizeof(*hsb));
+       hsb->magic = HFS_SB_MAGIC;
+       hsb->s_uid   = current->uid;
+       hsb->s_gid   = current->gid;
+       hsb->s_umask = current->fs->umask;
+       hsb->s_type    = 0x3f3f3f3f;    /* == '????' */
+       hsb->s_creator = 0x3f3f3f3f;    /* == '????' */
+       hsb->s_lowercase = 0;
+       hsb->s_quiet     = 0;
+       hsb->s_afpd      = 0;
+       hsb->s_conv = 'b';
+       names = '?';
+       fork = '?';
+       *part = 0;
+
+       if (!options) {
+               goto done;
+       }
+       for (this_char = strtok(options,","); this_char;
+            this_char = strtok(NULL,",")) {
+               if ((value = strchr(this_char,'=')) != NULL) {
+                       *value++ = 0;
+               }
+       /* Numeric-valued options */
+               if (!strcmp(this_char,"uid")) {
+                       if (!value || !*value) {
+                               return 0;
+                       }
+                       hsb->s_uid = simple_strtoul(value,&value,0);
+                       if (*value) {
+                               return 0;
+                       }
+               } else if (!strcmp(this_char,"gid")) {
+                       if (!value || !*value) {
+                               return 0;
+                       }
+                       hsb->s_gid = simple_strtoul(value,&value,0);
+                       if (*value) {
+                               return 0;
+                       }
+               } else if (!strcmp(this_char,"umask")) {
+                       if (!value || !*value) {
+                               return 0;
+                       }
+                       hsb->s_umask = simple_strtoul(value,&value,8);
+                       if (*value) {
+                               return 0;
+                       }
+               } else if (!strcmp(this_char,"part")) {
+                       if (!value || !*value) {
+                               return 0;
+                       }
+                       *part = simple_strtoul(value,&value,0);
+                       if (*value) {
+                               return 0;
+                       }
+       /* String-valued options */
+               } else if (!strcmp(this_char,"type") && value) {
+                       if (strlen(value) != 4) {
+                               return 0;
+                       }
+                       hsb->s_type = hfs_get_nl(value);
+               } else if (!strcmp(this_char,"creator") && value) {
+                       if (strlen(value) != 4) {
+                               return 0;
+                       }
+                       hsb->s_creator = hfs_get_nl(value);
+       /* Boolean-valued options */
+               } else if (!strcmp(this_char,"quiet")) {
+                       if (value) {
+                               return 0;
+                       }
+                       hsb->s_quiet = 1;
+               } else if (!strcmp(this_char,"afpd")) {
+                       if (value) {
+                               return 0;
+                       }
+                       hsb->s_afpd = 1;
+       /* Multiple choice options */
+               } else if (!strcmp(this_char,"names") && value) {
+                       if ((*value && !value[1] && strchr("ntal78c",*value)) ||
+                           !strcmp(value,"netatalk") ||
+                           !strcmp(value,"trivial") ||
+                           !strcmp(value,"alpha") ||
+                           !strcmp(value,"latin") ||
+                           !strcmp(value,"7bit") ||
+                           !strcmp(value,"8bit") ||
+                           !strcmp(value,"cap")) {
+                               names = *value;
+                       } else {
+                               return 0;
+                       }
+               } else if (!strcmp(this_char,"fork") && value) {
+                       if ((*value && !value[1] && strchr("nsdc",*value)) ||
+                           !strcmp(value,"netatalk") ||
+                           !strcmp(value,"single") ||
+                           !strcmp(value,"double") ||
+                           !strcmp(value,"cap")) {
+                               fork = *value;
+                       } else {
+                               return 0;
+                       }
+               } else if (!strcmp(this_char,"case") && value) {
+                       if ((*value && !value[1] && strchr("la",*value)) ||
+                           !strcmp(value,"lower") ||
+                           !strcmp(value,"asis")) {
+                               hsb->s_lowercase = (*value == 'l');
+                       } else {
+                               return 0;
+                       }
+               } else if (!strcmp(this_char,"conv") && value) {
+                       if ((*value && !value[1] && strchr("bta",*value)) ||
+                           !strcmp(value,"binary") ||
+                           !strcmp(value,"text") ||
+                           !strcmp(value,"auto")) {
+                               hsb->s_conv = *value;
+                       } else {
+                               return 0;
+                       }
+               } else {
+                       return 0;
+               }
+       }
+
+done:
+       /* Parse the "fork" and "names" options */
+       if (fork == '?') {
+               fork = hsb->s_afpd ? 'n' : 'c';
+       }
+       switch (fork) {
+       default:
+       case 'c':
+               hsb->s_ifill = hfs_cap_ifill;
+               hsb->s_reserved1 = hfs_cap_reserved1;
+               hsb->s_reserved2 = hfs_cap_reserved2;
+               break;
+
+       case 's':
+               hfs_warn("hfs_fs: AppleSingle not yet implemented.\n");
+               return 0;
+               /* break; */
+
+       case 'd':
+               hsb->s_ifill = hfs_dbl_ifill;
+               hsb->s_reserved1 = hfs_dbl_reserved1;
+               hsb->s_reserved2 = hfs_dbl_reserved2;
+               break;
+
+       case 'n':
+               hsb->s_ifill = hfs_nat_ifill;
+               hsb->s_reserved1 = hfs_nat_reserved1;
+               hsb->s_reserved2 = hfs_nat_reserved2;
+               break;
+       }
+
+       if (names == '?') {
+               names = fork;
+       }
+       switch (names) {
+       default:
+       case 'n':
+               hsb->s_nameout = hfs_colon2mac;
+               hsb->s_namein = hfs_mac2nat;
+               break;
+
+       case 'c':
+               hsb->s_nameout = hfs_colon2mac;
+               hsb->s_namein = hfs_mac2cap;
+               break;
+
+       case 't':
+               hsb->s_nameout = hfs_triv2mac;
+               hsb->s_namein = hfs_mac2triv;
+               break;
+
+       case '7':
+               hsb->s_nameout = hfs_prcnt2mac;
+               hsb->s_namein = hfs_mac2seven;
+               break;
+
+       case '8':
+               hsb->s_nameout = hfs_prcnt2mac;
+               hsb->s_namein = hfs_mac2eight;
+               break;
+
+       case 'l':
+               hsb->s_nameout = hfs_latin2mac;
+               hsb->s_namein = hfs_mac2latin;
+               break;
+
+       case 'a':       /* 's' and 'd' are unadvertised aliases for 'alpha', */
+       case 's':       /* since 'alpha' is the default if fork=s or fork=d. */
+       case 'd':       /* (It is also helpful for poor typists!)           */
+               hsb->s_nameout = hfs_prcnt2mac;
+               hsb->s_namein = hfs_mac2alpha;
+               break;
+       }
+
+       return 1;
+}
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_read_super()
+ *
+ * This is the function that is responsible for mounting an HFS
+ * filesystem. It performs all the tasks necessary to get enough data
+ * from the disk to read the root inode.  This includes parsing the
+ * mount options, dealing with Macintosh partitions, reading the
+ * superblock and the allocation bitmap blocks, calling
+ * hfs_btree_init() to get the necessary data about the extents and
+ * catalog B-trees and, finally, reading the root inode into memory.
+ */
+struct super_block *hfs_read_super(struct super_block *s, void *data,
+                                  int silent)
+{
+       struct hfs_mdb *mdb;
+       struct hfs_cat_key key;
+       kdev_t dev = s->s_dev;
+       hfs_s32 part_size, part_start;
+       int part;
+
+       if (!parse_options((char *)data, HFS_SB(s), &part)) {
+               hfs_warn("hfs_fs: unable to parse mount options.\n");
+               goto bail3;
+       }
+
+       /* in case someone tries to unload the module while we wait on I/O */
+       MOD_INC_USE_COUNT;
+
+       lock_super(s);
+
+       /* set the device driver to 512-byte blocks */
+       set_blocksize(dev, HFS_SECTOR_SIZE);
+
+       /* look for a partition table and find the correct partition */
+       if (hfs_part_find(s, part, silent, &part_size, &part_start)) {
+               goto bail2;
+       }
+
+       if (!part_size && blk_size[MAJOR(dev)]) {
+               part_size =
+                       (blk_size[MAJOR(dev)][MINOR(dev)] << 1) - part_start;
+       }
+
+       mdb = hfs_mdb_get(s, part_size, part_start);
+       if (!mdb) {
+               if (!silent) {
+                       printk("VFS: Can't find a HFS filesystem on dev %s.\n",
+                              kdevname(dev));
+               }
+               goto bail2;
+       }
+       HFS_SB(s)->s_mdb = mdb;
+
+       if (HFS_ITYPE(mdb->next_id) != 0) {
+               hfs_warn("hfs_fs: too many files.\n");
+               goto bail1;
+       }
+
+       s->s_magic = HFS_SUPER_MAGIC;
+       s->s_blocksize_bits = HFS_SECTOR_SIZE_BITS;
+       s->s_blocksize = HFS_SECTOR_SIZE;
+       s->s_op = &hfs_super_operations;
+
+       /* try to get the root inode */
+       hfs_cat_build_key(htonl(HFS_POR_CNID),
+                         (struct hfs_name *)(mdb->vname), &key);
+       s->s_mounted = hfs_iget(mdb, &key, HFS_ITYPE_NORM);
+       if (!s->s_mounted) {
+               hfs_warn("hfs_fs: get root inode failed.\n");
+               goto bail1;
+       }
+       unlock_super(s);
+       return s;
+
+bail1:
+       hfs_mdb_put(mdb);
+bail2:
+       set_blocksize(dev, BLOCK_SIZE);
+       MOD_DEC_USE_COUNT;
+       unlock_super(s);
+bail3:
+       s->s_dev = 0;
+       return NULL;
+}
+
+int init_hfs_fs(void)
+{
+       return register_filesystem(&hfs_fs);
+}
+
+#ifdef MODULE
+int init_module(void) {
+       int error;
+
+#if defined(DEBUG_SIZES) || defined(DEBUG_ALL)
+       hfs_warn("HFS inode: %d bytes available\n",
+                sizeof(struct ext2_inode_info)-sizeof(struct hfs_inode_info));
+       hfs_warn("HFS super_block: %d bytes available\n",
+                sizeof(struct ext2_sb_info)-sizeof(struct hfs_sb_info));
+       if ((sizeof(struct hfs_inode_info)>sizeof(struct ext2_inode_info)) ||
+           (sizeof(struct hfs_sb_info)>sizeof(struct ext2_sb_info))) {
+               return -ENOMEM; /* well sort of */
+       }
+#endif
+       error = init_hfs_fs();
+       if (!error) {
+               /* register_symtab(NULL); */
+       }
+       return error;
+}
+
+void cleanup_module(void) {
+       hfs_cat_free();
+       unregister_filesystem(&hfs_fs);
+}
+#endif
+
+#if defined(DEBUG_ALL) || defined(DEBUG_MEM)
+long int hfs_alloc = 0;
+#endif
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/fs/hfs/sysdep.c linux-2.0.29/fs/hfs/sysdep.c
--- linux.vanilla/fs/hfs/sysdep.c       Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/fs/hfs/sysdep.c        Thu Apr 10 12:33:08 1997
@@ -0,0 +1,44 @@
+/*
+ * linux/fs/hfs/sysdep.c
+ *
+ * Copyright (C) 1996  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the code to do various system dependent things.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include <linux/hfs.h>
+#include <linux/hfs_fs_sb.h>
+#include <linux/hfs_fs_i.h>
+#include <linux/hfs_fs.h>
+
+/*
+ * hfs_buffer_get()
+ *
+ * Return a buffer for the 'block'th block of the media.
+ * If ('read'==0) then the buffer is not read from disk.
+ */
+hfs_buffer hfs_buffer_get(hfs_sysmdb sys_mdb, int block, int read) {
+       hfs_buffer tmp = HFS_BAD_BUFFER;
+
+       if (read) {
+               tmp = bread(sys_mdb->s_dev, block, HFS_SECTOR_SIZE);
+       } else {
+               tmp = getblk(sys_mdb->s_dev, block, HFS_SECTOR_SIZE);
+               if (tmp) {
+                       mark_buffer_uptodate(tmp, 1);
+               }
+       }
+       if (!tmp) {
+               hfs_error("hfs_fs: unable to read block 0x%08x from dev %s\n",
+                         block, hfs_mdb_name(sys_mdb));
+       }
+
+       return tmp;
+}
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/fs/hfs/trans.c linux-2.0.29/fs/hfs/trans.c
--- linux.vanilla/fs/hfs/trans.c        Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/fs/hfs/trans.c Thu Apr 10 12:33:18 1997
@@ -0,0 +1,556 @@
+/*
+ * linux/fs/hfs/trans.c
+ *
+ * Copyright (C) 1995-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains routines for converting between the Macintosh
+ * character set and various other encodings.  This includes dealing
+ * with ':' vs. '/' as the path-element separator.
+ *
+ * Latin-1 translation based on code contributed by Holger Schemel
+ * ([email protected]).
+ *
+ * The '8-bit', '7-bit ASCII' and '7-bit alphanumeric' encodings are
+ * implementations of the three encodings recommended by Apple in the
+ * document "AppleSingle/AppleDouble Formats: Developer's Note
+ * (9/94)".  This document is available from Apple's Technical
+ * Information Library from the World Wide Web server
+ * www.info.apple.com.
+ *
+ * The 'CAP' encoding is an implementation of the naming scheme used
+ * by the Columbia AppleTalk Package, available for anonymous FTP from
+ * ????.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#include <linux/hfs.h>
+#include <linux/hfs_fs_sb.h>
+#include <linux/hfs_fs_i.h>
+#include <linux/hfs_fs.h>
+
+/*================ File-local variables ================*/
+
+/* int->ASCII map for a single hex digit */
+static char hex[16] = {'0','1','2','3','4','5','6','7',
+                      '8','9','a','b','c','d','e','f'};
+/*
+ * Latin-1 to Mac character set map
+ *
+ * For the sake of consistency this map is generated from the Mac to
+ * Latin-1 map the first time it is needed.  This means there is just
+ * one map to maintain.
+ */
+static unsigned char latin2mac_map[128]; /* initially all zero */
+
+/*
+ * Mac to Latin-1 map for the upper 128 characters (both have ASCII in
+ * the lower 128 positions)
+ */
+static unsigned char mac2latin_map[128] = {
+       0xC4, 0xC5, 0xC7, 0xC9, 0xD1, 0xD6, 0xDC, 0xE1,
+       0xE0, 0xE2, 0xE4, 0xE3, 0xE5, 0xE7, 0xE9, 0xE8,
+       0xEA, 0xEB, 0xED, 0xEC, 0xEE, 0xEF, 0xF1, 0xF3,
+       0xF2, 0xF4, 0xF6, 0xF5, 0xFA, 0xF9, 0xFB, 0xFC,
+       0x00, 0xB0, 0xA2, 0xA3, 0xA7, 0xB7, 0xB6, 0xDF,
+       0xAE, 0xA9, 0x00, 0xB4, 0xA8, 0x00, 0xC6, 0xD8,
+       0x00, 0xB1, 0x00, 0x00, 0xA5, 0xB5, 0xF0, 0x00,
+       0x00, 0x00, 0x00, 0xAA, 0xBA, 0x00, 0xE6, 0xF8,
+       0xBF, 0xA1, 0xAC, 0x00, 0x00, 0x00, 0x00, 0xAB,
+       0xBB, 0x00, 0xA0, 0xC0, 0xC3, 0xD5, 0x00, 0x00,
+       0xAD, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF7, 0x00,
+       0xFF, 0x00, 0x00, 0xA4, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0xB8, 0x00, 0x00, 0xC2, 0xCA, 0xC1,
+       0xCB, 0xC8, 0xCD, 0xCE, 0xCF, 0xCC, 0xD3, 0xD4,
+       0x00, 0xD2, 0xDA, 0xDB, 0xD9, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/*================ File-local functions ================*/
+
+/*
+ * dehex()
+ *
+ * Given a hexadecimal digit in ASCII, return the integer representation.
+ */
+static inline const unsigned char dehex(char c) {
+       if ((c>='0')&&(c<='9')) {
+               return c-'0';
+       }
+       if ((c>='a')&&(c<='f')) {
+               return c-'a'+10;
+       }
+       if ((c>='A')&&(c<='F')) {
+               return c-'A'+10;
+       }
+       return 0xff;
+}
+
+/*================ Global functions ================*/
+
+/*
+ * hfs_mac2nat()
+ *
+ * Given a 'Pascal String' (a string preceded by a length byte) in
+ * the Macintosh character set produce the corresponding filename using
+ * the Netatalk name-mangling scheme, returning the length of the
+ * mangled filename.  Note that the output string is not NULL terminated.
+ *
+ * The name-mangling works as follows:
+ * Characters 32-126 (' '-'~') except '/' and any initial '.' are passed
+ * unchanged from input to output.  The remaining characters are replaced
+ * by three characters: ':xx' where xx is the hexadecimal representation
+ * of the character, using lowercase 'a' through 'f'.
+ */
+int hfs_mac2nat(char *out, const struct hfs_name *in) {
+       unsigned char c;
+       const unsigned char *p = in->Name;
+       int len = in->Len;
+       int count = 0;
+
+       /* Special case for .AppleDesktop which in the
+          distant future may be a pseudodirectory. */
+       if (strncmp(".AppleDesktop", p, len) == 0) {
+               strncpy(out, p, 13);
+               return 13;
+       }
+
+       while (len--) {
+               c = *p++;
+               if ((c<32) || (c=='/') || (c>126) || (!count && (c=='.'))) {
+                       *out++ = ':';
+                       *out++ = hex[(c>>4) & 0xf];
+                       *out++ = hex[c & 0xf];
+                       count += 3;
+               } else {
+                       *out++ = c;
+                       count++;
+               }
+       }
+       return count;
+}
+
+/*
+ * hfs_mac2cap()
+ *
+ * Given a 'Pascal String' (a string preceded by a length byte) in
+ * the Macintosh character set produce the corresponding filename using
+ * the CAP name-mangling scheme, returning the length of the mangled
+ * filename.  Note that the output string is not NULL terminated.
+ *
+ * The name-mangling works as follows:
+ * Characters 32-126 (' '-'~') except '/' are passed unchanged from
+ * input to output.  The remaining characters are replaced by three
+ * characters: ':xx' where xx is the hexadecimal representation of the
+ * character, using lowercase 'a' through 'f'.
+ */
+int hfs_mac2cap(char *out, const struct hfs_name *in) {
+       unsigned char c;
+       const unsigned char *p = in->Name;
+       int len = in->Len;
+       int count = 0;
+
+       while (len--) {
+               c = *p++;
+               if ((c<32) || (c=='/') || (c>126)) {
+                       *out++ = ':';
+                       *out++ = hex[(c>>4) & 0xf];
+                       *out++ = hex[c & 0xf];
+                       count += 3;
+               } else {
+                       *out++ = c;
+                       count++;
+               }
+       }
+       return count;
+}
+
+/*
+ * hfs_mac2eight()
+ *
+ * Given a 'Pascal String' (a string preceded by a length byte) in
+ * the Macintosh character set produce the corresponding filename using
+ * the '8-bit' name-mangling scheme, returning the length of the
+ * mangled filename.  Note that the output string is not NULL
+ * terminated.
+ *
+ * This is one of the three recommended naming conventions described
+ * in Apple's document "AppleSingle/AppleDouble Formats: Developer's
+ * Note (9/94)"
+ *
+ * The name-mangling works as follows:
+ * Characters 0, '%' and '/' are replaced by three characters: '%xx'
+ * where xx is the hexadecimal representation of the character, using
+ * lowercase 'a' through 'f'.  All other characters are passed
+ * unchanged from input to output.  Note that this format is mainly
+ * implemented for completeness and is rather hard to read.
+ */
+int hfs_mac2eight(char *out, const struct hfs_name *in) {
+       unsigned char c;
+       const unsigned char *p = in->Name;
+       int len = in->Len;
+       int count = 0;
+
+       while (len--) {
+               c = *p++;
+               if (!c || (c=='/') || (c=='%')) {
+                       *out++ = '%';
+                       *out++ = hex[(c>>4) & 0xf];
+                       *out++ = hex[c & 0xf];
+                       count += 3;
+               } else {
+                       *out++ = c;
+                       count++;
+               }
+       }
+       return count;
+}
+
+/*
+ * hfs_mac2seven()
+ *
+ * Given a 'Pascal String' (a string preceded by a length byte) in
+ * the Macintosh character set produce the corresponding filename using
+ * the '7-bit ASCII' name-mangling scheme, returning the length of the
+ * mangled filename.  Note that the output string is not NULL
+ * terminated.
+ *
+ * This is one of the three recommended naming conventions described
+ * in Apple's document "AppleSingle/AppleDouble Formats: Developer's
+ * Note (9/94)"
+ *
+ * The name-mangling works as follows:
+ * Characters 0, '%', '/' and 128-255 are replaced by three
+ * characters: '%xx' where xx is the hexadecimal representation of the
+ * character, using lowercase 'a' through 'f'. All other characters
+ * are passed unchanged from input to output.  Note that control
+ * characters (including newline) and space are unchanged make reading
+ * these filenames difficult.
+ */
+int hfs_mac2seven(char *out, const struct hfs_name *in) {
+       unsigned char c;
+       const unsigned char *p = in->Name;
+       int len = in->Len;
+       int count = 0;
+
+       while (len--) {
+               c = *p++;
+               if (!c || (c=='/') || (c=='%') || (c&0x80)) {
+                       *out++ = '%';
+                       *out++ = hex[(c>>4) & 0xf];
+                       *out++ = hex[c & 0xf];
+                       count += 3;
+               } else {
+                       *out++ = c;
+                       count++;
+               }
+       }
+       return count;
+}
+
+/*
+ * hfs_mac2alpha()
+ *
+ * Given a 'Pascal String' (a string preceded by a length byte) in
+ * the Macintosh character set produce the corresponding filename using
+ * the '7-bit alphanumeric' name-mangling scheme, returning the length
+ * of the mangled filename.  Note that the output string is not NULL
+ * terminated.
+ *
+ * This is one of the three recommended naming conventions described
+ * in Apple's document "AppleSingle/AppleDouble Formats: Developer's
+ * Note (9/94)"
+ *
+ * The name-mangling works as follows:
+ * The characters 'a'-'z', 'A'-'Z', '0'-'9', '_' and the last '.' in
+ * the filename are passed unchanged from input to output.  All
+ * remaining characters (including any '.'s other than the last) are
+ * replaced by three characters: '%xx' where xx is the hexadecimal
+ * representation of the character, using lowercase 'a' through 'f'.
+ */
+int hfs_mac2alpha(char *out, const struct hfs_name *in) {
+       unsigned char c;
+       const unsigned char *p = in->Name;
+       int len = in->Len;
+       int count = 0;
+       const unsigned char *lp;        /* last period */
+
+       /* strrchr() would be good here, but 'in' is not null-terminated */
+       for (lp=p+len-1; (lp>=p)&&(*lp!='.'); --lp) {}
+       ++lp;
+
+       while (len--) {
+               c = *p++;
+               if ((p==lp) || ((c>='0')&&(c<='9')) || ((c>='A')&&(c<='Z')) ||
+                               ((c>='a')&&(c<='z')) || (c=='_')) {
+                       *out++ = c;
+                       count++;
+               } else {
+                       *out++ = '%';
+                       *out++ = hex[(c>>4) & 0xf];
+                       *out++ = hex[c & 0xf];
+                       count += 3;
+               }
+       }
+       return count;
+}
+
+/*
+ * hfs_mac2triv()
+ *
+ * Given a 'Pascal String' (a string preceded by a length byte) in
+ * the Macintosh character set produce the corresponding filename using
+ * the 'trivial' name-mangling scheme, returning the length of the
+ * mangled filename.  Note that the output string is not NULL
+ * terminated.
+ *
+ * The name-mangling works as follows:
+ * The character '/', which is illegal in Linux filenames is replaced
+ * by ':' which never appears in HFS filenames.         All other characters
+ * are passed unchanged from input to output.
+ */
+int hfs_mac2triv(char *out, const struct hfs_name *in) {
+       unsigned char c;
+       const unsigned char *p = in->Name;
+       int len = in->Len;
+       int count = 0;
+
+       while (len--) {
+               c = *p++;
+               if (c=='/') {
+                       *out++ = ':';
+               } else {
+                       *out++ = c;
+               }
+               count++;
+       }
+       return count;
+}
+
+/*
+ * hfs_mac2latin()
+ *
+ * Given a 'Pascal String' (a string preceded by a length byte) in
+ * the Macintosh character set produce the corresponding filename using
+ * the 'Latin-1' name-mangling scheme, returning the length of the
+ * mangled filename.  Note that the output string is not NULL
+ * terminated.
+ *
+ * The Macintosh character set and Latin-1 are both extensions of the
+ * ASCII character set.         Some, but certainly not all, of the characters
+ * in the Macintosh character set are also in Latin-1 but not with the
+ * same encoding.  This name-mangling scheme replaces the characters in
+ * the Macintosh character set that have Latin-1 equivalents by those
+ * equivalents; the characters 32-126, excluding '/' and '%', are
+ * passed unchanged from input to output.  The remaining characters
+ * are replaced by three characters: '%xx' where xx is the hexadecimal
+ * representation of the character, using lowercase 'a' through 'f'.
+ *
+ * The array mac2latin_map[] indicates the correspondence between the
+ * two character sets. The byte in element x-128 gives the Latin-1
+ * encoding of the character with encoding x in the Macintosh
+ * character set.  A value of zero indicates Latin-1 has no
+ * corresponding character.
+ */
+int hfs_mac2latin(char *out, const struct hfs_name *in) {
+       unsigned char c;
+       const unsigned char *p = in->Name;
+       int len = in->Len;
+       int count = 0;
+
+       while (len--) {
+               c = *p++;
+
+               if ((c & 0x80) && mac2latin_map[c & 0x7f]) {
+                       *out++ = mac2latin_map[c & 0x7f];
+                       count++;
+               } else if ((c>=32) && (c<=126) && (c!='/') && (c!='%')) {
+                       *out++ =  c;
+                       count++;
+               } else {
+                       *out++ = '%';
+                       *out++ = hex[(c>>4) & 0xf];
+                       *out++ = hex[c & 0xf];
+                       count += 3;
+               }
+       }
+       return count;
+}
+
+/*
+ * hfs_colon2mac()
+ *
+ * Given an ASCII string (not null-terminated) and its length,
+ * generate the corresponding filename in the Macintosh character set
+ * using the 'CAP' name-mangling scheme, returning the length of the
+ * mangled filename.  Note that the output string is not NULL
+ * terminated.
+ *
+ * This routine is a inverse to hfs_mac2cap() and hfs_mac2nat().
+ * A ':' not followed by a 2-digit hexadecimal number (or followed
+ * by the codes for NULL or ':') is replaced by a '|'.
+ */
+void hfs_colon2mac(struct hfs_name *out, const char *in, int len) {
+       int hi, lo;
+       unsigned char code, c, *count;
+       unsigned char *p = out->Name;
+
+       out->Len = 0;
+       count = &out->Len;
+       while (len-- && (*count < HFS_NAMELEN)) {
+               c = *in++;
+               (*count)++;
+               if (c!=':') {
+                       *p++ = c;
+               } else if ((len<2) ||
+                          ((hi=dehex(in[0])) & 0xf0) ||
+                          ((lo=dehex(in[1])) & 0xf0) ||
+                          !(code = (hi << 4) | lo) ||
+                          (code == ':')) {
+                       *p++ = '|';
+               } else {
+                       *p++ = code;
+                       len -= 2;
+                       in += 2;
+               }
+       }
+}
+
+/*
+ * hfs_prcnt2mac()
+ *
+ * Given an ASCII string (not null-terminated) and its length,
+ * generate the corresponding filename in the Macintosh character set
+ * using Apple's three recommended name-mangling schemes, returning
+ * the length of the mangled filename. Note that the output string is
+ * not NULL terminated.
+ *
+ * This routine is a inverse to hfs_mac2alpha(), hfs_mac2seven() and
+ * hfs_mac2eight().
+ * A '%' not followed by a 2-digit hexadecimal number (or followed
+ * by the code for NULL or ':') is unchanged.
+ * A ':' is replaced by a '|'.
+ */
+void hfs_prcnt2mac(struct hfs_name *out, const char *in, int len) {
+       int hi, lo;
+       unsigned char code, c, *count;
+       unsigned char *p = out->Name;
+
+       out->Len = 0;
+       count = &out->Len;
+       while (len-- && (*count < HFS_NAMELEN)) {
+               c = *in++;
+               (*count)++;
+               if (c==':') {
+                       *p++ = '|';
+               } else if (c!='%') {
+                       *p++ = c;
+               } else if ((len<2) ||
+                          ((hi=dehex(in[0])) & 0xf0) ||
+                          ((lo=dehex(in[1])) & 0xf0) ||
+                          !(code = (hi << 4) | lo) ||
+                          (code == ':')) {
+                       *p++ = '%';
+               } else {
+                       *p++ = code;
+                       len -= 2;
+                       in += 2;
+               }
+       }
+}
+
+/*
+ * hfs_triv2mac()
+ *
+ * Given an ASCII string (not null-terminated) and its length,
+ * generate the corresponding filename in the Macintosh character set
+ * using the 'trivial' name-mangling scheme, returning the length of
+ * the mangled filename.  Note that the output string is not NULL
+ * terminated.
+ *
+ * This routine is a inverse to hfs_mac2triv().
+ * A ':' is replaced by a '/'.
+ */
+void hfs_triv2mac(struct hfs_name *out, const char *in, int len) {
+       unsigned char c, *count;
+       unsigned char *p = out->Name;
+
+       out->Len = 0;
+       count = &out->Len;
+       while (len-- && (*count < HFS_NAMELEN)) {
+               c = *in++;
+               (*count)++;
+               if (c==':') {
+                       *p++ = '/';
+               } else {
+                       *p++ = c;
+               }
+       }
+}
+
+/*
+ * hfs_latin2mac()
+ *
+ * Given an Latin-1 string (not null-terminated) and its length,
+ * generate the corresponding filename in the Macintosh character set
+ * using the 'Latin-1' name-mangling scheme, returning the length of
+ * the mangled filename.  Note that the output string is not NULL
+ * terminated.
+ *
+ * This routine is a inverse to hfs_latin2cap().
+ * A '%' not followed by a 2-digit hexadecimal number (or followed
+ * by the code for NULL or ':') is unchanged.
+ * A ':' is replaced by a '|'.
+ *
+ * Note that the character map is built the first time it is needed.
+ */
+void hfs_latin2mac(struct hfs_name *out, const char *in, int len)
+{
+       int hi, lo;
+       unsigned char code, c, *count;
+       unsigned char *p = out->Name;
+       static int map_initialized = 0;
+
+       if (!map_initialized) {
+               int i;
+
+               /* build the inverse mapping at run time */
+               for (i = 0; i < 128; i++) {
+                       if ((c = mac2latin_map[i])) {
+                               latin2mac_map[(int)c - 128] = i + 128;
+                       }
+               }
+               map_initialized = 1;
+       }
+
+       out->Len = 0;
+       count = &out->Len;
+       while (len-- && (*count < HFS_NAMELEN)) {
+               c = *in++;
+               (*count)++;
+
+               if (c==':') {
+                       *p++ = '|';
+               } else if (c!='%') {
+                       if (c<128 || !(*p = latin2mac_map[c-128])) {
+                               *p = c;
+                       }
+                       p++;
+               } else if ((len<2) ||
+                          ((hi=dehex(in[0])) & 0xf0) ||
+                          ((lo=dehex(in[1])) & 0xf0) ||
+                          !(code = (hi << 4) | lo) ||
+                          (code == ':')) {
+                       *p++ = '%';
+               } else {
+                       *p++ = code;
+                       len -= 2;
+                       in += 2;
+               }
+       }
+}
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/fs/hfs/version.c linux-2.0.29/fs/hfs/version.c
--- linux.vanilla/fs/hfs/version.c      Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/fs/hfs/version.c       Wed Apr  9 04:41:46 1997
@@ -0,0 +1,10 @@
+/*
+ * linux/fs/hfs/version.c
+ *
+ * Copyright (C) 1995-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the version string for this release.
+ */
+
+const char hfs_version[]="0.8.3";
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/include/asm-m68k/adb.h linux-2.0.29/include/asm-m68k/adb.h
--- linux.vanilla/include/asm-m68k/adb.h        Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/include/asm-m68k/adb.h Mon Apr 14 16:45:58 1997
@@ -0,0 +1,75 @@
+/*
+ * Definitions for talking to ADB and CUDA.  The CUDA is a microcontroller
+ * which controls the ADB, system power, RTC, and various other things on
+ * later Macintoshes
+ *
+ * Copyright (C) 1996 Paul Mackerras.
+ */
+
+/* First byte sent to or received from CUDA */
+#define ADB_PACKET     0
+#define CUDA_PACKET    1
+#define ERROR_PACKET   2
+#define TIMER_PACKET   3
+#define POWER_PACKET   4
+#define MACIIC_PACKET  5
+
+/* ADB commands (2nd byte) */
+#define ADB_BUSRESET           0
+#define ADB_FLUSH(id)          (1 + ((id) << 4))
+#define ADB_WRITEREG(id, reg)  (8 + (reg) + ((id) << 4))
+#define ADB_READREG(id, reg)   (0xc + (reg) + ((id) << 4))
+
+/* ADB default device IDs (upper 4 bits of 2nd byte) */
+#define ADB_DONGLE     1       /* "software execution control" devices */
+#define ADB_KEYBOARD   2
+#define ADB_MOUSE      3
+#define ADB_TABLET     4
+#define ADB_MODEM      5
+#define ADB_MISC       7       /* maybe a monitor */
+
+/* CUDA commands (2nd byte) */
+#define CUDA_WARM_START                0
+#define CUDA_AUTOPOLL          1
+#define CUDA_GET_6805_ADDR     2
+#define CUDA_GET_TIME          3
+#define CUDA_GET_PRAM          7
+#define CUDA_SET_6805_ADDR     8
+#define CUDA_SET_TIME          9
+#define CUDA_POWERDOWN         0xa
+#define CUDA_POWERUP_TIME      0xb
+#define CUDA_SET_PRAM          0xc
+#define CUDA_MS_RESET          0xd
+#define CUDA_SEND_DFAC         0xe
+#define CUDA_RESET_SYSTEM      0x11
+#define CUDA_SET_IPL           0x12
+#define CUDA_SET_AUTO_RATE     0x14
+#define CUDA_GET_AUTO_RATE     0x16
+#define CUDA_SET_DEVICE_LIST   0x19
+#define CUDA_GET_DEVICE_LIST   0x1a
+#define CUDA_GET_SET_IIC       0x22
+
+#ifdef __KERNEL__
+
+struct adb_request {
+    unsigned char data[16];
+    int nbytes;
+    unsigned char reply[16];
+    int reply_len;
+    unsigned char reply_expected;
+    unsigned char sent;
+    unsigned char got_reply;
+    void (*done)(struct adb_request *);
+    void *arg;
+    struct adb_request *next;
+};
+
+void via_adb_init(void);
+int adb_request(struct adb_request *req,
+                void (*done)(struct adb_request *), int nbytes, ...);
+int adb_send_request(struct adb_request *req);
+void adb_poll(void);
+int adb_register(int default_id,
+                void (*handler)(unsigned char *, int, struct pt_regs *));
+
+#endif /* __KERNEL */
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/include/asm-m68k/cuda.h linux-2.0.29/include/asm-m68k/cuda.h
--- linux.vanilla/include/asm-m68k/cuda.h       Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/include/asm-m68k/cuda.h        Thu Apr 10 16:36:37 1997
@@ -0,0 +1,74 @@
+/*
+ * Definitions for talking to the CUDA.  The CUDA is a microcontroller
+ * which controls the ADB, system power, RTC, and various other things.
+ *
+ * Copyright (C) 1996 Paul Mackerras.
+ */
+
+/* First byte sent to or received from CUDA */
+#define ADB_PACKET     0
+#define CUDA_PACKET    1
+#define ERROR_PACKET   2
+#define TIMER_PACKET   3
+#define POWER_PACKET   4
+#define MACIIC_PACKET  5
+
+/* ADB commands (2nd byte) */
+#define ADB_BUSRESET           0
+#define ADB_FLUSH(id)          (1 + ((id) << 4))
+#define ADB_WRITEREG(id, reg)  (8 + (reg) + ((id) << 4))
+#define ADB_READREG(id, reg)   (0xc + (reg) + ((id) << 4))
+
+/* ADB default device IDs (upper 4 bits of 2nd byte) */
+#define ADB_DONGLE     1       /* "software execution control" devices */
+#define ADB_KEYBOARD   2
+#define ADB_MOUSE      3
+#define ADB_TABLET     4
+#define ADB_MODEM      5
+#define ADB_MISC       7       /* maybe a monitor */
+
+/* CUDA commands (2nd byte) */
+#define CUDA_WARM_START                0
+#define CUDA_AUTOPOLL          1
+#define CUDA_GET_6805_ADDR     2
+#define CUDA_GET_TIME          3
+#define CUDA_GET_PRAM          7
+#define CUDA_SET_6805_ADDR     8
+#define CUDA_SET_TIME          9
+#define CUDA_POWERDOWN         0xa
+#define CUDA_POWERUP_TIME      0xb
+#define CUDA_SET_PRAM          0xc
+#define CUDA_MS_RESET          0xd
+#define CUDA_SEND_DFAC         0xe
+#define CUDA_RESET_SYSTEM      0x11
+#define CUDA_SET_IPL           0x12
+#define CUDA_SET_AUTO_RATE     0x14
+#define CUDA_GET_AUTO_RATE     0x16
+#define CUDA_SET_DEVICE_LIST   0x19
+#define CUDA_GET_DEVICE_LIST   0x1a
+#define CUDA_GET_SET_IIC       0x22
+
+#ifdef __KERNEL__
+
+struct adb_request {
+    unsigned char data[16];
+    int nbytes;
+    unsigned char reply[16];
+    int reply_len;
+    unsigned char reply_expected;
+    unsigned char sent;
+    unsigned char got_reply;
+    void (*done)(struct adb_request *);
+    void *arg;
+    struct adb_request *next;
+};
+
+void via_adb_init(void);
+int adb_request(struct adb_request *req,
+                void (*done)(struct adb_request *), int nbytes, ...);
+int adb_send_request(struct adb_request *req);
+void adb_poll(void);
+int adb_register(int default_id,
+                void (*handler)(unsigned char *, int, struct pt_regs *));
+
+#endif /* __KERNEL */
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/include/asm-m68k/setup.h linux-2.0.29/include/asm-m68k/setup.h
--- linux.vanilla/include/asm-m68k/setup.h      Thu Feb 27 17:42:23 1997
+++ linux-2.0.29/include/asm-m68k/setup.h       Thu Apr 10 14:09:59 1997
@@ -185,6 +185,43 @@

#endif /* __ASSEMBLY__ */

+#ifndef __ASSEMBLY__
+
+struct bi_Macintosh
+{
+       unsigned long videoaddr;
+       unsigned long videorow;
+       unsigned long videodepth;
+       unsigned long dimensions;
+       unsigned long args;
+       unsigned long boottime;
+       unsigned long gmtbias;
+       unsigned long bootver;
+       unsigned long videological;
+       unsigned long scc;
+       unsigned long id;
+       unsigned long memsize;
+       unsigned long serialmf;
+       unsigned long serialhsk;
+       unsigned long serialgpi;
+       unsigned long printf;
+       unsigned long printhsk;
+       unsigned long printgpi;
+       unsigned long cpuid;
+       unsigned long rombase;
+       unsigned long adbdelay;
+       unsigned long timedbra;
+};
+
+#else
+
+#define BI_videoaddr   BI_un
+#define BI_videorow    BI_videoaddr+4
+#define BI_videodepth  BI_videorow+4
+#define BI_dimensions  BI_videodepth+4
+#define BI_args                BI_dimensions+4
+#endif
+
#define NUM_MEMINFO  4

#define MACH_AMIGA   1
@@ -241,8 +278,13 @@
#  define MACH_TYPE (MACH_ATARI)
#endif

+/*
+ *     FIXME:  When we have the booter we can multiarch this
+ */
+
#if defined(CONFIG_MAC)
-#  error Currently no Mac support!
+#  define MAC_TYPE (MACH_MAC)
+#  define MACH_IS_MAC (1)
#endif

#ifndef MACH_TYPE
@@ -336,6 +378,7 @@
       union {
               struct bi_Amiga bi_ami;         /* Amiga specific information */
               struct bi_Atari bi_ata;         /* Atari specific information */
+               struct bi_Macintosh bi_mac;     /* Mac specific information */
       } bi_un;
};
#define bi_amiga bi_un.bi_ami
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/include/linux/compile.h linux-2.0.29/include/linux/compile.h
--- linux.vanilla/include/linux/compile.h       Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/include/linux/compile.h        Wed Apr 16 16:58:02 1997
@@ -0,0 +1,6 @@
+#define UTS_VERSION "#315 Wed Apr 16 16:57:59 BST 1997"
+#define LINUX_COMPILE_TIME "16:58:00"
+#define LINUX_COMPILE_BY "alan"
+#define LINUX_COMPILE_HOST "blacksun"
+#define LINUX_COMPILE_DOMAIN "cymru.net"
+#define LINUX_COMPILER "gcc version 2.7.2.2"
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/include/linux/hfs.h linux-2.0.29/include/linux/hfs.h
--- linux.vanilla/include/linux/hfs.h   Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/include/linux/hfs.h    Thu Apr 10 14:34:06 1997
@@ -0,0 +1,491 @@
+/*
+ * linux/fs/hfs/hfs.h
+ *
+ * Copyright (C) 1995-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ */
+
+#ifndef _HFS_H
+#define _HFS_H
+
+#include <linux/hfs_sysdep.h>
+
+#define HFS_NEW(X)     ((X) = hfs_malloc(sizeof(*(X))))
+#define HFS_DELETE(X)  { hfs_free((X), sizeof(*(X))); (X) = NULL; }
+
+/* offsets to various blocks */
+#define HFS_DD_BLK             0 /* Driver Descriptor block */
+#define HFS_PMAP_BLK           1 /* First block of partition map */
+#define HFS_MDB_BLK            2 /* Block (w/i partition) of MDB */
+
+/* magic numbers for various disk blocks */
+#define HFS_DRVR_DESC_MAGIC    0x4552 /* "ER": driver descriptor map */
+#define HFS_OLD_PMAP_MAGIC     0x5453 /* "TS": old-type partition map */
+#define HFS_NEW_PMAP_MAGIC     0x504D /* "PM": new-type partition map */
+#define HFS_SUPER_MAGIC                0x4244 /* "BD": HFS MDB (super block) */
+#define HFS_MFS_SUPER_MAGIC    0xD2D7 /* MFS MDB (super block) */
+
+/* magic numbers for various internal structures */
+#define HFS_FILE_MAGIC         0x4801
+#define HFS_DIR_MAGIC          0x4802
+#define HFS_MDB_MAGIC          0x4803
+#define HFS_EXT_MAGIC          0x4804 /* XXX currently unused */
+#define HFS_BREC_MAGIC         0x4811 /* XXX currently unused */
+#define HFS_BTREE_MAGIC                0x4812
+#define HFS_BNODE_MAGIC                0x4813
+
+/* various FIXED size parameters */
+#define HFS_SECTOR_SIZE                512    /* size of an HFS sector */
+#define HFS_SECTOR_SIZE_BITS   9      /* log_2(HFS_SECTOR_SIZE) */
+#define HFS_NAMELEN            31     /* maximum length of an HFS filename */
+#define HFS_NAMEMAX            (3*31) /* max size of ENCODED filename */
+#define HFS_BM_MAXBLOCKS       (16)   /* max number of bitmap blocks */
+#define HFS_BM_BPB (8*HFS_SECTOR_SIZE) /* number of bits per bitmap block */
+#define HFS_MAX_VALENCE                32767U
+#define HFS_FORK_MAX           (0x7FFFFFFF)
+
+/* Meanings of the drAtrb field of the MDB,
+ * Reference: _Inside Macintosh: Files_ p. 2-61
+ */
+#define HFS_SB_ATTRIB_HLOCK 0x0080
+#define HFS_SB_ATTRIB_CLEAN 0x0100
+#define HFS_SB_ATTRIB_SPARED 0x0200
+#define HFS_SB_ATTRIB_SLOCK 0x8000
+
+/* 2**16 - 1 */
+#define HFS_USHRT_MAX  65535
+
+/* Some special File ID numbers */
+#define HFS_POR_CNID   1       /* Parent Of the Root */
+#define HFS_ROOT_CNID  2       /* ROOT directory */
+#define HFS_EXT_CNID   3       /* EXTents B-tree */
+#define HFS_CAT_CNID   4       /* CATalog B-tree */
+#define HFS_BAD_CNID   5       /* BAD blocks file */
+
+/* values for hfs_cat_rec.cdrType */
+#define HFS_CDR_DIR    0x01
+#define HFS_CDR_FIL    0x02
+#define HFS_CDR_THD    0x03
+#define HFS_CDR_FTH    0x04
+
+/* legal values for hfs_ext_key.FkType and hfs_file.fork */
+#define HFS_FK_DATA    0x00
+#define HFS_FK_RSRC    0xFF
+
+/* bits in hfs_fil_entry.Flags */
+#define HFS_FIL_LOCK   0x01
+#define HFS_FIL_THD    0x02
+#define HFS_FIL_USED   0x80
+
+/* Access types used when requesting access to a B-node */
+#define HFS_LOCK_NONE  0x0000  /* Illegal */
+#define HFS_LOCK_READ  0x0001  /* read-only access */
+#define HFS_LOCK_RESRV 0x0002  /* might potentially modify */
+#define HFS_LOCK_WRITE 0x0003  /* will modify now (exclusive access) */
+#define HFS_LOCK_MASK  0x000f
+
+/* Flags field of the hfs_path_elem */
+#define HFS_BPATH_FIRST                0x0100
+#define HFS_BPATH_OVERFLOW     0x0200
+#define HFS_BPATH_UNDERFLOW    0x0400
+#define HFS_BPATH_MASK         0x0f00
+
+/* Flags for hfs_bfind() */
+#define HFS_BFIND_EXACT                0x0010
+#define HFS_BFIND_LOCK         0x0020
+
+/* Modes for hfs_bfind() */
+#define HFS_BFIND_WRITE   (HFS_LOCK_RESRV|HFS_BFIND_EXACT|HFS_BFIND_LOCK)
+#define HFS_BFIND_READ_EQ (HFS_LOCK_READ|HFS_BFIND_EXACT)
+#define HFS_BFIND_READ_LE (HFS_LOCK_READ)
+#define HFS_BFIND_INSERT  (HFS_LOCK_RESRV|HFS_BPATH_FIRST|HFS_BPATH_OVERFLOW)
+#define HFS_BFIND_DELETE \
+        (HFS_LOCK_RESRV|HFS_BFIND_EXACT|HFS_BPATH_FIRST|HFS_BPATH_UNDERFLOW)
+
+/*======== HFS structures as they appear on the disk ========*/
+
+/* Pascal-style string of up to 31 characters */
+struct hfs_name {
+       hfs_byte_t      Len;
+       hfs_byte_t      Name[31];
+};
+
+typedef struct {
+       hfs_word_t      v;
+       hfs_word_t      h;
+} hfs_point_t;
+
+typedef struct {
+       hfs_word_t      top;
+       hfs_word_t      left;
+       hfs_word_t      bottom;
+       hfs_word_t      right;
+} hfs_rect_t;
+
+typedef struct {
+       hfs_lword_t      fdType;
+       hfs_lword_t      fdCreator;
+       hfs_word_t       fdFlags;
+       hfs_point_t      fdLocation;
+       hfs_word_t       fdFldr;
+} hfs_finfo_t;
+
+typedef struct {
+       hfs_word_t      fdIconID;
+       hfs_byte_t      fdUnused[8];
+       hfs_word_t      fdComment;
+       hfs_lword_t     fdPutAway;
+} hfs_fxinfo_t;
+
+typedef struct {
+       hfs_rect_t      frRect;
+       hfs_word_t      frFlags;
+       hfs_point_t     frLocation;
+       hfs_word_t      frView;
+} hfs_dinfo_t;
+
+typedef struct {
+       hfs_point_t     frScroll;
+       hfs_lword_t     frOpenChain;
+       hfs_word_t      frUnused;
+       hfs_word_t      frComment;
+       hfs_lword_t     frPutAway;
+} hfs_dxinfo_t;
+
+union hfs_finder_info {
+       struct {
+               hfs_finfo_t     finfo;
+               hfs_fxinfo_t    fxinfo;
+       } file;
+       struct {
+               hfs_dinfo_t     dinfo;
+               hfs_dxinfo_t    dxinfo;
+       } dir;
+};
+
+/* A btree record key on disk */
+struct hfs_bkey {
+       hfs_byte_t      KeyLen;         /* number of bytes in the key */
+       hfs_byte_t      value[1];       /* (KeyLen) bytes of key */
+};
+
+/* Cast to a pointer to a generic bkey */
+#define        HFS_BKEY(X)     (((void)((X)->KeyLen)), ((struct hfs_bkey *)(X)))
+
+/* The key used in the catalog b-tree: */
+struct hfs_cat_key {
+       hfs_byte_t      KeyLen; /* number of bytes in the key */
+       hfs_byte_t      Resrv1; /* padding */
+       hfs_lword_t     ParID;  /* CNID of the parent dir */
+       struct hfs_name CName;  /* The filename of the entry */
+};
+
+/* The key used in the extents b-tree: */
+struct hfs_ext_key {
+       hfs_byte_t      KeyLen; /* number of bytes in the key */
+       hfs_byte_t      FkType; /* HFS_FK_{DATA,RSRC} */
+       hfs_lword_t     FNum;   /* The File ID of the file */
+       hfs_word_t      FABN;   /* allocation blocks number*/
+};
+
+/*======== Data structures kept in memory ========*/
+
+/*
+ * struct hfs_mdb
+ *
+ * The fields from the MDB of an HFS filesystem
+ */
+struct hfs_mdb {
+       int                     magic;          /* A magic number */
+       unsigned char           vname[28];      /* The volume name */
+       hfs_sysmdb              sys_mdb;        /* superblock */
+       hfs_buffer              buf;            /* The hfs_buffer
+                                                  holding the real
+                                                  superblock (aka VIB
+                                                  or MDB) */
+       hfs_buffer              alt_buf;        /* The hfs_buffer holding
+                                                  the alternate superblock */
+       hfs_buffer              bitmap[16];     /* The hfs_buffer holding the
+                                                  allocation bitmap */
+       struct hfs_btree *      ext_tree;       /* Information about
+                                                  the extents b-tree */
+       struct hfs_btree *      cat_tree;       /* Information about
+                                                  the catalog b-tree */
+       hfs_u32                 file_count;     /* The number of
+                                                  regular files in
+                                                  the filesystem */
+       hfs_u32                 dir_count;      /* The number of
+                                                  directories in the
+                                                  filesystem */
+       hfs_u32                 next_id;        /* The next available
+                                                  file id number */
+       hfs_u32                 clumpablks;     /* The number of allocation
+                                                  blocks to try to add when
+                                                  extending a file */
+       hfs_u32                 write_count;    /* The number of MDB
+                                                  writes (a sort of
+                                                  version number) */
+       hfs_u32                 dev_size;       /* The number of
+                                                  512-byte blocks
+                                                  on the device */
+       hfs_u32                 fs_start;       /* The first 512-byte
+                                                  block represented
+                                                  in the bitmap */
+       hfs_u32                 create_date;    /* In network byte-order */
+       hfs_u32                 modify_date;    /* In network byte-order */
+       hfs_u32                 backup_date;    /* In network byte-order */
+       hfs_u16                 root_files;     /* The number of
+                                                  regular
+                                                  (non-directory)
+                                                  files in the root
+                                                  directory */
+       hfs_u16                 root_dirs;      /* The number of
+                                                  directories in the
+                                                  root directory */
+       hfs_u16                 fs_ablocks;     /* The number of
+                                                  allocation blocks
+                                                  in the filesystem */
+       hfs_u16                 free_ablocks;   /* The number of unused
+                                                  allocation blocks
+                                                  in the filesystem */
+       hfs_u16                 alloc_blksz;    /* The number of
+                                                  512-byte blocks per
+                                                  "allocation block" */
+       hfs_u16                 attrib;         /* Attribute word */
+       hfs_wait_queue          rename_wait;
+       int                     rename_lock;
+       hfs_wait_queue          bitmap_wait;
+       int                     bitmap_lock;
+};
+
+/*
+ * struct hfs_extent
+ *
+ * The offset to allocation block mapping for a given file is
+ * contained in a series of these structures.  Each (struct
+ * hfs_extent) records up to three runs of contiguous allocation
+ * blocks.  An allocation block is a contiguous group of physical
+ * blocks.
+ */
+struct hfs_extent {
+       int                magic;     /* A magic number */
+       unsigned short     start;     /* Where in the file this record
+                                        begins (in allocation blocks) */
+       unsigned short     end;       /* Where in the file this record
+                                        ends (in allocation blocks) */
+       unsigned short     block[3];  /* The allocation block on disk which
+                                        begins this extent */
+       unsigned short     length[3]; /* The number of allocation blocks
+                                        in this extent */
+       struct hfs_extent  *next;     /* Next extent record for this file */
+       struct hfs_extent  *prev;     /* Previous extent record for this file */
+       int                count;     /* Number of times it is used */
+};
+
+/*
+ * struct hfs_dir
+ *
+ * This structure holds information specific
+ * to a directory in an HFS filesystem.
+ */
+struct hfs_dir {
+       int             magic;          /* A magic number */
+       hfs_u16         flags;
+       hfs_u16         dirs;           /* Number of directories in this one */
+       hfs_u16         files;          /* Number of files in this directory */
+       int             readers;
+       hfs_wait_queue  read_wait;
+       int             writers;
+       hfs_wait_queue  write_wait;
+};
+
+/*
+ * struct hfs_fork
+ *
+ * This structure holds the information
+ * specific to a single fork of a file.
+ */
+struct hfs_fork {
+       struct hfs_cat_entry    *entry;    /* The file this fork is part of */
+       struct hfs_extent       first;     /* The first extent record for
+                                                this fork */
+       struct hfs_extent       *cache;    /* The most-recently accessed
+                                                extent record for this fork */
+       hfs_u32                 lsize;     /* The logical size in bytes */
+       hfs_u32                 psize;     /* The phys size (512-byte blocks) */
+        hfs_u8                 fork;      /* Which fork is this? */
+};
+
+/*
+ * struct hfs_file
+ *
+ * This structure holds information specific
+ * to a file in an HFS filesystem.
+ */
+struct hfs_file {
+       int                magic;
+       struct hfs_fork    data_fork;
+       struct hfs_fork    rsrc_fork;
+       hfs_u16            clumpablks;
+       hfs_u8             flags;
+};
+
+/*
+ * struct hfs_file
+ *
+ * This structure holds information about a
+ * file or directory in an HFS filesystem.
+ *
+ * 'wait' must remain 1st and 'next' 2nd since we do some pointer arithmetic.
+ */
+struct hfs_cat_entry {
+       hfs_wait_queue          wait;
+       struct hfs_cat_entry    *next;
+       struct hfs_cat_entry    *prev;
+       struct hfs_cat_entry    *hash_next;
+       struct hfs_cat_entry    *hash_prev;
+       struct hfs_mdb          *mdb;
+       hfs_sysentry            sys_entry;
+       struct hfs_cat_key      key;
+       union hfs_finder_info   info;
+       hfs_u32                 cnid;           /* In network byte-order */
+       hfs_u32                 create_date;    /* In network byte-order */
+       hfs_u32                 modify_date;    /* In network byte-order */
+       hfs_u32                 backup_date;    /* In network byte-order */
+       unsigned short          count;
+       unsigned char           lock;
+       unsigned char           dirt;
+       unsigned char           key_dirt;
+       unsigned char           deleted;
+       hfs_u8                  type;
+       union {
+               struct hfs_dir  dir;
+               struct hfs_file file;
+       } u;
+};
+
+/*
+ * struct hfs_bnode_ref
+ *
+ * A pointer to a (struct hfs_bnode) and the type of lock held on it.
+ */
+struct hfs_bnode_ref {
+        struct hfs_bnode *bn;
+        int lock_type;
+};
+
+/*
+ * struct hfs_belem
+ *
+ * An element of the path from the root of a B-tree to a leaf.
+ * Includes the reference to a (struct hfs_bnode), the index of
+ * the appropriate record in that node, and some flags.
+ */
+struct hfs_belem {
+       struct hfs_bnode_ref    bnr;
+       int                     record;
+       int                     flags;
+};
+
+/*
+ * struct hfs_brec
+ *
+ * The structure returned by hfs_bfind() to describe the requested record.
+ */
+struct hfs_brec {
+       int                     keep_flags;
+       struct hfs_btree        *tree;
+       struct hfs_belem        *top;
+       struct hfs_belem        *bottom;
+       struct hfs_belem        elem[9];
+       struct hfs_bkey         *key;
+       void                    *data;  /* The actual data */
+};
+
+/*================ Function prototypes ================*/
+
+/* bdelete.c */
+extern int hfs_bdelete(struct hfs_btree *, const struct hfs_bkey *);
+
+/* bfind.c */
+extern void hfs_brec_relse(struct hfs_brec *, struct hfs_belem *);
+extern int hfs_bsucc(struct hfs_brec *, int);
+extern int hfs_bfind(struct hfs_brec *, struct hfs_btree *,
+                    const struct hfs_bkey *, int);
+
+/* binsert.c */
+extern int hfs_binsert(struct hfs_btree *, const struct hfs_bkey *,
+                      const void *, hfs_u16);
+
+/* bitmap.c */
+extern hfs_u16 hfs_vbm_count_free(const struct hfs_mdb *, hfs_u16);
+extern hfs_u16 hfs_vbm_search_free(const struct hfs_mdb *, hfs_u16 *);
+extern int hfs_set_vbm_bits(struct hfs_mdb *, hfs_u16, hfs_u16);
+extern int hfs_clear_vbm_bits(struct hfs_mdb *, hfs_u16, hfs_u16);
+
+/* bitops.c */
+extern hfs_u32 hfs_find_first_zero_bit(const void *, hfs_u32);
+extern hfs_u32 hfs_find_next_zero_bit(const void *, hfs_u32, hfs_u32);
+extern hfs_u32 hfs_count_zero_bits(const void *, hfs_u32, hfs_u32, hfs_u32);
+
+/* btree.c */
+extern struct hfs_btree *hfs_btree_init(struct hfs_mdb *, ino_t,
+                                       hfs_byte_t *, hfs_u32, hfs_u32);
+extern void hfs_btree_free(struct hfs_btree *);
+extern void hfs_btree_commit(struct hfs_btree *, hfs_byte_t *, hfs_lword_t);
+
+/* catalog.c */
+extern void hfs_cat_put(struct hfs_cat_entry *);
+extern struct hfs_cat_entry *hfs_cat_get(struct hfs_mdb *,
+                                        const struct hfs_cat_key *);
+
+extern void hfs_cat_invalidate(struct hfs_mdb *);
+extern void hfs_cat_commit(struct hfs_mdb *);
+extern void hfs_cat_free(void);
+
+extern int hfs_cat_compare(const struct hfs_cat_key *,
+                          const struct hfs_cat_key *);
+extern void hfs_cat_build_key(hfs_u32, const struct hfs_name *,
+                             struct hfs_cat_key *);
+extern struct hfs_cat_entry *hfs_cat_parent(struct hfs_cat_entry *);
+
+extern int hfs_cat_open(struct hfs_cat_entry *, struct hfs_brec *);
+extern int hfs_cat_next(struct hfs_cat_entry *, struct hfs_brec *,
+                       hfs_u16, hfs_u32 *, hfs_u8 *);
+extern void hfs_cat_close(struct hfs_cat_entry *, struct hfs_brec *);
+
+extern int hfs_cat_create(struct hfs_cat_entry *, struct hfs_cat_key *,
+                         hfs_u8, hfs_u32, hfs_u32, struct hfs_cat_entry **);
+extern int hfs_cat_mkdir(struct hfs_cat_entry *, struct hfs_cat_key *,
+                        struct hfs_cat_entry **);
+extern int hfs_cat_delete(struct hfs_cat_entry *, struct hfs_cat_entry *, int);
+extern int hfs_cat_move(struct hfs_cat_entry *, struct hfs_cat_entry *,
+                       struct hfs_cat_entry *, struct hfs_cat_key *,
+                       struct hfs_cat_entry **);
+
+/* extent.c */
+extern int hfs_ext_compare(const struct hfs_ext_key *,
+                          const struct hfs_ext_key *);
+extern void hfs_extent_in(struct hfs_fork *, const hfs_byte_t *);
+extern void hfs_extent_out(const struct hfs_fork *, hfs_byte_t *);
+extern int hfs_extent_map(struct hfs_fork *, int, int);
+extern void hfs_extent_adj(struct hfs_fork *);
+extern void hfs_extent_free(struct hfs_fork *);
+
+/* mdb.c */
+extern struct hfs_mdb *hfs_mdb_get(hfs_sysmdb, hfs_s32, hfs_s32);
+extern void hfs_mdb_commit(struct hfs_mdb *, int);
+extern void hfs_mdb_put(struct hfs_mdb *);
+
+/* part_tbl.c */
+extern int hfs_part_find(hfs_sysmdb, int, int, hfs_s32 *, hfs_s32 *);
+
+/* string.c */
+extern unsigned int hfs_strhash(const struct hfs_name *);
+extern int hfs_strcmp(const struct hfs_name *, const struct hfs_name *);
+extern int hfs_streq(const struct hfs_name *, const struct hfs_name *);
+extern void hfs_tolower(unsigned char *, int);
+
+#endif
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/include/linux/hfs_btree.h linux-2.0.29/include/linux/hfs_btree.h
--- linux.vanilla/include/linux/hfs_btree.h     Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/include/linux/hfs_btree.h      Thu Apr 10 12:34:30 1997
@@ -0,0 +1,268 @@
+/*
+ * linux/fs/hfs/hfs_btree.h
+ *
+ * Copyright (C) 1995-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains the declarations of the private B-tree
+ * structures and functions.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ */
+
+#ifndef _HFS_BTREE_H
+#define _HFS_BTREE_H
+
+#include "hfs.h"
+
+/*================ Variable-like macros ================*/
+
+/* The stickiness of a (struct hfs_bnode) */
+#define HFS_NOT_STICKY 0
+#define HFS_STICKY     1
+
+/* The number of hash buckets in a B-tree's bnode cache */
+#define HFS_CACHELEN   17      /* primes are best? */
+
+/*
+ * Legal values for the 'ndType' field of a (struct NodeDescriptor)
+ *
+ * Reference: _Inside Macintosh: Files_ p. 2-65
+ */
+#define ndIndxNode     0x00    /* An internal (index) node */
+#define ndHdrNode      0x01    /* The tree header node (node 0) */
+#define ndMapNode      0x02    /* Holds part of the bitmap of used nodes */
+#define ndLeafNode     0xFF    /* A leaf (ndNHeight==1) node */
+
+/*================ Function-like macros ================*/
+
+/* Access the cache slot which should contain the desired node */
+#define bhash(tree, node) ((tree)->cache[(node) % HFS_CACHELEN])
+
+/* round up to multiple of sizeof(hfs_u16) */
+#define ROUND(X) ((X + sizeof(hfs_u16) - 1) & ~(sizeof(hfs_u16)-1))
+
+/* Refer to the (base-1) array of offsets in a bnode */
+#define RECTBL(X,N) \
+       (((hfs_u16 *)(hfs_buffer_data((X)->buf)+HFS_SECTOR_SIZE))-(N))
+
+/*================ Private data types ================*/
+
+/*
+ * struct BTHdrRec
+ *
+ * The B-tree header record
+ *
+ * This data structure is stored in the first node (512-byte block) of
+ * each B-tree file.  It contains important information about the
+ * B-tree.  Most fields vary over the life of the tree and are
+ * indicated by a 'V' in the comments. The other fields are fixed for
+ * the life of the tree and are indicated by a 'F'.
+ *
+ * Reference: _Inside Macintosh: Files_ pp. 2-68 through 2-69 */
+struct BTHdrRec {
+       hfs_word_t  bthDepth;   /* (V) The number of levels in this B-tree */
+       hfs_lword_t bthRoot;    /* (V) The node number of the root node */
+       hfs_lword_t bthNRecs;   /* (V) The number of leaf records */
+       hfs_lword_t bthFNode;   /* (V) The number of the first leaf node */
+       hfs_lword_t bthLNode;   /* (V) The number of the last leaf node */
+       hfs_word_t  bthNodeSize;        /* (F) The number of bytes in a node (=512) */
+       hfs_word_t  bthKeyLen;  /* (F) The length of a key in an index node */
+       hfs_lword_t bthNNodes;  /* (V) The total number of nodes */
+       hfs_lword_t bthFree;    /* (V) The number of unused nodes */
+       hfs_byte_t  bthResv[76];        /* Reserved */
+};
+
+/*
+ * struct NodeDescriptor
+ *
+ * The B-tree node descriptor.
+ *
+ * This structure begins each node in the B-tree file. It contains
+ * important information about the node's contents.  'V' and 'F' in
+ * the comments indicate fields that are variable or fixed over the
+ * life of a node, where the 'life' of a node is defined as the period
+ * between leaving and reentering the free pool.
+ *
+ * Reference: _Inside Macintosh: Files_ p. 2-64
+ */
+struct NodeDescriptor {
+       hfs_lword_t ndFLink;    /* (V) Number of the next node at this level */
+       hfs_lword_t ndBLink;    /* (V) Number of the prev node at this level */
+       hfs_byte_t  ndType;     /* (F) The type of node */
+       hfs_byte_t  ndNHeight;  /* (F) The level of this node (leaves=1) */
+       hfs_word_t  ndNRecs;    /* (V) The number of records in this node */
+       hfs_word_t  ndResv2;    /* Reserved */
+};
+
+/*
+ * typedef hfs_cmpfn
+ *
+ * The type 'hfs_cmpfn' is a comparison function taking 2 keys and
+ * returning a positive, negative or zero integer according to the
+ * ordering of the two keys (just like strcmp() does for strings).
+ */
+typedef int (*hfs_cmpfn)(const void *, const void *);
+
+/*
+ * struct hfs_bnode
+ *
+ * An in-core B-tree node
+ *
+ * This structure holds information from the NodeDescriptor in native
+ * byte-order, a pointer to the buffer which contains the actual
+ * node and fields necessary for locking access to the node during
+ * updates.  The use of the locking fields is explained with the
+ * locking functions.
+ */
+struct hfs_bnode {
+       int                 magic;   /* Magic number to guard against
+                                       wild pointers */
+       hfs_buffer          buf;     /* The buffer containing the
+                                       actual node */
+       struct hfs_btree    *tree;   /* The tree to which this node
+                                       belongs */
+       struct hfs_bnode    *prev;   /* Next node in this hash bucket */
+       struct hfs_bnode    *next;   /* Previous node in this hash
+                                       bucket */
+       int                 sticky;  /* Boolean: non-zero means keep
+                                       this node in-core (set for
+                                       root and head) */
+       hfs_u32             node;    /* Node number */
+       /* locking related fields: */
+       hfs_wait_queue      wqueue;  /* Wait queue for write access */
+       hfs_wait_queue      rqueue;  /* Wait queue for read or reserve
+                                       access */
+       int                 count;   /* Number of processes accessing
+                                       this node */
+       int                 resrv;   /* Boolean, true means a process
+                                       had placed a 'reservation' on
+                                       this node */
+       int                 lock;    /* Boolean, true means some
+                                       process has exclusive access,
+                                       so KEEP OUT */
+       /* fields from the NodeDescriptor in native byte-order: */
+       hfs_u32             ndFLink;
+       hfs_u32             ndBLink;
+       hfs_u16             ndNRecs;
+       hfs_u8              ndType;
+       hfs_u8              ndNHeight;
+};
+
+/*
+ * struct hfs_btree
+ *
+ * An in-core B-tree.
+ *
+ * This structure holds information from the BTHdrRec, MDB
+ * (superblock) and other information needed to work with the B-tree.
+ */
+struct hfs_btree {
+       int                     magic;         /* Magic number to
+                                                 guard against wild
+                                                 pointers */
+       hfs_cmpfn               compare;       /* Comparison function
+                                                 for this tree */
+       struct hfs_bnode        head;          /* in-core copy of node 0 */
+       struct hfs_bnode        *root;         /* Pointer to the in-core
+                                                 copy of the root node */
+       hfs_sysmdb              sys_mdb;       /* The "device" holding
+                                                 the filesystem */
+       int                     reserved;      /* bnodes claimed but
+                                                 not yet used */
+       struct hfs_bnode                       /* The bnode cache */
+                               *cache[HFS_CACHELEN];
+       struct hfs_cat_entry    entry;         /* Fake catalog entry */
+       int                     lock;
+       hfs_wait_queue          wait;
+       int                     dirt;
+       /* Fields from the BTHdrRec in native byte-order: */
+       hfs_u32                 bthRoot;
+       hfs_u32                 bthNRecs;
+       hfs_u32                 bthFNode;
+       hfs_u32                 bthLNode;
+       hfs_u32                 bthNNodes;
+       hfs_u32                 bthFree;
+       hfs_u16                 bthKeyLen;
+       hfs_u16                 bthDepth;
+};
+
+/*================ Global functions ================*/
+
+/* Convert a (struct hfs_bnode *) and an index to the value of the
+   n-th offset in the bnode (N >= 1) to the offset */
+extern inline hfs_u16 bnode_offset(const struct hfs_bnode *bnode, int n)
+{ return hfs_get_hs(RECTBL(bnode,n)); }
+
+/* Convert a (struct hfs_bnode *) and an index to the size of the
+   n-th record in the bnode (N >= 1) */
+extern inline hfs_u16 bnode_rsize(const struct hfs_bnode *bnode, int n)
+{ return bnode_offset(bnode, n+1) - bnode_offset(bnode, n); }
+
+/* Convert a (struct hfs_bnode *) to the offset of the empty part */
+extern inline hfs_u16 bnode_end(const struct hfs_bnode *bnode)
+{ return bnode_offset(bnode, bnode->ndNRecs + 1); }
+
+/* Convert a (struct hfs_bnode *) to the number of free bytes it contains */
+extern inline hfs_u16 bnode_freespace(const struct hfs_bnode *bnode)
+{ return HFS_SECTOR_SIZE - bnode_end(bnode)
+         - (bnode->ndNRecs + 1)*sizeof(hfs_u16); }
+
+/* Convert a (struct hfs_bnode *) X and an index N to
+   the address of the record N in the bnode (N >= 1) */
+extern inline void *bnode_datastart(const struct hfs_bnode *bnode)
+{ return (void *)(hfs_buffer_data(bnode->buf)+sizeof(struct NodeDescriptor)); }
+
+/* Convert a (struct hfs_bnode *) to the address of the empty part */
+extern inline void *bnode_dataend(const struct hfs_bnode *bnode)
+{ return (void *)(hfs_buffer_data(bnode->buf) + bnode_end(bnode)); }
+
+/* Convert various pointers to address of record's key */
+extern inline void *bnode_key(const struct hfs_bnode *bnode, int n)
+{ return (void *)(hfs_buffer_data(bnode->buf) + bnode_offset(bnode, n)); }
+extern inline void *belem_key(const struct hfs_belem *elem)
+{ return bnode_key(elem->bnr.bn, elem->record); }
+extern inline void *brec_key(const struct hfs_brec *brec)
+{ return belem_key(brec->bottom); }
+
+/* Convert various pointers to the address of a record */
+extern inline void *bkey_record(const struct hfs_bkey *key)
+{ return (void *)key + ROUND(key->KeyLen + 1); }
+extern inline void *bnode_record(const struct hfs_bnode *bnode, int n)
+{ return bkey_record(bnode_key(bnode, n)); }
+extern inline void *belem_record(const struct hfs_belem *elem)
+{ return bkey_record(belem_key(elem)); }
+extern inline void *brec_record(const struct hfs_brec *brec)
+{ return bkey_record(brec_key(brec)); }
+
+/*================ Function Prototypes ================*/
+
+/* balloc.c */
+extern int hfs_bnode_bitop(struct hfs_btree *, hfs_u32, int);
+extern struct hfs_bnode_ref hfs_bnode_alloc(struct hfs_btree *);
+extern int hfs_bnode_free(struct hfs_bnode_ref *);
+extern void hfs_btree_extend(struct hfs_btree *);
+
+/* bins_del.c */
+extern void hfs_bnode_update_key(struct hfs_brec *, struct hfs_belem *,
+                                struct hfs_bnode *, int);
+extern void hfs_bnode_shift_right(struct hfs_bnode *, struct hfs_bnode *, int);
+extern void hfs_bnode_shift_left(struct hfs_bnode *, struct hfs_bnode *, int);
+extern int hfs_bnode_in_brec(hfs_u32 node, const struct hfs_brec *brec);
+
+/* bnode.c */
+extern void hfs_bnode_read(struct hfs_bnode *, struct hfs_btree *,
+                          hfs_u32, int);
+extern void hfs_bnode_relse(struct hfs_bnode_ref *);
+extern struct hfs_bnode_ref hfs_bnode_find(struct hfs_btree *, hfs_u32, int);
+extern void hfs_bnode_lock(struct hfs_bnode_ref *, int);
+extern void hfs_bnode_delete(struct hfs_bnode *);
+extern void hfs_bnode_commit(struct hfs_bnode *);
+
+/* brec.c */
+extern void hfs_brec_lock(struct hfs_brec *, struct hfs_belem *);
+extern struct hfs_belem *hfs_brec_init(struct hfs_brec *, struct hfs_btree *,
+                                      int);
+extern struct hfs_belem *hfs_brec_next(struct hfs_brec *);
+
+#endif
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/include/linux/hfs_fs.h linux-2.0.29/include/linux/hfs_fs.h
--- linux.vanilla/include/linux/hfs_fs.h        Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/include/linux/hfs_fs.h Tue Apr 15 19:19:24 1997
@@ -0,0 +1,355 @@
+/*
+ * linux/include/linux/hfs_fs.h
+ *
+ * Copyright (C) 1995-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * The source code distribution of the Columbia AppleTalk Package for
+ * UNIX, version 6.0, (CAP) was used as a specification of the
+ * location and format of files used by CAP's Aufs.  No code from CAP
+ * appears in hfs_fs.  hfs_fs is not a work ``derived'' from CAP in
+ * the sense of intellectual property law.
+ *
+ * The source code distributions of Netatalk, versions 1.3.3b2 and
+ * 1.4b2, were used as a specification of the location and format of
+ * files used by Netatalk's afpd.  No code from Netatalk appears in
+ * hfs_fs.  hfs_fs is not a work ``derived'' from Netatalk in the
+ * sense of intellectual property law.
+ */
+
+#ifndef _LINUX_HFS_FS_H
+#define _LINUX_HFS_FS_H
+
+#include <linux/hfs_sysdep.h>
+
+/* magic numbers for Apple Double header files */
+#define HFS_DBL_MAGIC          0x00051607
+#define HFS_SNGL_MAGIC         0x00051600
+#define HFS_HDR_VERSION_1      0x00010000
+#define HFS_HDR_VERSION_2      0x00020000
+
+/* magic numbers for various internal structures */
+#define HFS_INO_MAGIC          0x4821
+#define HFS_SB_MAGIC           0x4822
+
+/* The space used for the AppleDouble or AppleSingle headers */
+#define HFS_DBL_HDR_LEN                1024
+
+/* The space used for the Netatalk header */
+#define HFS_NAT_HDR_LEN                1024  /* 589 for an exact match */
+
+/* Macros to extract CNID and file "type" from the Linux inode number */
+#define HFS_CNID(X)    ((X) & 0x3FFFFFFF)
+#define HFS_ITYPE(X)   ((X) & 0xC0000000)
+
+/* Macros to enumerate types */
+#define HFS_ITYPE_TO_INT(X)    ((X) >> 30)
+#define HFS_INT_TO_ITYPE(X)    ((X) << 30)
+
+/* generic ITYPEs */
+#define HFS_ITYPE_0    0x00000000
+#define HFS_ITYPE_1    0x40000000
+#define HFS_ITYPE_2    0x80000000
+#define HFS_ITYPE_3    0xC0000000
+#define HFS_ITYPE_NORM HFS_ITYPE_0     /* "normal" directory or file */
+
+/* ITYPEs for CAP */
+#define HFS_CAP_NORM   HFS_ITYPE_0     /* data fork or normal directory */
+#define HFS_CAP_DATA   HFS_ITYPE_0     /* data fork of file */
+#define HFS_CAP_NDIR   HFS_ITYPE_0     /* normal directory */
+#define HFS_CAP_FNDR   HFS_ITYPE_1     /* finder info for file or dir */
+#define HFS_CAP_RSRC   HFS_ITYPE_2     /* resource fork of file */
+#define HFS_CAP_RDIR   HFS_ITYPE_2     /* .resource directory */
+#define HFS_CAP_FDIR   HFS_ITYPE_3     /* .finderinfo directory */
+
+/* ITYPEs for Apple Double */
+#define HFS_DBL_NORM   HFS_ITYPE_0     /* data fork or directory */
+#define HFS_DBL_DATA   HFS_ITYPE_0     /* data fork of file */
+#define HFS_DBL_DIR    HFS_ITYPE_0     /* directory */
+#define HFS_DBL_HDR    HFS_ITYPE_1     /* AD header of file or dir */
+
+/* ITYPEs for netatalk */
+#define HFS_NAT_NORM   HFS_ITYPE_0     /* data fork or directory */
+#define HFS_NAT_DATA   HFS_ITYPE_0     /* data fork of file */
+#define HFS_NAT_NDIR   HFS_ITYPE_0     /* normal directory */
+#define HFS_NAT_HDR    HFS_ITYPE_1     /* AD header of file or dir */
+#define HFS_NAT_HDIR   HFS_ITYPE_2     /* directory holding AD headers */
+
+/* ITYPEs for Apple Single */
+#define HFS_SGL_NORM   HFS_ITYPE_0     /* AppleSingle file or directory */
+#define HFS_SGL_SNGL   HFS_ITYPE_0     /* AppleSingle file */
+#define HFS_SGL_DIR    HFS_ITYPE_0     /* directory */
+#define HFS_SGL_DINF   HFS_ITYPE_1     /* %DirInfo for directory */
+
+/* IDs for elements of an AppleDouble or AppleSingle header */
+#define HFS_HDR_DATA   1
+#define HFS_HDR_RSRC   2
+#define HFS_HDR_FNAME  3
+#define HFS_HDR_COMNT  4
+#define HFS_HDR_BWICN  5
+#define HFS_HDR_CICON  6
+#define HFS_HDR_OLDI   7
+#define HFS_HDR_DATES  8
+#define HFS_HDR_FINFO  9
+#define HFS_HDR_MACI   10
+#define HFS_HDR_MAX    10
+
+/*
+ * There are three time systems.  All three are based on seconds since
+ * a particular time/date.
+ *     Unix:   unsigned lil-endian since 00:00 GMT, Jan. 1, 1970
+ *     mac:    unsigned big-endian since 00:00 GMT, Jan. 1, 1904
+ *     header:   SIGNED big-endian since 00:00 GMT, Jan. 1, 2000
+ *
+ */
+#define hfs_h_to_mtime(ARG)    htonl((hfs_s32)ntohl(ARG)+3029529600U)
+#define hfs_m_to_htime(ARG)    ((hfs_s32)htonl(ntohl(ARG)-3029529600U))
+#define hfs_h_to_utime(ARG)    ((hfs_s32)ntohl(ARG)+946684800U)
+#define hfs_u_to_htime(ARG)    ((hfs_s32)htonl((ARG)-946684800U))
+#define hfs_u_to_mtime(ARG)    htonl((ARG)+2082844800U)
+#define hfs_m_to_utime(ARG)    (ntohl(ARG)-2082844800U)
+
+/*======== Data structures kept in memory ========*/
+
+/*
+ * A descriptor for a single entry within the header of an
+ * AppleDouble or AppleSingle header file.
+ * An array of these make up a table of contents for the file.
+ */
+struct hfs_hdr_descr {
+       hfs_u32 id;     /* The Apple assigned ID for the entry type */
+       hfs_u32 offset; /* The offset to reach the entry */
+       hfs_u32 length; /* The length of the entry */
+};
+
+/*
+ * The info needed to reconstruct a given header layout
+ */
+struct hfs_hdr_layout {
+       hfs_u32         magic;                  /* AppleSingle or AppleDouble */
+       hfs_u32         version;                /* 0x00010000 or 0x00020000 */
+       hfs_u16         entries;                /* How many entries used */
+       struct hfs_hdr_descr
+                       descr[HFS_HDR_MAX];     /* Descriptors */
+       struct hfs_hdr_descr
+                       *order[HFS_HDR_MAX];    /* 'descr' ordered by offset */
+};
+
+/*
+ * Default header layout for Netatalk
+ */
+struct hfs_nat_hdr {
+       hfs_lword_t     magic;
+       hfs_lword_t     version;
+       hfs_byte_t      homefs[16];
+       hfs_word_t      entries;
+       hfs_byte_t      descrs[60];
+       hfs_byte_t      real_name[255]; /* id=3 */
+       hfs_byte_t      comment[200];   /* id=4 XXX: not yet implemented */
+       hfs_lword_t     create_time;    /* \       */
+       hfs_lword_t     modify_time;    /*  |      */
+       hfs_lword_t     backup_time;    /*  | id=7 */
+       hfs_word_t      filler;         /*  |      */
+       hfs_word_t      attr;           /* /       */
+       hfs_byte_t      finderinfo[32]; /* id=9 */
+};
+
+/*
+ * Default header layout for AppleDouble
+ */
+struct hfs_dbl_hdr {
+       hfs_lword_t     magic;
+       hfs_lword_t     version;
+       hfs_byte_t      filler[16];
+       hfs_word_t      entries;
+       hfs_byte_t      descrs[12*HFS_HDR_MAX];
+       hfs_u32         create_time;    /* \       */
+       hfs_u32         modify_time;    /*  | id=8 */
+       hfs_u32         backup_time;    /*  |      */
+       hfs_u32         access_time;    /* /       */
+       hfs_u8          finderinfo[32]; /* id=9 */
+       hfs_u32         fileinfo;       /* id=10 */
+       hfs_u8          real_name[32];  /* id=3 */
+       hfs_u8          comment[200];   /* id=4 XXX: not yet implemented */
+};
+
+/* finder metadata for CAP */
+struct hfs_cap_info {
+       hfs_byte_t      fi_fndr[32];    /* Finder's info */
+       hfs_word_t      fi_attr;        /* AFP attributes */
+#define HFS_AFP_WRI            0x020   /* Write inhibit bit */
+#define HFS_AFP_RNI            0x080   /* Rename inhibit bit (AFP >= 2.0) */
+#define HFS_AFP_DEI            0x100   /* Delete inhibit bit (AFP >= 2.0) */
+#define HFS_AFP_RDONLY (       HFS_AFP_WRI|HFS_AFP_RNI|HFS_AFP_DEI)
+       hfs_byte_t      fi_magic1;      /* Magic number: */
+#define HFS_CAP_MAGIC1         0xFF
+       hfs_byte_t      fi_version;     /* Version of this structure: */
+#define HFS_CAP_VERSION                0x10
+       hfs_byte_t      fi_magic;       /* Another magic number: */
+#define HFS_CAP_MAGIC          0xDA
+       hfs_byte_t      fi_bitmap;      /* Bitmap of which names are valid: */
+#define HFS_CAP_SHORTNAME      0x01
+#define HFS_CAP_LONGNAME       0x02
+       hfs_byte_t      fi_shortfilename[12+1]; /* "short name" (unused) */
+       hfs_byte_t      fi_macfilename[32+1];   /* Original (Macintosh) name */
+       hfs_byte_t      fi_comln;       /* Length of comment (always 0) */
+       hfs_byte_t      fi_comnt[200];  /* Finder comment (unused) */
+       /* optional:    used by aufs only if compiled with USE_MAC_DATES */
+       hfs_byte_t      fi_datemagic;   /* Magic number for dates extension: */
+#define HFS_CAP_DMAGIC         0xDA
+       hfs_byte_t      fi_datevalid;   /* Bitmap of which dates are valid: */
+#define HFS_CAP_MDATE          0x01
+#define HFS_CAP_CDATE          0x02
+       hfs_lword_t     fi_ctime;       /* Creation date (in AFP format) */
+       hfs_lword_t     fi_mtime;       /* Modify date (in AFP format) */
+       hfs_lword_t     fi_utime;       /* Un*x time of last mtime change */
+       hfs_byte_t      pad;
+};
+
+#ifdef __KERNEL__
+
+#include <linux/version.h>
+
+#if (LINUX_VERSION_CODE < 0x020001)
+#error "Linux kernel version 2.0.1 or newer is required."
+#endif
+
+#if (LINUX_VERSION_CODE < 0x020100) && !defined(__alpha__)
+typedef int hfs_rwret_t;
+typedef int hfs_rwarg_t;
+#else
+typedef long hfs_rwret_t;
+typedef unsigned long hfs_rwarg_t;
+#endif
+
+#if (LINUX_VERSION_CODE < 0x020104)
+#      define  copy_to_user    memcpy_tofs
+#      define  copy_from_user  memcpy_fromfs
+#endif
+
+#if (LINUX_VERSION_CODE < 0x020106)
+#      include <asm/segment.h>
+#else
+#      include <asm/uaccess.h>
+#endif
+
+#if (LINUX_VERSION_CODE < 0x020105)
+extern inline void clear_user(char *addr, off_t count)
+{ while (count--) { put_user(0, addr++); } }
+#endif
+
+/* Some forward declarations */
+struct hfs_cat_key;
+struct hfs_cat_entry;
+extern struct hfs_cat_entry *hfs_cat_get(struct hfs_mdb *,
+                                        const struct hfs_cat_key *);
+extern void hfs_tolower(unsigned char *, int);
+
+/* dir.c */
+extern hfs_rwret_t hfs_dir_read(struct inode *, struct file *, char *,
+                               hfs_rwarg_t);
+extern int hfs_create(struct inode *, const char *, int, int, struct inode **);
+extern int hfs_mkdir(struct inode *, const char *, int, int);
+extern int hfs_mknod(struct inode *, const char *, int, int, int);
+extern int hfs_unlink(struct inode *, const char *, int);
+extern int hfs_rmdir(struct inode *, const char *, int);
+extern int hfs_rename(struct inode *, const char *, int,
+                     struct inode *, const char *, int, int);
+
+/* dir_cap.c */
+extern const struct hfs_name hfs_cap_reserved1[];
+extern const struct hfs_name hfs_cap_reserved2[];
+extern struct inode_operations hfs_cap_ndir_inode_operations;
+extern struct inode_operations hfs_cap_fdir_inode_operations;
+extern struct inode_operations hfs_cap_rdir_inode_operations;
+
+/* dir_dbl.c */
+extern const struct hfs_name hfs_dbl_reserved1[];
+extern const struct hfs_name hfs_dbl_reserved2[];
+extern struct inode_operations hfs_dbl_dir_inode_operations;
+
+/* dir_nat.c */
+extern const struct hfs_name hfs_nat_reserved1[];
+extern const struct hfs_name hfs_nat_reserved2[];
+extern struct inode_operations hfs_nat_ndir_inode_operations;
+extern struct inode_operations hfs_nat_hdir_inode_operations;
+
+/* dir_sngl.c */
+extern const struct hfs_name hfs_sngl_reserved1[];
+extern const struct hfs_name hfs_sngl_reserved2[];
+extern struct inode_operations hfs_sngl_dir_inode_operations;
+
+/* file.c */
+extern hfs_s32 hfs_do_read(struct inode *, struct hfs_fork *, hfs_u32,
+                          char *, hfs_u32, int);
+extern hfs_s32 hfs_do_write(struct inode *, struct hfs_fork *, hfs_u32,
+                           const char *, hfs_u32);
+extern void hfs_file_fix_mode(struct hfs_cat_entry *entry);
+extern struct inode_operations hfs_file_inode_operations;
+
+/* file_cap.c */
+extern struct inode_operations hfs_cap_info_inode_operations;
+
+/* file_hdr.c */
+extern struct inode_operations hfs_hdr_inode_operations;
+extern const struct hfs_hdr_layout hfs_dbl_fil_hdr_layout;
+extern const struct hfs_hdr_layout hfs_dbl_dir_hdr_layout;
+extern const struct hfs_hdr_layout hfs_nat_hdr_layout;
+extern const struct hfs_hdr_layout hfs_sngl_hdr_layout;
+
+/* inode.c */
+extern void hfs_put_inode(struct inode *);
+extern int hfs_notify_change(struct inode *, struct iattr *);
+extern struct inode *__hfs_iget(struct hfs_cat_entry *, ino_t, int);
+extern inline struct inode *hfs_iget(struct hfs_mdb *mdb,
+                                    const struct hfs_cat_key *key,
+                                    ino_t type)
+{ return __hfs_iget(hfs_cat_get(mdb, key), type, 1); }
+
+extern void hfs_cap_ifill(struct inode *, ino_t);
+extern void hfs_dbl_ifill(struct inode *, ino_t);
+extern void hfs_nat_ifill(struct inode *, ino_t);
+extern void hfs_sngl_ifill(struct inode *, ino_t);
+
+/* super.c */
+extern struct super_block *hfs_read_super(struct super_block *,void *,int);
+extern int init_hfs_fs(void);
+
+/* trans.c */
+extern void hfs_colon2mac(struct hfs_name *, const char *, int);
+extern void hfs_prcnt2mac(struct hfs_name *, const char *, int);
+extern void hfs_triv2mac(struct hfs_name *, const char *, int);
+extern void hfs_latin2mac(struct hfs_name *, const char *, int);
+extern int hfs_mac2cap(char *, const struct hfs_name *);
+extern int hfs_mac2nat(char *, const struct hfs_name *);
+extern int hfs_mac2latin(char *, const struct hfs_name *);
+extern int hfs_mac2seven(char *, const struct hfs_name *);
+extern int hfs_mac2eight(char *, const struct hfs_name *);
+extern int hfs_mac2alpha(char *, const struct hfs_name *);
+extern int hfs_mac2triv(char *, const struct hfs_name *);
+
+extern __inline__ struct hfs_inode_info *HFS_I(struct inode *inode) {
+       return (struct hfs_inode_info *)(&inode->u);
+}
+
+extern __inline__ struct hfs_sb_info *HFS_SB(struct super_block *super) {
+       return (struct hfs_sb_info *)(&super->u);
+}
+
+extern __inline__ void hfs_nameout(struct inode *dir, struct hfs_name *out,
+                                  const char *in, int len) {
+       HFS_SB(dir->i_sb)->s_nameout(out, in, len);
+}
+
+extern __inline__ int hfs_namein(struct inode *dir, char *out,
+                                const struct hfs_name *in) {
+       int len = HFS_SB(dir->i_sb)->s_namein(out, in);
+       if (HFS_SB(dir->i_sb)->s_lowercase) {
+               hfs_tolower(out, len);
+       }
+       return len;
+}
+
+#endif /* __KERNEL__ */
+
+#endif
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/include/linux/hfs_fs_i.h linux-2.0.29/include/linux/hfs_fs_i.h
--- linux.vanilla/include/linux/hfs_fs_i.h      Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/include/linux/hfs_fs_i.h       Wed Apr  9 04:41:47 1997
@@ -0,0 +1,37 @@
+/*
+ * linux/include/linux/hfs_fs_i.h
+ *
+ * Copyright (C) 1995, 1996  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file defines the type (struct hfs_inode_info) and the two
+ * subordinate types hfs_extent and hfs_file.
+ */
+
+#ifndef _LINUX_HFS_FS_I_H
+#define _LINUX_HFS_FS_I_H
+
+/*
+ * struct hfs_inode_info
+ *
+ * The HFS-specific part of a Linux (struct inode)
+ */
+struct hfs_inode_info {
+       int                             magic;     /* A magic number */
+
+       struct hfs_cat_entry            *entry;
+
+       /* For a regular or header file */
+       struct hfs_fork                 *fork;
+       int                             convert;
+
+       /* For a directory */
+       ino_t                           file_type;
+       char                            dir_size;
+
+       /* For header files */
+       const struct hfs_hdr_layout     *default_layout;
+       struct hfs_hdr_layout           *layout;
+};
+
+#endif
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/include/linux/hfs_fs_sb.h linux-2.0.29/include/linux/hfs_fs_sb.h
--- linux.vanilla/include/linux/hfs_fs_sb.h     Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/include/linux/hfs_fs_sb.h      Wed Apr  9 04:41:47 1997
@@ -0,0 +1,52 @@
+/*
+ * linux/include/linux/hfs_fs_sb.h
+ *
+ * Copyright (C) 1995-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file defines the type (struct hfs_sb_info) which contains the
+ * HFS-specific information in the in-core superblock.
+ */
+
+#ifndef _LINUX_HFS_FS_SB_H
+#define _LINUX_HFS_FS_SB_H
+
+/* forward declaration: */
+struct hfs_name;
+
+typedef int (*hfs_namein_fn) (char *, const struct hfs_name *);
+typedef void (*hfs_nameout_fn) (struct hfs_name *, const char *, int);
+typedef void (*hfs_ifill_fn) (struct inode *, ino_t);
+
+/*
+ * struct hfs_sb_info
+ *
+ * The HFS-specific part of a Linux (struct super_block)
+ */
+struct hfs_sb_info {
+       int                     magic;          /* A magic number */
+       struct hfs_mdb          *s_mdb;         /* The HFS MDB */
+       int                     s_quiet;        /* Silent failure when
+                                                  changing owner or mode? */
+       int                     s_lowercase;    /* Map names to lowercase? */
+       int                     s_afpd;         /* AFPD compatible mode? */
+       hfs_namein_fn           s_namein;       /* The function used to
+                                                  map Mac filenames to
+                                                  Linux filenames */
+       hfs_nameout_fn          s_nameout;      /* The function used to
+                                                   map Linux filenames
+                                                   to Mac filenames */
+       hfs_ifill_fn            s_ifill;        /* The function used
+                                                  to fill in inode fields */
+       const struct hfs_name   *s_reserved1;   /* Reserved names */
+       const struct hfs_name   *s_reserved2;   /* Reserved names */
+       __u32                   s_type;         /* Type for new files */
+       __u32                   s_creator;      /* Creator for new files */
+       umode_t                 s_umask;        /* The umask applied to the
+                                                  permissions on all files */
+       uid_t                   s_uid;          /* The uid of all files */
+       gid_t                   s_gid;          /* The gid of all files */
+       char                    s_conv;         /* Type of text conversion */
+};
+
+#endif
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/include/linux/hfs_sysdep.h linux-2.0.29/include/linux/hfs_sysdep.h
--- linux.vanilla/include/linux/hfs_sysdep.h    Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/include/linux/hfs_sysdep.h     Thu Apr 10 14:34:06 1997
@@ -0,0 +1,224 @@
+/*
+ * linux/include/linux/hfs_sysdep.h
+ *
+ * Copyright (C) 1996-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ * This file contains constants, types and inline
+ * functions for various system dependent things.
+ *
+ * "XXX" in a comment is a note to myself to consider changing something.
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ */
+
+#ifndef _HFS_SYSDEP_H
+#define _HFS_SYSDEP_H
+
+#include <linux/malloc.h>
+#include <linux/types.h>
+#include <linux/locks.h>
+#include <linux/fs.h>
+
+#include <asm/byteorder.h>
+#include <asm/unaligned.h>
+
+
+#undef offsetof
+#define offsetof(TYPE, MEMB) ((size_t) &((TYPE *)0)->MEMB)
+
+/* Typedefs for integer types by size and signedness */
+typedef __u8            hfs_u8;
+typedef __u16           hfs_u16;
+typedef __u32           hfs_u32;
+typedef __s8            hfs_s8;
+typedef __s16           hfs_s16;
+typedef __s32           hfs_s32;
+
+/* Typedefs for unaligned integer types */
+typedef unsigned char hfs_byte_t;
+typedef unsigned char hfs_word_t[2];
+typedef unsigned char hfs_lword_t[4];
+
+/* these funny looking things are GCC variable argument macros */
+#define hfs_warn(format, args...) printk(KERN_WARNING format , ## args)
+#define hfs_error(format, args...) printk(KERN_ERR format , ## args)
+
+
+#if defined(DEBUG_ALL) || defined(DEBUG_MEM)
+extern long int hfs_alloc;
+#endif
+
+extern inline void *hfs_malloc(unsigned int size) {
+#if defined(DEBUG_ALL) || defined(DEBUG_MEM)
+       hfs_warn("%ld bytes allocation at %s:%u\n",
+                (hfs_alloc += size), __FILE__, __LINE__);
+#endif
+       return kmalloc(size, GFP_KERNEL);
+}
+
+extern inline void hfs_free(void *ptr, unsigned int size) {
+       kfree_s(ptr, size);
+#if defined(DEBUG_ALL) || defined(DEBUG_MEM)
+       hfs_warn("%ld bytes allocation at %s:%u\n",
+                 (hfs_alloc -= ptr ? size : 0), __FILE__, __LINE__);
+#endif
+}
+
+
+extern inline hfs_u32 hfs_time(void) {
+       return htonl(CURRENT_TIME+2082844800U);
+}
+
+
+/*
+ * hfs_wait_queue
+ */
+typedef struct wait_queue *hfs_wait_queue;
+
+extern inline void hfs_sleep_on(hfs_wait_queue *queue) {
+       sleep_on(queue);
+}
+
+extern inline void hfs_wake_up(hfs_wait_queue *queue) {
+       wake_up(queue);
+}
+
+extern inline void hfs_relinquish(void) {
+       schedule();
+}
+
+
+/*
+ * hfs_sysmdb
+ */
+typedef struct super_block *hfs_sysmdb;
+
+extern inline void hfs_mdb_dirty(hfs_sysmdb sys_mdb) {
+       sys_mdb->s_dirt = 1;
+}
+
+extern inline char *hfs_mdb_name(hfs_sysmdb sys_mdb) {
+       return kdevname(sys_mdb->s_dev);
+}
+
+
+/*
+ * hfs_sysentry
+ */
+typedef struct inode *hfs_sysentry[4];
+
+
+/*
+ * hfs_buffer
+ */
+typedef struct buffer_head *hfs_buffer;
+
+#define HFS_BAD_BUFFER NULL
+
+/* In sysdep.c, since it needs HFS_SECTOR_SIZE */
+extern hfs_buffer hfs_buffer_get(hfs_sysmdb, int, int);
+
+extern inline int hfs_buffer_ok(hfs_buffer buffer) {
+       return (buffer != NULL);
+}
+
+extern inline void hfs_buffer_put(hfs_buffer buffer) {
+       brelse(buffer);
+}
+
+extern inline void hfs_buffer_dirty(hfs_buffer buffer) {
+       mark_buffer_dirty(buffer, 1);
+}
+
+extern inline void hfs_buffer_sync(hfs_buffer buffer) {
+       while (buffer_locked(buffer)) {
+               wait_on_buffer(buffer);
+       }
+       if (buffer_dirty(buffer)) {
+               ll_rw_block(WRITE, 1, &buffer);
+               wait_on_buffer(buffer);
+       }
+}
+
+extern inline void *hfs_buffer_data(const hfs_buffer buffer) {
+       return buffer->b_data;
+}
+
+
+/*
+ * bit operations
+ */
+
+#undef BITNR
+#if defined(__BIG_ENDIAN)
+#      define BITNR(X) ((X)^31)
+#      if !defined(__constant_htonl)
+#              define __constant_htonl(x) (x)
+#      endif
+#      if !defined(__constant_htons)
+#              define __constant_htons(x) (x)
+#      endif
+#elif defined(__LITTLE_ENDIAN)
+#      define BITNR(X) ((X)^7)
+#      if !defined(__constant_htonl)
+#              define __constant_htonl(x) \
+        ((unsigned long int)((((unsigned long int)(x) & 0x000000ffU) << 24) | \
+                             (((unsigned long int)(x) & 0x0000ff00U) <<  8) | \
+                             (((unsigned long int)(x) & 0x00ff0000U) >>  8) | \
+                             (((unsigned long int)(x) & 0xff000000U) >> 24)))
+#      endif
+#      if !defined(__constant_htons)
+#              define __constant_htons(x) \
+        ((unsigned short int)((((unsigned short int)(x) & 0x00ff) << 8) | \
+                              (((unsigned short int)(x) & 0xff00) >> 8)))
+#      endif
+#else
+#      error "Don't know if bytes are big- or little-endian!"
+#endif
+
+extern inline int hfs_clear_bit(int bitnr, hfs_u32 *lword) {
+       return clear_bit(BITNR(bitnr), lword);
+}
+
+extern inline int hfs_set_bit(int bitnr, hfs_u32 *lword) {
+       return set_bit(BITNR(bitnr), lword);
+}
+
+extern inline int hfs_test_bit(int bitnr, const hfs_u32 *lword) {
+       return test_bit(BITNR(bitnr), lword);
+}
+
+#undef BITNR
+
+/*
+ * HFS structures have fields aligned to 16-bit boundaries.
+ * So, 16-bit get/put are easy while 32-bit get/put need
+ * some care on architectures like the DEC Alpha.
+ *
+ * In what follows:
+ *     ns  = 16-bit integer in network byte-order w/ 16-bit alignment
+ *     hs  = 16-bit integer in host byte-order w/ 16-bit alignment
+ *     nl  = 32-bit integer in network byte-order w/ unknown alignment
+ *     hl  = 32-bit integer in host byte-order w/ unknown alignment
+ *     anl = 32-bit integer in network byte-order w/ 32-bit alignment
+ *     ahl = 32-bit integer in host byte-order w/ 32-bit alignment
+ * Example: hfs_get_hl() gets an unaligned 32-bit integer converting
+ *     it to host byte-order.
+ */
+#define hfs_get_hs(addr)       ntohs(*((hfs_u16 *)(addr)))
+#define hfs_get_ns(addr)       (*((hfs_u16 *)(addr)))
+#define hfs_get_hl(addr)       ntohl(get_unaligned((hfs_u32 *)(addr)))
+#define hfs_get_nl(addr)       get_unaligned((hfs_u32 *)(addr))
+#define hfs_get_ahl(addr)      ntohl(*((hfs_u32 *)(addr)))
+#define hfs_get_anl(addr)      (*((hfs_u32 *)(addr)))
+#define hfs_put_hs(val, addr)  ((void)(*((hfs_u16 *)(addr)) = ntohs(val)))
+#define hfs_put_ns(val, addr)  ((void)(*((hfs_u16 *)(addr)) = (val)))
+#define hfs_put_hl(val, addr)  put_unaligned(htonl(val), (hfs_u32 *)(addr))
+#define hfs_put_nl(val, addr)  put_unaligned((val), (hfs_u32 *)(addr))
+#define hfs_put_ahl(val, addr)         ((void)(*((hfs_u32 *)(addr)) = ntohl(val)))
+#define hfs_put_anl(val, addr)         ((void)(*((hfs_u32 *)(addr)) = (val)))
+
+#endif
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/include/linux/nubus.h linux-2.0.29/include/linux/nubus.h
--- linux.vanilla/include/linux/nubus.h Thu Jan  1 01:00:00 1970
+++ linux-2.0.29/include/linux/nubus.h  Wed Apr 16 16:51:18 1997
@@ -0,0 +1,22 @@
+
+struct nubus_slot
+{
+       int slot_flags;
+#define NUBUS_DEVICE_PRESENT   1
+#define NUBUS_DEVICE_ACTIVE    2
+#define NUBUS_DEVICE_IRQ       4
+       __u32 slot_directory;
+       __u32 slot_dlength;
+       __u32 slot_crc;
+       __u8  slot_rev;
+       __u8  slot_format;
+       /*
+        *      Stuff we pulled from the directory
+        */
+       __u32  slot_dirbase;
+       __u32  slot_thisdir;
+       char   slot_vendor[64];
+       char   slot_cardname[64];
+};
+
+
\ No newline at end of file
diff --recursive --unified --new-file --exclude-from exclude linux.vanilla/init/main.c linux-2.0.29/init/main.c
--- linux.vanilla/init/main.c   Wed Dec  4 22:38:06 1996
+++ linux-2.0.29/init/main.c    Tue Mar 25 18:38:17 1997
@@ -39,6 +39,9 @@

#include <asm/bugs.h>

+#include <asm/setup.h> /*AC*/
+#include <linux/console.h> /*AC*/
+
/*
 * Versions of gcc older than that listed below may actually compile
 * and link okay, but the end product can have subtle run time bugs.
@@ -789,8 +792,15 @@

asmlinkage void start_kernel(void)
{
+       extern unsigned long precookie,postcookie;      /* AC*/
       char * command_line;

+       extern struct consw fb_con;
+       extern u_long fbcon_startup(u_long, char **);
+
+       if(fb_con.con_startup!=fbcon_startup)
+               mac_boom(1);
+
/*
 *     This little check will move.
 */
@@ -833,12 +843,16 @@
               memory_start += prof_len * sizeof(unsigned int);
               memset(prof_buffer, 0, prof_len * sizeof(unsigned int));
       }
+       mac_debugging_penguin(1);
       memory_start = console_init(memory_start,memory_end);
+/*     mac_debugging_penguin(2);*/
#ifdef CONFIG_PCI
       memory_start = pci_init(memory_start,memory_end);
#endif
       memory_start = kmalloc_init(memory_start,memory_end);
       sti();
+       printk("Memory start=%p, Memory end=%p\n",
+               memory_start,memory_end);
       calibrate_delay();
       memory_start = inode_init(memory_start,memory_end);
       memory_start = file_table_init(memory_start,memory_end);