diff -rc2N linux-dist/config.in linux/config.in
*** linux-dist/config.in	Fri Sep 17 14:00:40 1993
--- linux/config.in	Sat Oct 23 21:00:15 1993
***************
*** 79,82 ****
--- 79,83 ----
  bool 'NFS filesystem support' CONFIG_NFS_FS y
  bool 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS n
+ bool 'os/2 HPFS filesystem support (read only)' CONFIG_HPFS_FS n
  *
  *  character devices
diff -rc2N linux-dist/fs/Makefile linux/fs/Makefile
*** linux-dist/fs/Makefile	Fri Aug 20 08:59:30 1993
--- linux/fs/Makefile	Mon Oct 18 19:49:23 1993
***************
*** 8,12 ****
  # Note 2! The CFLAGS definitions are now in the main makefile...
  
! SUBDIRS = minix ext ext2 msdos proc isofs nfs xiafs
  
  ifdef CONFIG_MINIX_FS
--- 8,12 ----
  # Note 2! The CFLAGS definitions are now in the main makefile...
  
! SUBDIRS = minix ext ext2 msdos proc isofs nfs xiafs hpfs
  
  ifdef CONFIG_MINIX_FS
***************
*** 33,36 ****
--- 33,39 ----
  ifdef CONFIG_XIA_FS
  FS_SUBDIRS := $(FS_SUBDIRS) xiafs
+ endif
+ ifdef CONFIG_HPFS_FS
+ FS_SUBDIRS := $(FS_SUBDIRS) hpfs
  endif
  
diff -rc2N linux-dist/fs/filesystems.c linux/fs/filesystems.c
*** linux-dist/fs/filesystems.c	Sun Apr  4 07:08:51 1993
--- linux/fs/filesystems.c	Mon Oct 18 19:49:23 1993
***************
*** 33,36 ****
--- 33,39 ----
  #include <linux/iso_fs.h>
  #endif
+ #ifdef CONFIG_HPFS_FS
+ #include <linux/hpfs_fs.h>
+ #endif
  
  struct file_system_type file_systems[] = {
***************
*** 58,61 ****
--- 61,67 ----
  #ifdef CONFIG_ISO9660_FS
  	{isofs_read_super,	"iso9660",	1},
+ #endif
+ #ifdef CONFIG_HPFS_FS
+ 	{hpfs_read_super,	"hpfs",		1},
  #endif
  	{NULL,			NULL,		0}
diff -rc2N linux-dist/fs/hpfs/Makefile linux/fs/hpfs/Makefile
*** linux-dist/fs/hpfs/Makefile
--- linux/fs/hpfs/Makefile	Mon Oct 18 19:49:24 1993
***************
*** 0 ****
--- 1,33 ----
+ #
+ # Makefile for the linux HPFS filesystem routines.
+ #
+ # Note! Dependencies are done automagically by 'make dep', which also
+ # removes any old dependencies. DON'T put your own dependencies here
+ # unless it's something special (ie not a .c file).
+ #
+ # Note 2! The CFLAGS definitions are now in the main makefile...
+ 
+ .c.s:
+ 	$(CC) $(CFLAGS) -S $<
+ .c.o:
+ 	$(CC) $(CFLAGS) -c $<
+ .s.o:
+ 	$(AS) -o $*.o $<
+ 
+ OBJS=	hpfs_fs.o
+ 
+ hpfs.o: $(OBJS)
+ 	ln -f hpfs_fs.o hpfs.o
+ 
+ clean:
+ 	rm -f core *.o *.a *.s
+ 
+ dep:
+ 	$(CPP) -M *.c > .depend
+ 
+ #
+ # include a dependency file if one exists
+ #
+ ifeq (.depend,$(wildcard .depend))
+ include .depend
+ endif
diff -rc2N linux-dist/fs/hpfs/hpfs.h linux/fs/hpfs/hpfs.h
*** linux-dist/fs/hpfs/hpfs.h
--- linux/fs/hpfs/hpfs.h	Sat Oct 23 20:41:49 1993
***************
*** 0 ****
--- 1,499 ----
+ /* The paper
+ 
+      Duncan, Roy
+      Design goals and implementation of the new High Performance File System
+      Microsoft Systems Journal  Sept 1989  v4 n5 p1(13)
+ 
+    describes what HPFS looked like when it was new, and it is the source
+    of most of the information given here.  The rest is conjecture.
+ 
+    For definitive information on the Duncan paper, see it, not this file.
+    For definitive information on HPFS, ask somebody else -- this is guesswork.
+    There are certain to be many mistakes. */
+ 
+ /* Notation */
+ 
+ typedef unsigned secno;			/* sector number, partition relative */
+ 
+ typedef secno dnode_secno;		/* sector number of a dnode */
+ typedef secno fnode_secno;		/* sector number of an fnode */
+ typedef secno anode_secno;		/* sector number of an anode */
+ 
+ 
+ /* sector 0 */
+ 
+ /* The boot block is very like a FAT boot block, except that the
+    29h signature byte is 28h instead, and the ID string is "HPFS". */
+ 
+ struct hpfs_boot_block
+ {
+   unsigned char jmp[3];
+   unsigned char oem_id[8];
+   unsigned char bytes_per_sector[2];	/* 512 */
+   unsigned char sectors_per_cluster;
+   unsigned char n_reserved_sectors[2];
+   unsigned char n_fats;
+   unsigned char n_rootdir_entries[2];
+   unsigned char n_sectors_s[2];
+   unsigned char media_byte;
+   unsigned short sectors_per_fat;
+   unsigned short sectors_per_track;
+   unsigned short heads_per_cyl;
+   unsigned int n_hidden_sectors;
+   unsigned int n_sectors_l;		/* size of partition */
+   unsigned char drive_number;
+   unsigned char mbz;
+   unsigned char sig_28h;		/* 28h */
+   unsigned char vol_serno[4];
+   unsigned char vol_label[11];
+   unsigned char sig_hpfs[8];		/* "HPFS    " */
+   unsigned char pad[448];
+   unsigned short magic;			/* aa55 */
+ };
+ 
+ 
+ /* sector 16 */
+ 
+ /* The super block has the pointer to the root directory. */
+ 
+ #define SB_MAGIC 0xf995e849
+ 
+ struct hpfs_super_block
+ {
+   unsigned magic;			/* f995 e849 */
+   unsigned magic1;			/* fa53 e9c5, more magic? */
+   unsigned huh202;			/* 202 ?? version number(s)?
+ 						  sector size again?
+ 					   	  dnode multiplier?
+ 						  n_spare_dnodes? */
+   fnode_secno root;			/* fnode of root directory */
+   secno n_sectors;			/* size of filesystem */
+   unsigned n_badblocks;			/* number of bad blocks */
+   secno bitmaps;			/* pointers to free space bit maps */
+   unsigned zero1;			/* 0 */
+   secno badblocks;			/* bad block list */
+   unsigned zero3;			/* 0 */
+   time_t last_chkdsk;			/* date last checked, 0 if never */
+   unsigned zero4;			/* 0 */
+   secno n_dir_band;			/* number of sectors in dir band */
+   secno dir_band_start;			/* first sector in dir band */
+   secno dir_band_end;			/* last sector in dir band */
+   secno dir_band_bitmap;		/* free space map, 1 dnode per bit */
+   unsigned zero5[8];			/* 0 */
+   secno scratch_dnodes;			/* ?? 8 preallocated sectors near dir
+ 					   band, 4-aligned. */
+   unsigned zero6[103];			/* 0 */
+ };
+ 
+ 
+ /* sector 17 */
+ 
+ /* The spare block has pointers to spare sectors.  */
+ 
+ #define SP_MAGIC 0xf9911849
+ 
+ struct hpfs_spare_block
+ {
+   unsigned magic;			/* f991 1849 */
+   unsigned magic1;			/* fa52 29c5, more magic? */
+   unsigned dirty;			/* 0 clean, 1 "improperly stopped" */
+ 
+   secno hotfix_map;			/* info about remapped bad sectors */
+   unsigned n_spares_used;		/* number of hotfixes */
+   unsigned n_spares;			/* number of spares in hotfix map */
+   unsigned n_dnode_spares_free;		/* spare dnodes unused */
+   unsigned n_dnode_spares;		/* length of spare_dnodes[] list,
+ 					   follows in this block*/
+   secno code_page_dir;			/* code page directory block */
+   unsigned n_code_pages;		/* number of code pages */
+   unsigned large_numbers[2];		/* ?? */
+   unsigned zero1[15];
+   dnode_secno spare_dnodes[20];		/* emergency free dnode list */
+   unsigned zero2[81];			/* room for more? */
+ };
+ 
+ /* The bad block list is 4 sectors long.  The first word must be zero,
+    the remaining words give n_badblocks bad block numbers.
+    I can't resist. */
+ 
+ #define BAD_MAGIC 0
+        
+ /* The hotfix map is 4 sectors long.  It looks like
+ 
+        secno from[n_spares];
+        secno to[n_spares];
+ 
+    The to[] list is initalized to point to n_spares preallocated empty
+    sectors.  The from[] list contains the sector numbers of bad blocks
+    which have been remapped to corresponding sectors in the to[] list.
+    n_spares_used gives the length of the from[] list. */
+ 
+ 
+ /* Sectors 18 and 19 are preallocated and unused.
+    Maybe they're spares for 16 and 17, but simple substitution fails. */
+ 
+ 
+ /* The code page info pointed to by the spare block consists of an index
+    block and blocks containing character maps.  The following is pretty
+    sketchy, but Linux is Latin-1 all the way so it doesn't matter. */
+ 
+ /* block pointed to by spareblock->code_page_dir */
+ 
+ #define CP_DIR_MAGIC 0x494521f7
+ 
+ struct code_page_directory
+ {
+   unsigned magic;			/* 4945 21f7 */
+   unsigned n_code_pages;		/* number of pointers following */
+   unsigned zero1[2];
+   struct {
+     unsigned short ix;			/* index */
+     unsigned short code_page_number;	/* code page number */
+     unsigned bounds;			/* matches corresponding word
+ 					   in data block */
+     secno code_page_data;		/* sector number of a code_page_data
+ 					   containing c.p. array */
+     unsigned index;			/* index in c.p. array in that sector*/
+   } array[31];				/* unknown length */
+ };
+ 
+ /* blocks pointed to by code_page_directory */
+ 
+ #define CP_DATA_MAGIC 0x894521f7
+ 
+ struct code_page_data
+ {
+   unsigned magic;			/* 8945 21f7 */
+   unsigned n_used;			/* # elements used in c_p_data[] */
+   unsigned bounds[3];			/* looks a bit like
+ 					     (beg1,end1), (beg2,end2)
+ 					   one byte each */
+   unsigned short offs[3];		/* offsets from start of sector
+ 					   to start of c_p_data[ix] */
+   struct {
+     unsigned short ix;			/* index */
+     unsigned short code_page_number;	/* code page number */
+     unsigned short zero1;
+     unsigned char map[128];		/* map for chars 80..ff */
+     unsigned short zero2;
+   } code_page[3];
+   unsigned char incognita[78];
+ };
+ 
+ 
+ /* Free space bitmaps are 4 sectors long, which is 16384 bits.
+    16384 sectors is 8 meg, and each 8 meg band has a 4-sector bitmap.
+    Bit order in the maps is little-endian.  0 means taken, 1 means free.
+ 
+    Bit map sectors are marked allocated in the bit maps, and so are sectors 
+    off the end of the partition.
+ 
+    Band 0 is sectors 0-3fff, its map is in sectors 18-1b.
+    Band 1 is 4000-7fff, its map is in 7ffc-7fff.
+    Band 2 is 8000-ffff, its map is in 8000-8003.
+    The remaining bands have maps in their first (even) or last (odd) 4 sectors
+      -- if the last, partial, band is odd its map is in its last 4 sectors.
+ 
+    The bitmap locations are given in a table pointed to by the super block.
+    No doubt they aren't constrained to be at 18, 7ffc, 8000, ...; that is
+    just where they usually are.
+ 
+    The "directory band" is a bunch of sectors preallocated for dnodes.
+    It has a 4-sector free space bitmap of its own.  Each bit in the map
+    corresponds to one 4-sector dnode, bit 0 of the map corresponding to
+    the first 4 sectors of the directory band.  The entire band is marked
+    allocated in the main bitmap.   The super block gives the locations
+    of the directory band and its bitmap.  ("band" doesn't mean it is
+    8 meg long; it isn't.)  */
+ 
+ 
+ /* dnode: directory.  4 sectors long */
+ 
+ /* A directory is a tree of dnodes.  There is one pointer to the root of
+    the tree, in the fnode for the directory.  Every dnode in the tree
+    points up to this fnode, and the parent directory points down to it.
+    The fnode never moves, the dnodes do the B-tree thing, splitting
+    and merging as files are added and removed.  */
+ 
+ #define DNODE_MAGIC   0x77e40aae
+ 
+ struct dnode {
+   unsigned magic;			/* 77e4 0aae */
+   unsigned first_free;			/* offset from start of dnode to
+ 					   first free dir entry */
+   unsigned increment_me;		/* some kind of activity counter?
+ 					   Neither HPFS.IFS nor CHKDSK cares
+ 					   if you change this word */
+   secno up;				/* (root dnode) directory's fnode
+ 					   (nonroot) parent dnode */
+   dnode_secno self;			/* pointer to this dnode */
+   unsigned char dirent[2028];		/* one or more dirents */
+ };
+ 
+ struct hpfs_dirent {
+   unsigned short length;		/* offset to next dirent */
+   unsigned first: 1;			/* set on phony ^A^A (".") entry */
+   unsigned flag1: 1;
+   unsigned down: 1;			/* down pointer present (after name) */
+   unsigned last: 1;			/* set on phony \377 entry */
+   unsigned flag4: 1;
+   unsigned flag5: 1;
+   unsigned flag6: 1;
+   unsigned has_needea: 1;		/* ? some EA has NEEDEA set
+ 					   I have no idea why this would be
+ 					   needed in a dir entry */
+   unsigned read_only: 1;		/* dos attrib */
+   unsigned hidden: 1;			/* dos attrib */
+   unsigned system: 1;			/* dos attrib */
+   unsigned flag11: 1;			/* would be volume label dos attrib */
+   unsigned directory: 1;		/* dos attrib */
+   unsigned archive: 1;			/* dos attrib */
+   unsigned not_8x3: 1;			/* name is not 8.3 */
+   unsigned flag15: 1;
+   fnode_secno fnode;			/* fnode giving allocation info */
+   time_t write_date;			/* mtime */
+   unsigned file_size;			/* file length, bytes */
+   time_t read_date;			/* atime */
+   time_t creation_date;			/* ctime */
+   unsigned ea_size;			/* total EA length, bytes */
+   unsigned char zero1;
+   unsigned char locality;		/* ? 0=unk 1=seq 2=random 3=both */
+   unsigned char namelen, name[1];	/* file name */
+   /* dnode_secno down;	  btree down pointer, if present,
+      			  follows name on next word boundary, or maybe it's
+ 			  precedes next dirent, which is on a word boundary. */
+ };
+ 
+ /* The b-tree down pointer from a dir entry */
+ 
+ static inline dnode_secno de_down_pointer (struct hpfs_dirent *de)
+ {
+   return *(dnode_secno *) ((void *) de + de->length - 4);
+ }
+ 
+ /* The first dir entry in a dnode */
+ 
+ static inline struct hpfs_dirent *dnode_first_de (struct dnode *dnode)
+ {
+   return (void *) dnode->dirent;
+ }
+ 
+ /* The end+1 of the dir entries */
+ 
+ static inline struct hpfs_dirent *dnode_end_de (struct dnode *dnode)
+ {
+   return (void *) dnode + dnode->first_free;
+ }
+ 
+ /* The dir entry after dir entry de */
+ 
+ static inline struct hpfs_dirent *de_next_de (struct hpfs_dirent *de)
+ {
+   return (void *) de + de->length;
+ }
+ 
+ 
+ /* B+ tree: allocation info in fnodes and anodes */
+ 
+ /* dnodes point to fnodes which are responsible for listing the sectors
+    assigned to the file.  This is done with trees of (length,address)
+    pairs.  (Actually triples, of (length, file-address, disk-address)
+    which can represent holes.  Find out if HPFS does that.)
+    At any rate, fnodes contain a small tree; if subtrees are needed
+    they occupy essentially a full block in anodes.  A leaf-level tree node
+    has 3-word entries giving sector runs, a non-leaf node has 2-word
+    entries giving subtree pointers.  A flag in the header says which. */
+ 
+ struct bplus_leaf_node
+ {
+   unsigned file_secno;			/* first file sector in extent */
+   unsigned length;			/* length, sectors */
+   secno disk_secno;			/* first corresponding disk sector */
+ };
+ 
+ struct bplus_internal_node
+ {
+   unsigned file_secno;			/* subtree maps sectors < this  */
+   anode_secno down;			/* pointer to subtree */
+ };
+ 
+ struct bplus_header
+ {
+   unsigned flag0: 1;
+   unsigned flag1: 1;
+   unsigned flag2: 1;
+   unsigned flag3: 1;
+   unsigned flag4: 1;
+   unsigned fnode_parent: 1;		/* ? we're pointed to by an fnode,
+ 					   the data btree or some ea or the
+ 					   main ea bootage pointer ea_secno */
+ 					/* also can get set in fnodes, which
+ 					   may be a chkdsk bug or may mean
+ 					   this bit is irrelevant in fnodes,
+ 					   or this interpretation is all wet */
+   unsigned flag6: 1;
+   unsigned internal: 1;			/* 1 -> (internal) tree of anodes
+ 					   0 -> (leaf) list of extents */
+   unsigned char fill[3];
+   unsigned char n_free_nodes;		/* free nodes in following array */
+   unsigned char n_used_nodes;		/* used nodes in following array */
+   unsigned short first_free;		/* offset from start of header to
+ 					   first free node in array */
+   union {
+     struct bplus_internal_node internal[0]; /* (internal) 2-word entries giving
+ 					       subtree pointers */
+     struct bplus_leaf_node external[0];	    /* (external) 3-word entries giving
+ 					       sector runs */
+   } u;
+ };
+ 
+ /* fnode: root of allocation b+ tree, and EA's */
+ 
+ /* Every file and every directory has one fnode, pointed to by the directory
+    entry and pointing to the file's sectors or directory's root dnode.  EA's
+    are also stored here, and there are said to be ACL's somewhere here too. */
+ 
+ #define FNODE_MAGIC 0xf7e40aae
+ 
+ struct fnode
+ {
+   unsigned magic;			/* f7e4 0aae */
+   unsigned zero1[2];
+   unsigned char len, name[15];		/* true length, truncated name */
+   fnode_secno up;			/* pointer to file's directory fnode */
+   unsigned zero2[3];
+   unsigned ea_size_l;			/* length of disk-resident ea's */
+   secno ea_secno;			/* first sector of disk-resident ea's*/
+   unsigned short ea_size_s;		/* length of fnode-resident ea's */
+ 
+   unsigned flag0: 1;
+   unsigned ea_anode: 1;			/* 1 -> ea_secno is an anode */
+   unsigned flag2: 1;
+   unsigned flag3: 1;
+   unsigned flag4: 1;
+   unsigned flag5: 1;
+   unsigned flag6: 1;
+   unsigned flag7: 1;
+   unsigned dirflag: 1;			/* 1 -> directory.  first & only extent
+ 					   points to dnode. */
+   unsigned flag9: 1;
+   unsigned flag10: 1;
+   unsigned flag11: 1;
+   unsigned flag12: 1;
+   unsigned flag13: 1;
+   unsigned flag14: 1;
+   unsigned flag15: 1;
+ 
+   struct bplus_header btree;		/* b+ tree, 8 extents or 12 subtrees */
+   union {
+     struct bplus_leaf_node external[8];
+     struct bplus_internal_node internal[12];
+   } u;
+ 
+   unsigned file_size;			/* file length, bytes */
+   unsigned n_needea;			/* number of required EA's */
+   unsigned zero4[4];
+   unsigned ea_offs;			/* offset from start of fnode
+ 					   to first fnode-resident ea */
+   unsigned zero5[2];
+   unsigned char ea[316];		/* zero or more EA's, packed together
+ 					   with no alignment padding.
+ 					   (Do not use this field, get here
+ 					   via fnode + ea_offs. I think.) */
+ };
+ 
+ 
+ /* anode: 99.44% pure allocation tree */
+ 
+ #define ANODE_MAGIC 0x37e40aae
+ 
+ struct anode
+ {
+   unsigned magic;			/* 37e4 0aae */
+   anode_secno self;			/* pointer to this anode */
+   secno up;				/* parent anode or fnode */
+ 
+   struct bplus_header btree;		/* b+tree, 40 extents or 60 subtrees */
+   union {
+     struct bplus_leaf_node external[40];
+     struct bplus_internal_node internal[60];
+   } u;
+ 
+   unsigned fill[3];			/* unused */
+ };
+ 
+ 
+ /* extended attributes.
+ 
+    A file's EA info is stored as a list of (name,value) pairs.  It is
+    usually in the fnode, but (if it's large) it is moved to a single
+    sector run outside the fnode, or to multiple runs with an anode tree
+    that points to them.
+ 
+    The value of a single EA is stored along with the name, or (if large)
+    it is moved to a single sector run, or multiple runs pointed to by an
+    anode tree, pointed to by the value field of the (name,value) pair.
+ 
+    Flags in the EA tell whether the value is immediate, in a single sector
+    run, or in multiple runs.  Flags in the fnode tell whether the EA list
+    is immediate, in a single run, or in multiple runs. */
+ 
+ struct extended_attribute
+ {
+   unsigned indirect: 1;			/* 1 -> value gives sector number
+ 					   where real value starts */
+   unsigned anode: 1;			/* 1 -> sector is an anode
+ 					   that points to fragmented value */
+   unsigned flag2: 1;
+   unsigned flag3: 1;
+   unsigned flag4: 1;
+   unsigned flag5: 1;
+   unsigned flag6: 1;
+   unsigned needea: 1;			/* required ea */
+   unsigned char namelen;		/* length of name, bytes */
+   unsigned short valuelen;		/* length of value, bytes */
+   /*
+     unsigned char name[namelen];	ascii attrib name
+     unsigned char nul;			terminating '\0', not counted
+     unsigned char value[valuelen];	value, arbitrary
+       if this.indirect, valuelen is 8 and the value is
+         unsigned length;		real length of value, bytes
+         secno secno;			sector address where it starts
+       if this.anode, the above sector number is the root of an anode tree
+         which points to the value.
+   */
+ };
+ 
+ static inline unsigned char *ea_name (struct extended_attribute *ea)
+ {
+   return (void *) ea + sizeof *ea;
+ }
+ 
+ static inline unsigned char *ea_value (struct extended_attribute *ea)
+ {
+   return (void *) ea + sizeof *ea + ea->namelen + 1;
+ }
+ 
+ static inline struct extended_attribute *
+     ea_next_ea (struct extended_attribute *ea)
+ {
+   return (void *) ea + sizeof *ea + ea->namelen + 1 + ea->valuelen;
+ }
+ 
+ static inline unsigned ea_indirect_length (struct extended_attribute *ea)
+ {
+   unsigned *v = (void *) ea_value (ea);
+   return v[0];
+ }
+ 
+ static inline unsigned ea_indirect_secno (struct extended_attribute *ea)
+ {
+   unsigned *v = (void *) ea_value (ea);
+   return v[1];
+ }
+ 
+ /*
+    Local Variables:
+    comment-column: 40
+    End:
+ */
diff -rc2N linux-dist/fs/hpfs/hpfs_fs.c linux/fs/hpfs/hpfs_fs.c
*** linux-dist/fs/hpfs/hpfs_fs.c
--- linux/fs/hpfs/hpfs_fs.c	Sat Oct 23 20:46:59 1993
***************
*** 0 ****
--- 1,1451 ----
+ /*
+  *  linux/fs/hpfs/hhpfs_fs.c
+  *
+  *  Sources & references:
+  *   Duncan, _Design ... of HPFS_, MSSJ 4(5)   (C) 1989 Microsoft Corp
+  *   linux/fs/minix  Copyright (C) 1991, 1992, 1993  Linus Torvalds
+  *   linux/fs/msdos  Written 1992, 1993 by Werner Almesberger
+  *   linux/fs/isofs  Copyright (C) 1991  Eric Youngdale
+  */
+ 
+ /* read-only HPFS */
+ 
+ #include <linux/fs.h>
+ #include <linux/hpfs_fs.h>
+ #include <linux/errno.h>
+ #include <linux/sched.h>
+ #include <linux/locks.h>
+ #include <linux/stat.h>
+ #include <linux/string.h>
+ #include <asm/bitops.h>
+ #include <asm/segment.h>
+ 
+ #include "hpfs.h"
+ 
+ /* HPFS is a mixture of 512-byte blocks and 2048-byte blocks.  The 2k blocks
+    are used for directories (dnodes) and bitmaps.  For bmap to work, we must
+    run the file system with 512-byte blocks.  The 2k blocks are read into
+    buffers obtained from kmalloc.
+ 
+    For a file's i-number we use the sector number of its fnode, coded.
+    (Directory ino's are even, file ino's are odd, and ino >> 1 is the
+    sector address of the fnode.  This is a hack to allow lookup() to
+    tell read_inode() whether it is necessary to read the fnode.)
+ 
+    The map_xxx routines all read something into a buffer and return a
+    pointer somewhere in the buffer.  The caller must do the brelse.
+    The other routines are balanced.
+ 
+    For details on the data structures see hpfs.h and the Duncan paper.
+ 
+    Overview
+ 
+    [ The names of these data structures, except fnode, are not Microsoft's
+    or IBM's.  I don't know what names they use.  The semantics described
+    here are those of this implementation, and any coincidence between it
+    and real HPFS is to be hoped for but not guaranteed by me, and
+    certainly not guaranteed by MS or IBM.  Who know nothing about this. ]
+ 
+    [ Also, the following will make little sense if you haven't read the
+    Duncan paper, which is excellent. ]
+ 
+    HPFS is a tree.  There are 3 kinds of nodes.  A directory is a tree
+    of dnodes, and a file's allocation info is a tree of sector runs
+    stored in fnodes and anodes.
+ 
+    The top pointer is in the super block, it points to the fnode of the
+    root directory.
+    
+    The root directory -- all directories -- gives file names, dates &c,
+    and fnode addresses.  If the directory fits in one dnode, that's it,
+    otherwise the top dnode points to other dnodes, forming a tree.  A
+    dnode tree (one directory) might look like
+ 
+        ((a b c) d (e f g) h (i j) k l (m n o p))
+ 
+    The subtrees appear between the files.  Each dir entry contains, along
+    with the name and fnode, a dnode pointer to the subtree that precedes it
+    (if there is one; a flag tells that).  The first entry in every directory
+    is ^A^A, the "." entry for the directory itself.  The last entry in every
+    dnode is \377, a fake entry whose only valid fields are the bit marking
+    it last and the down pointer to the subtree preceding it, if any.
+ 
+    The "value" field of directory entries is an fnode address.  The fnode
+    tells where the sectors of the file are.  The fnode for a subdirectory
+    contains one pointer, to the root dnode of the subdirectory.  The fnode
+    for a data file contains, in effect, a tiny anode.  (Most of the space
+    in fnodes is for extended attributes.)
+ 
+    anodes and the anode part of fnodes are trees of extents.  An extent
+    is a (length, disk address) pair, labeled with the file address being
+    mapped.  E.g.,
+ 
+        (0: 3@1000  3: 1@2000  4: 2@10)
+ 
+    means the file:disk sector map (0:1000 1:1001 2:1002 3:2000 4:10 5:11).
+ 
+    There is space for 8 file:len@disk triples in an fnode, or for 40 in an
+    anode.  If this is insufficient, subtrees are used, as in
+ 
+     (6: (0: 3@1000  3: 1@2000  4: 2@10)  12: (6: 3@8000  9: 1@9000  10: 2@20))
+ 
+    The label on a subtree is the first address *after* that tree.  The
+    subtrees are always anodes.  The label:subtree pairs require only
+    two words each, so non-leaf subtrees have a different format; there
+    is room for 12 label:subtree pairs in an fnode, or 60 in an anode.
+ 
+    Within a directory, each dnode contains a pointer up to its parent
+    dnode.  The root dnode points up to the directory's fnode.
+ 
+    Each fnode contains a pointer to the directory that contains it
+    (to the fnode of the directory).  So this pointer in a directory
+    fnode is "..".
+ 
+    On the disk, dnodes are all together in the center of the partition, and
+    HPFS even manages to put all the dnodes for a single directory together,
+    generally.   fnodes are out with the data.  anodes are pretty much not
+    seen -- in fact noncontiguous files are pretty much not seen.  I think
+    this is partly the open() call that lets programs specify the length of
+    an output file when they know it, and partly because HPFS.IFS really is
+    very good at resisting fragmentation. */
+ 
+ /* notation */
+ 
+ #define little_ushort(x) (*(unsigned short *) &(x))
+ typedef void nonconst;
+ 
+ /* super block ops */
+ 
+ static void hpfs_read_inode (struct inode *);
+ static void hpfs_put_super (struct super_block *);
+ static void hpfs_statfs (struct super_block *, struct statfs *);
+ static int hpfs_remount_fs (struct super_block *, int *);
+ 
+ static const struct super_operations hpfs_sops =
+ {
+   hpfs_read_inode,			/* read_inode */
+   NULL,					/* notify_change */
+   NULL,					/* write_inode */
+   NULL,					/* put_inode */
+   hpfs_put_super,			/* put_super */
+   NULL,					/* write_super */
+   hpfs_statfs,				/* statfs */
+   hpfs_remount_fs,			/* remount_fs */
+ };
+ 
+ /* file ops */
+ 
+ static int hpfs_file_read (struct inode *, struct file *, char *, int);
+ static secno hpfs_bmap (struct inode *, unsigned);
+ 
+ static const struct file_operations hpfs_file_ops = 
+ {
+   NULL,					/* lseek - default */
+   hpfs_file_read,			/* read */
+   NULL,					/* write */
+   NULL,					/* readdir - bad */
+   NULL,					/* select - default */
+   NULL,					/* ioctl - default */
+   generic_mmap,				/* mmap */
+   NULL,					/* no special open is needed */
+   NULL,					/* release */
+   file_fsync,				/* fsync */
+ };
+ 
+ static const struct inode_operations hpfs_file_iops = 
+ {
+   (nonconst *) &hpfs_file_ops,		/* 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 */
+   (int (*)(struct inode *,int))
+   &hpfs_bmap,				/* bmap */
+   NULL,					/* truncate */
+   NULL,					/* permission */
+ };
+ 
+ /* directory ops */
+ 
+ static int hpfs_dir_read (struct inode * inode, struct file *filp,
+ 			   char *buf, int count);
+ static int hpfs_readdir (struct inode *inode, struct file *filp,
+ 			 struct dirent *dirent, int count);
+ static int hpfs_lookup (struct inode *,const char *,int,struct inode **);
+ 
+ static const struct file_operations hpfs_dir_ops = 
+ {
+   NULL,					/* lseek - default */
+   hpfs_dir_read,			/* read */
+   NULL,					/* write - bad */
+   hpfs_readdir,				/* readdir */
+   NULL,					/* select - default */
+   NULL,					/* ioctl - default */
+   NULL,					/* mmap */
+   NULL,					/* no special open code */
+   NULL,					/* no special release code */
+   file_fsync,				/* fsync */
+ };
+ 
+ static const struct inode_operations hpfs_dir_iops =
+ {
+   (nonconst *) &hpfs_dir_ops,		/* default directory file ops */
+   NULL,					/* create */
+   hpfs_lookup,				/* lookup */
+   NULL,					/* link */
+   NULL,					/* unlink */
+   NULL,					/* symlink */
+   NULL,					/* mkdir */
+   NULL,					/* rmdir */
+   NULL,					/* mknod */
+   NULL,					/* rename */
+   NULL,					/* readlink */
+   NULL,					/* follow_link */
+   NULL,					/* bmap */
+   NULL,					/* truncate */
+   NULL,					/* permission */
+ };
+ 
+ /* 4 512-byte buffers and the 2k block obtained by concatenating them */
+ 
+ struct quad_buffer_head
+ {
+   struct buffer_head *bh[4];
+   void *data;
+ };
+ 
+ /* forwards */
+ 
+ static int parse_opts (char *opts, uid_t *uid, gid_t *gid, umode_t *umask,
+ 		       int *lowercase, int *conv);
+ static int check_warn (int not_ok,
+ 		       const char *p1, const char *p2, const char *p3);
+ static int zerop (void *addr, unsigned len);
+ static void count_dnodes (struct inode *inode, dnode_secno dno,
+ 			  unsigned *n_dnodes, unsigned *n_subdirs);
+ static unsigned count_bitmap (struct super_block *s);
+ static unsigned count_one_bitmap (dev_t dev, secno secno);
+ static secno bplus_lookup (struct inode *inode, struct bplus_header *b,
+ 			   secno file_secno, struct buffer_head **bhp);
+ static struct hpfs_dirent *map_dirent (struct inode *inode, dnode_secno dno,
+ 				       const char *name, unsigned len,
+ 				       struct quad_buffer_head *qbh);
+ static struct hpfs_dirent *map_pos_dirent (struct inode *inode, off_t *posp,
+ 					   struct quad_buffer_head *qbh);
+ static void write_one_dirent (struct dirent *dirent, const unsigned char *name,
+ 			      unsigned namelen, ino_t ino, int lowercase);
+ static dnode_secno dir_subdno (struct inode *inode, unsigned pos);
+ static struct hpfs_dirent *map_nth_dirent (dev_t dev, dnode_secno dno,
+ 					   int n,
+ 					   struct quad_buffer_head *qbh);
+ static unsigned choose_conv (unsigned char *p, unsigned len);
+ static unsigned convcpy_tofs (unsigned char *out, unsigned char *in,
+ 			      unsigned len);
+ static dnode_secno fnode_dno (dev_t dev, ino_t ino);
+ static void *map_fnode (dev_t dev, ino_t ino, struct buffer_head **bhp);
+ static void *map_anode (dev_t dev, unsigned secno, struct buffer_head **bhp);
+ static void *map_dnode (dev_t dev, unsigned secno,
+ 			struct quad_buffer_head *qbh);
+ static void *map_sector (dev_t dev, unsigned secno, struct buffer_head **bhp);
+ static void *map_4sectors (dev_t dev, unsigned secno,
+ 			   struct quad_buffer_head *qbh);
+ static void brelse4 (struct quad_buffer_head *qbh);
+ 
+ /* make inode number for a file */
+ 
+ static inline ino_t file_ino (fnode_secno secno)
+ {
+   return secno << 1 | 1;
+ }
+ 
+ /* make inode number for a directory */
+ 
+ static inline ino_t dir_ino (fnode_secno secno)
+ {
+   return secno << 1;
+ }
+ 
+ /* get fnode address from an inode number */
+ 
+ static inline fnode_secno ino_secno (ino_t ino)
+ {
+   return ino >> 1;
+ }
+ 
+ /* test for directory's inode number */
+ 
+ static inline int ino_is_dir (ino_t ino)
+ {
+   return (ino & 1) == 0;
+ }
+ 
+ /* conv= options */
+ 
+ #define CONV_BINARY 0		/* no conversion */
+ #define CONV_TEXT 1		/* crlf->newline */
+ #define CONV_AUTO 2		/* decide based on file contents */
+ 
+ /* local time (HPFS) to GMT (Unix) */
+ 
+ static inline time_t local_to_gmt (time_t t)
+ {
+   extern struct timezone sys_tz;
+   return t + sys_tz.tz_minuteswest * 60;
+ }
+ 
+ /* super block ops */
+ 
+ /* mount.  This gets one thing, the root directory inode.  It does a
+    bunch of guessed-at consistency checks.  */
+ 
+ struct super_block *hpfs_read_super (struct super_block *s,
+ 				     void *options, int silent)
+ {
+   struct hpfs_boot_block *bootblock;
+   struct hpfs_super_block *superblock;
+   struct hpfs_spare_block *spareblock;
+   struct hpfs_dirent *de;
+   struct buffer_head *bh0, *bh1, *bh2;
+   struct quad_buffer_head qbh;
+   dnode_secno root_dno;
+   dev_t dev; 
+   uid_t uid;
+   gid_t gid;
+   umode_t umask;
+   int lowercase;
+   int conv;
+   int dubious;
+ 
+   /* Get the mount options */
+ 
+   if (! parse_opts (options, &uid, &gid, &umask, &lowercase, &conv)) {
+     printk ("HPFS: syntax error in mount options.  Not mounted.\n");
+     s->s_dev = 0; return 0;
+   }
+ 
+   /* Fill in the super block struct */
+ 
+   lock_super (s);
+   dev = s->s_dev;
+   set_blocksize (dev, 512);
+ 
+   /* fetch sectors 0, 16, 17 */
+ 
+   bootblock = map_sector (dev, 0, &bh0);
+   if (! bootblock) goto bail;
+ 
+   superblock = map_sector (dev, 16, &bh1);
+   if (! superblock) goto bail0;
+ 
+   spareblock = map_sector (dev, 17, &bh2);
+   if (! spareblock) goto bail1;
+ 
+   /* Check that this fs looks enough like a known one that we can find
+      and read the root directory. */
+   
+   if (bootblock->magic != 0xaa55
+       || superblock->magic != SB_MAGIC
+       || spareblock->magic != SP_MAGIC
+       || bootblock->sig_28h != 0x28
+       || memcmp (&bootblock->sig_hpfs, "HPFS    ", 8)
+       || little_ushort (bootblock->bytes_per_sector) != 512) {
+     printk ("HPFS: hpfs_read_super: Not HPFS\n");
+     goto bail2;
+   }
+ 
+   /* Check for inconsistencies -- possibly wrong guesses here, possibly
+      filesystem problems. */
+ 
+   dubious = 0;
+ 
+   dubious |= check_warn (spareblock->dirty != 0,
+ 			 "`Improperly stopped'", "flag is set", "run CHKDSK");
+   dubious |= check_warn (spareblock->n_spares_used != 0,
+ 			 "Spare blocks", "may be in use", "run CHKDSK");
+ 
+   /* Above errors mean we could get into serious trouble if we proceed,
+      so don't */
+ 
+   if (dubious) goto bail2;
+ 
+   dubious |= check_warn ((spareblock->n_dnode_spares !=
+ 			  spareblock ->n_dnode_spares_free),
+ 			 "Spare dnodes", "may be in use", "run CHKDSK");
+   dubious |= check_warn (superblock->zero1 != 0,
+ 			 "#1", "unknown word nonzero", "investigate");
+   dubious |= check_warn (superblock->zero3 != 0,
+ 			 "#3", "unknown word nonzero", "investigate");
+   dubious |= check_warn (superblock->zero4 != 0,
+ 			 "#4", "unknown word nonzero", "investigate");
+   dubious |= check_warn (! zerop (superblock->zero5, sizeof superblock->zero5),
+ 			 "#5", "unknown word nonzero", "investigate");
+   dubious |= check_warn (! zerop (superblock->zero6, sizeof superblock->zero6),
+ 			 "#6", "unknown word nonzero", "investigate");
+ 
+   if (dubious)
+     printk ("HPFS: Proceeding, but operation may be unreliable\n");
+ 
+   /* set fs read only */
+ 
+   s->s_flags |= MS_RDONLY;
+ 
+   /* fill in standard stuff */
+ 
+   s->s_magic = HPFS_SUPER_MAGIC;
+   s->s_blocksize = 512;
+   s->s_blocksize_bits = 9;
+   s->s_op = (struct super_operations *) &hpfs_sops;
+ 
+   /* fill in hpfs stuff */
+ 
+   s->s_hpfs_root = dir_ino (superblock->root);
+   s->s_hpfs_fs_size = superblock->n_sectors;
+   s->s_hpfs_dirband_size = superblock->n_dir_band / 4;
+   s->s_hpfs_dmap = superblock->dir_band_bitmap;
+   s->s_hpfs_bitmaps = superblock->bitmaps;
+   s->s_hpfs_uid = uid;
+   s->s_hpfs_gid = gid;
+   s->s_hpfs_mode = 0777 & ~umask;
+   s->s_hpfs_n_free = -1;
+   s->s_hpfs_n_free_dnodes = -1;
+   s->s_hpfs_lowercase = lowercase;
+   s->s_hpfs_conv = conv;
+ 
+   /* done with the low blocks */
+ 
+   brelse (bh2); brelse (bh1); brelse (bh0);
+ 
+   /* all set.  try it out. */
+ 
+   s->s_mounted = iget (s, s->s_hpfs_root);
+   unlock_super (s);
+ 
+   if (! s->s_mounted) {
+     printk ("HPFS: hpfs_read_super: inode get failed\n");
+     s->s_dev = 0;
+     return 0;
+   } 
+ 
+   /* find the root directory's . pointer & finish filling in the inode */
+ 
+   root_dno = fnode_dno (dev, s->s_hpfs_root);
+   if (root_dno)
+     de = map_dirent (s->s_mounted, root_dno, "\001\001", 2, &qbh);
+   if (! root_dno || ! de) {
+     printk ("HPFS: hpfs_read_super: root dir isn't in the root dir\n");
+     s->s_dev = 0;
+     return 0;
+   }
+ 
+   s->s_mounted->i_atime = local_to_gmt (de->read_date);
+   s->s_mounted->i_mtime = local_to_gmt (de->write_date);
+   s->s_mounted->i_ctime = local_to_gmt (de->creation_date);
+ 
+   brelse4 (&qbh);
+   return s;
+ 
+  bail2:
+   brelse (bh2);
+  bail1:
+   brelse (bh1);
+  bail0:
+   brelse (bh0);
+  bail:
+   s->s_dev = 0;
+   unlock_super (s);
+   return 0;
+ }
+ 
+ static int check_warn (int not_ok,
+ 		       const char *p1, const char *p2, const char *p3)
+ {
+   if (not_ok)
+     printk ("HPFS: %s %s. Please %s\n", p1, p2, p3);
+   return not_ok;
+ }
+ 
+ static int zerop (void *addr, unsigned len)
+ {
+   unsigned char *p = addr;
+   return p[0] == 0 && memcmp (p, p + 1, len - 1) == 0;
+ }
+ 
+ /* A tiny parser for option strings, stolen from dosfs. */
+ 
+ static int parse_opts (char *opts, uid_t *uid, gid_t *gid, umode_t *umask,
+ 		       int *lowercase, int *conv)
+ {
+   char *p, *rhs;
+ 
+   *uid = current->uid;
+   *gid = current->gid;
+   *umask = current->umask;
+   *lowercase = 1;
+   *conv = CONV_BINARY;
+ 
+   if (! opts) return 1;
+ 
+   for (p = strtok (opts, ","); p != 0; p = strtok (0, ",")) {
+     if ((rhs = strchr (p, '=')) != 0)
+       *rhs++ = '\0';
+     if (! strcmp (p, "uid")) {
+       if (!rhs || !*rhs) return 0;
+       *uid = simple_strtoul (rhs, &rhs, 0);
+       if (*rhs) return 0;
+     } else if (! strcmp (p, "gid")) {
+       if (!rhs || !*rhs) return 0;
+       *gid = simple_strtoul (rhs, &rhs, 0);
+       if (*rhs) return 0;
+     } else if (! strcmp (p, "umask")) {
+       if (!rhs || !*rhs) return 0;
+       *umask = simple_strtoul (rhs,&rhs,8);
+       if  (*rhs) return 0;
+     } else if (! strcmp (p, "case")) {
+       if (! strcmp (rhs, "lower")) *lowercase = 1;
+       else if (! strcmp (rhs, "asis")) *lowercase = 0;
+       else return 0;
+     } else if (! strcmp (p, "conv")) {
+       if (! strcmp (rhs, "binary")) *conv = CONV_BINARY;
+       else if (! strcmp (rhs, "text")) *conv = CONV_TEXT;
+       else if (! strcmp (rhs, "auto")) *conv = CONV_AUTO;
+       else return 0;
+     } else
+       return 0;
+   }
+ 
+   return 1;
+ }
+ 
+ /* read_inode.  This is called with exclusive access to a new inode that
+    has only (i_dev,i_ino) set.  It is responsible for filling in the rest.
+    We evilly leave the dates blank, for lookup() to fill in later.  
+    Lookup has the dir entry, all we have here is the inode number.
+ 
+    It really is evil -- lookup does not have the inode exclusively and
+    has no business writing in it -- but the alternative is to read the
+    fnode, find the dir pointer in it, read that fnode to get the dnode
+    pointer, search through that whole directory for the ino we're
+    reading, and get the dates.  It works, but ls sounds like fsck.
+ 
+    It is ok to do this unless (a) iget sleeps between calling us and
+    returning to lookup, or (b) someone other than lookup calls iget.
+    Neither one ever happens right now, 0.99.13.  (b) is not a big
+    threat since lookup is the only source of inode numbers.
+ 
+    A good way to test for breakage is two processes running du on
+    the same hpfs partition, not in the buffer cache.  They will soon
+    sync up waiting for directory i/o in lookup, and have the race
+    that needs to work. */
+ 
+ static void hpfs_read_inode (struct inode *inode)
+ {
+   struct super_block *s = inode->i_sb;
+ 
+   /* be ready to bail out */
+ 
+   inode->i_op = 0;
+   inode->i_mode = 0;
+ 
+   if (inode->i_ino == 0
+       || ino_secno (inode->i_ino) >= inode->i_sb->s_hpfs_fs_size)
+     { printk ("HPFS: read_inode: bad ino\n"); return; }
+ 
+   /* canned stuff */
+ 
+   inode->i_uid = s->s_hpfs_uid;
+   inode->i_gid = s->s_hpfs_gid;
+   inode->i_mode = s->s_hpfs_mode;
+   inode->i_hpfs_conv = s->s_hpfs_conv;
+ 
+   inode->i_hpfs_dno = 0;
+   inode->i_hpfs_n_secs = 0;
+   inode->i_hpfs_file_sec = 0;
+   inode->i_hpfs_disk_sec = 0;
+   inode->i_hpfs_dpos = 0;
+   inode->i_hpfs_dsubdno = 0;
+ 
+   /* figure out whether we are looking at a directory or a file */
+ 
+   if (ino_is_dir (inode->i_ino))
+     inode->i_mode |= S_IFDIR;
+   else {
+     inode->i_mode |= S_IFREG;
+     inode->i_mode &= ~0111;
+   }
+ 
+   /* these fields must be filled in from the dir entry, which we don't have
+      but lookup does.  It will fill them in before letting the inode
+      out of its grasp. */
+ 
+   inode->i_atime = 0;
+   inode->i_mtime = 0;
+   inode->i_ctime = 0;
+   inode->i_size = 0;
+ 
+   /* fill in the rest */
+ 
+   if (S_ISREG (inode->i_mode)) {
+ 
+     inode->i_op = (struct inode_operations *) &hpfs_file_iops;
+     inode->i_nlink = 1;
+     inode->i_blksize = 512;
+ 
+   } else {
+ 
+     unsigned n_dnodes, n_subdirs;
+     struct buffer_head *bh0;
+     struct fnode *fnode = map_fnode (inode->i_dev, inode->i_ino, &bh0);
+ 
+     if (! fnode)
+       { printk ("HPFS: read_inode: no fnode\n"); inode->i_mode = 0; return; }
+ 
+     inode->i_hpfs_parent_dir = dir_ino (fnode->up);
+     inode->i_hpfs_dno = fnode->u.external[0].disk_secno;
+ 
+     brelse (bh0);
+ 
+     n_dnodes = n_subdirs = 0;
+     count_dnodes (inode, inode->i_hpfs_dno, &n_dnodes, &n_subdirs);
+ 
+     inode->i_op = (struct inode_operations *) &hpfs_dir_iops;
+     inode->i_blksize = 512;		/* 2048 here confuses ls & du & ...*/
+     inode->i_blocks = 4 * n_dnodes;
+     inode->i_size = 512 * inode->i_blocks;
+     inode->i_nlink = 2 + n_subdirs;
+   }
+ }
+ 
+ /* unmount. */
+ 
+ static void hpfs_put_super (struct super_block *s)
+ {
+   lock_super (s);
+   s->s_dev = 0;
+   unlock_super (s);
+ }
+ 
+ /* statfs.  We report results in 512-byte sectors, not our 2k buffer size.
+    For free inode counts we report the count of dnodes in the directory band
+    -- not exactly right but pretty analagous. */
+ 
+ static void hpfs_statfs (struct super_block *s, struct statfs *buf)
+ {
+   /* count the bits in the bitmaps, unless we already have */
+   if (s->s_hpfs_n_free == -1) {
+     s->s_hpfs_n_free = count_bitmap (s);
+     s->s_hpfs_n_free_dnodes = count_one_bitmap (s->s_dev, s->s_hpfs_dmap);
+   }
+ 
+   /* fill in the user's statfs struct */
+   put_fs_long (s->s_magic, &buf->f_type);
+   put_fs_long (512, &buf->f_bsize);
+   put_fs_long (s->s_hpfs_fs_size, &buf->f_blocks);
+   put_fs_long (s->s_hpfs_n_free, &buf->f_bfree);
+   put_fs_long (s->s_hpfs_n_free, &buf->f_bavail);
+   put_fs_long (s->s_hpfs_dirband_size, &buf->f_files);
+   put_fs_long (s->s_hpfs_n_free_dnodes, &buf->f_ffree);
+   put_fs_long (254, &buf->f_namelen);
+ }
+ 
+ /* remount. */
+ 
+ static int hpfs_remount_fs (struct super_block *s, int *flags)
+ {
+   /* don't let read only be turned off */
+   if (! (*flags & MS_RDONLY))
+     return -EINVAL;
+   return 0;
+ }
+ 
+ /* count the dnodes in a directory, and the subdirs. */
+ 
+ static void count_dnodes (struct inode *inode, dnode_secno dno,
+ 			  unsigned *n_dnodes, unsigned *n_subdirs)
+ {
+   struct quad_buffer_head qbh;
+   struct dnode *dnode;
+   struct hpfs_dirent *de;
+   struct hpfs_dirent *de_end;
+ 
+   dnode = map_dnode (inode->i_dev, dno, &qbh);
+   if (! dnode) return;
+   de = dnode_first_de (dnode);
+   de_end = dnode_end_de (dnode);
+ 
+   (*n_dnodes)++;
+ 
+   for ( ; de < de_end; de = de_next_de (de)) {
+     if (de->down)
+       count_dnodes (inode, de_down_pointer (de), n_dnodes, n_subdirs);
+     if (de->directory && ! de->first)
+       (*n_subdirs)++;
+     if (de->last || de->length == 0)
+       break;
+   }
+ 
+   brelse4 (&qbh);
+ }
+ 
+ /* count the bits in the free space bit maps */
+ 
+ static unsigned count_bitmap (struct super_block *s)
+ {
+   unsigned n, count, n_bands;
+   secno *bitmaps;
+   struct quad_buffer_head qbh;
+ 
+   /* there is one bit map for each 16384 sectors */
+   n_bands = (s->s_hpfs_fs_size + 0x3fff) >> 14;
+ 
+   /* their locations are given in an array pointed to by the super block */ 
+   bitmaps = map_4sectors (s->s_dev, s->s_hpfs_bitmaps, &qbh);
+   if (! bitmaps) return 0;
+ 
+   count = 0;
+ 
+   /* map each one and count the free sectors */
+   for (n = 0; n < n_bands; n++)
+     if (bitmaps[n] == 0)
+       printk ("HPFS: bit map pointer missing\n");
+     else
+       count += count_one_bitmap (s->s_dev, bitmaps[n]);
+ 
+   brelse4 (&qbh);
+   return count;
+ }
+ 
+ /* Read in one bit map, count the bits, return the count. */
+ 
+ static unsigned count_one_bitmap (dev_t dev, secno secno)
+ {
+   struct quad_buffer_head qbh;
+   char *bits; 
+   unsigned i, count;
+ 
+   bits = map_4sectors (dev, secno, &qbh);
+   if (! bits) return 0;
+ 
+   count = 0;
+ 
+   for (i = 0; i < 8 * 2048; i++)
+     count += (test_bit (i, bits) != 0);
+   brelse4 (&qbh);
+ 
+   return count;
+ }
+ 
+ /* file ops */
+ 
+ /* read.  Read the bytes, put them in buf, return the count. */
+ 
+ static int hpfs_file_read (struct inode *inode, struct file *filp,
+ 			   char *buf, int count)
+ {
+   unsigned q, r, n, n0;
+   struct buffer_head *bh;
+   char *block;
+   char *start;
+ 
+   if (inode == 0 || ! S_ISREG (inode->i_mode))
+     return -EINVAL;
+ 
+   /* truncate count at EOF */
+   if (count > inode->i_size - filp->f_pos)
+     count = inode->i_size - filp->f_pos;
+   
+   start = buf;
+   while (count > 0) {
+ 
+     /* get file sector number, offset in sector, length to end of sector */
+     q = filp->f_pos >> 9;
+     r = filp->f_pos & 511;
+     n = 512 - r;
+ 
+     /* get length to copy to user buffer */
+     if (n > count)
+       n = count;
+ 
+     /* read the sector, copy to user */
+     block = map_sector (inode->i_dev, hpfs_bmap (inode, q), &bh);
+     if (! block) return -EIO;
+ 
+     /* but first decide if it has \r\n, if the mount option said to */
+     if (inode->i_hpfs_conv == CONV_AUTO)
+       inode->i_hpfs_conv = choose_conv (block + r, n);
+ 
+     if (inode->i_hpfs_conv == CONV_BINARY) {
+       /* regular copy, output length is same as input length */
+       memcpy_tofs (buf, block + r, n);
+       n0 = n;
+     } else {
+       /* squeeze out \r, output length varies */
+       n0 = convcpy_tofs (buf, block + r, n);
+       if (count > inode->i_size - filp->f_pos - n + n0)
+ 	count = inode->i_size - filp->f_pos - n + n0;
+     }
+ 
+     brelse (bh);
+ 
+     /* advance input n bytes, output n0 bytes */
+     filp->f_pos += n;
+     buf += n0;
+     count -= n0;
+   }
+ 
+   return buf - start;
+ }
+ 
+ /* This routine implements conv=auto.  Return CONV_BINARY or CONV_TEXT. */
+ 
+ static unsigned choose_conv (unsigned char *p, unsigned len)
+ {
+   unsigned tvote, bvote;
+   unsigned c;
+ 
+   tvote = bvote = 0;
+ 
+   while (len--) {
+     c = *p++;
+     if (c < ' ')
+       if (c == '\r' && len && *p == '\n') tvote += 10;
+       else if (c == '\t' || c == '\n') ;
+       else bvote += 5;
+     else if (c < '\177')
+       tvote++;
+     else
+       bvote += 5;
+   }
+ 
+   if (tvote > bvote)
+     return CONV_TEXT;
+   else
+     return CONV_BINARY;
+ }
+ 
+ /* This routine implements conv=text.  :s/crlf/nl/  */
+ 
+ static unsigned convcpy_tofs (unsigned char *out, unsigned char *in,
+ 			      unsigned len)
+ {
+   unsigned char *start = out;
+ 
+   while (len--) {
+     unsigned c = *in++;
+     if (c == '\r' && (len == 0 || *in == '\n'))
+       ;
+     else
+       put_fs_byte (c, out++);
+   }
+ 
+   return out - start;
+ }
+ 
+ /* Return the disk sector number containing a file sector. */
+ 
+ static secno hpfs_bmap (struct inode *inode, unsigned file_secno)
+ {
+   unsigned n, disk_secno;
+   struct fnode *fnode;
+   struct buffer_head *bh;
+ 
+   /* There is one sector run cached in the inode.
+      See if the sector is in it. */
+ 
+   n = file_secno - inode->i_hpfs_file_sec;
+   if (n < inode->i_hpfs_n_secs)
+     return inode->i_hpfs_disk_sec + n;
+ 
+   /* No, read the fnode and go find the sector. */
+ 
+   else {
+     fnode = map_fnode (inode->i_dev, inode->i_ino, &bh);
+     if (! fnode) return 0;
+     disk_secno = bplus_lookup (inode, &fnode->btree, file_secno, &bh);
+     brelse (bh);
+     return disk_secno;
+   }
+ }
+ 
+ /* Search allocation tree *b for the given file sector number and return
+    the disk sector number.  Buffer *bhp has the tree in it, and can be
+    reused for subtrees when access to *b is no longer needed.
+    *bhp is busy on entry and exit. */
+ 
+ static secno bplus_lookup (struct inode *inode, struct bplus_header *b,
+ 			   secno file_secno, struct buffer_head **bhp)
+ {
+   int i;
+ 
+   /* A leaf-level tree gives a list of sector runs.  Find the one
+      containing the file sector we want, cache the map info in the inode
+      for later, and return the corresponding disk sector. */
+ 
+   if (! b->internal) {
+     struct bplus_leaf_node *n = b->u.external;
+     for (i = 0; i < b->n_used_nodes; i++) {
+       unsigned t = file_secno - n[i].file_secno;
+       if (t < n[i].length) {
+ 	inode->i_hpfs_file_sec = n[i].file_secno;
+ 	inode->i_hpfs_disk_sec = n[i].disk_secno;
+ 	inode->i_hpfs_n_secs = n[i].length;
+ 	return n[i].disk_secno + t;
+       }
+     }
+   }
+ 
+   /* A non-leaf tree gives a list of subtrees.  Find the one containing
+      the file sector we want, read it in, and recurse to search it. */
+ 
+   else {
+     struct bplus_internal_node *n = b->u.internal;
+     for (i = 0; i < b->n_used_nodes; i++) {
+       if (file_secno < n[i].file_secno) {
+ 	struct anode *anode;
+ 	anode_secno ano = n[i].down;
+ 	brelse (*bhp);
+ 	anode = map_anode (inode->i_dev, ano, bhp);
+ 	if (! anode) break;
+ 	return bplus_lookup (inode, &anode->btree, file_secno, bhp);
+       }
+     }
+   }
+ 
+   /* If we get here there was a hole in the file.  As far as I know
+      we never do get here, but falling off the end would be indelicate.
+      So return a pointer to a handy all-zero sector.  This is not a
+      reasonable way to handle files with holes if they really do happen. */
+ 
+   printk ("HPFS: bplus_lookup: sector not found\n");
+   return 15;
+ }
+ 
+ /* directory ops */
+ 
+ /* lookup.  Search the directory tree for the specified name, set
+    *result to the corresponding inode.
+ 
+    lookup uses the inode number to tell read_inode whether it is reading
+    the inode of a directory or a file -- file ino's are odd, directory
+    ino's are even.  read_inode avoids i/o for file inodes; everything
+    needed is up here in the directory.  And file fnodes are out in
+    the boondocks. */
+ 
+ static int hpfs_lookup (struct inode *dir, const char *name, int len,
+ 			struct inode **result)
+ {
+   struct quad_buffer_head qbh;
+   struct hpfs_dirent *de;
+   struct inode *inode;
+   ino_t ino;
+ 
+   /* In case of madness */
+ 
+   *result = 0;
+   if (dir == 0)
+     return -ENOENT;
+   if (! S_ISDIR (dir->i_mode))
+     goto bail;
+   
+   /* Read in the directory entry.
+      "." is there under the name ^A^A .
+      Always read the dir even for . and .. in case we need the dates. */
+ 
+   if (name[0] == '.' && len == 1)
+     de = map_dirent (dir, dir->i_hpfs_dno, "\001\001", 2, &qbh);
+   else if (name[0] == '.' && name[1] == '.' && len == 2)
+     de = map_dirent (dir, fnode_dno (dir->i_dev, dir->i_hpfs_parent_dir),
+ 		     "\001\001", 2, &qbh);
+   else
+     de = map_dirent (dir, dir->i_hpfs_dno, name, len, &qbh);
+ 
+   /* This is not really a bailout, just means file not found. */
+ 
+   if (! de) goto bail;
+ 
+   /* Get inode number, what we're after. */
+ 
+   if (de->directory)
+     ino = dir_ino (de->fnode);
+   else
+     ino = file_ino (de->fnode);
+ 
+   /* Go find or make an inode. */
+ 
+   if (! (inode = iget (dir->i_sb, ino)))
+     goto bail1;
+ 
+   /* Stuff in the info from the directory if this is a newly created inode. */
+ 
+   if (! inode->i_atime) {
+     inode->i_atime = local_to_gmt (de->read_date);
+     inode->i_mtime = local_to_gmt (de->write_date);
+     inode->i_ctime = local_to_gmt (de->creation_date);
+     if (de->read_only)
+       inode->i_mode &= ~0222;
+     if (! de->directory) {
+       inode->i_size = de->file_size;
+       /* i_blocks should count the fnode and any anodes.  We count 1 for
+ 	 the fnode and don't bother about anodes -- the disk heads are
+ 	 on the directory band and we want them to stay there.  If files
+ 	 can have holes, this may have to go look at the fnode. */
+       inode->i_blocks = 1 + ((inode->i_size + 511) >> 9);
+     }
+   }
+ 
+   brelse4 (&qbh);
+ 
+   /* Made it. */
+ 
+   *result = inode;
+   iput (dir);
+   return 0;
+ 
+   /* Didn't. */
+  bail1:
+   brelse4 (&qbh);
+  bail:
+   iput (dir);
+   return -ENOENT;
+ }
+ 
+ /* Compare two counted strings ignoring case.
+    HPFS directory order sorts letters as if they're upper case. */
+ 
+ static inline int memcasecmp (unsigned char *s1, unsigned char *s2, unsigned n)
+ {
+   int t;
+ 
+   if (n != 0)
+     do {
+       unsigned c1 = *s1++;
+       unsigned c2 = *s2++;
+       if (c1 - 'a' < 26) c1 -= 040;
+       if (c2 - 'a' < 26) c2 -= 040;
+       if ((t = c1 - c2) != 0)
+ 	return t;
+     } while (--n != 0);
+ 
+   return 0;
+ }
+ 
+ /* Search a directory for the given name, return a pointer to its dir entry
+    and a pointer to the buffer containing it. */
+ 
+ static struct hpfs_dirent *map_dirent (struct inode *inode, dnode_secno dno,
+ 				       const char *name, unsigned len,
+ 				       struct quad_buffer_head *qbh)
+ {
+   struct dnode *dnode;
+   struct hpfs_dirent *de;
+   struct hpfs_dirent *de_end;
+   int t, l;
+ 
+   /* read the dnode at the root of our subtree */
+   dnode = map_dnode (inode->i_dev, dno, qbh);
+   if (! dnode) return 0;
+ 
+   /* get pointers to start and end+1 of dir entries */
+   de = dnode_first_de (dnode);
+   de_end = dnode_end_de (dnode);
+ 
+   /* look through the entries for the name we're after */
+   for ( ; de < de_end; de = de_next_de (de)) {
+ 
+     /* compare names */
+     l = len < de->namelen ? len : de->namelen;
+     t = memcasecmp (name, de->name, l);
+ 
+     /* initial substring matches, compare lengths */
+     if (t == 0) {
+       t = len - de->namelen;
+       /* bingo */
+       if (t == 0)
+ 	return de;
+     }
+       
+     /* wanted name .lt. dir name => not present. */
+     if (t < 0) {
+       /* if there is a subtree, search it. */
+       if (de->down) {
+ 	dnode_secno sub_dno = de_down_pointer (de);
+ 	brelse4 (qbh);
+ 	return map_dirent (inode, sub_dno, name, len, qbh);
+       } else
+ 	break;
+     }
+ 
+     /* de->last is set on the last name in the dnode (it's always a "\377"
+        pseudo entry).  de->length == 0 means we're about to infinite loop.
+        This test does nothing in a well-formed dnode. */
+     if (de->last || de->length == 0)
+       break;
+   }
+ 
+   /* name not found. */
+ 
+   return 0;
+ }
+ 
+ /* readdir.  Return exactly 1 dirent.  (I tried and tried, but currently
+    the interface with libc just does not permit more than 1.  If it gets
+    fixed, throw this out and just walk the tree and write records into
+    the user buffer.)
+ 
+    We keep track of our position in the dnode tree with a sort of
+    dewey-decimal record of subtree locations.  Like so:
+ 
+    (1 (1.1 1.2 1.3) 2 3 (3.1 (3.1.1 3.1.2) 3.2 3.3 (3.3.1)) 4)
+ 
+    Subtrees appear after their file, out of lexical order,
+    which would be before their file.  It's easier.
+ 
+    A directory can't hold more than 56 files, so 6 bits are used for
+    position numbers.  If the tree is so deep that the position encoding
+    doesn't fit, I'm sure something absolutely fascinating happens.
+ 
+    The actual sequence of f_pos values is
+        0 => .   -1 => ..   1 1.1 ... 8.9 9 => files  -2 => eof
+ 
+    The directory inode caches one position-to-dnode correspondence so
+    we won't have to repeatedly scan the top levels of the tree. */
+ 
+ static int hpfs_readdir (struct inode *inode, struct file *filp,
+ 			 struct dirent *dirent, int likely_story)
+ {
+   struct quad_buffer_head qbh;
+   struct hpfs_dirent *de;
+   int namelen, lc;
+   ino_t ino;
+ 
+   if (inode == 0
+       || inode->i_sb == 0
+       || ! S_ISDIR (inode->i_mode))
+     return -EBADF;
+ 
+   lc = inode->i_sb->s_hpfs_lowercase;
+ 
+   switch (filp->f_pos) {
+   case 0:
+     write_one_dirent (dirent, ".", 1, inode->i_ino, lc);
+     filp->f_pos = -1;
+     return 1;
+ 
+   case -1:
+     write_one_dirent (dirent, "..", 2, inode->i_hpfs_parent_dir, lc);
+     filp->f_pos = 1;
+     return 2;
+     
+   case -2:
+     return 0;
+ 
+   default:
+ 
+     de = map_pos_dirent (inode, &filp->f_pos, &qbh);
+     if (! de) {
+       filp->f_pos = -2;
+       return 0;
+     }
+ 
+     namelen = de->namelen;
+     if (de->directory)
+       ino = dir_ino (de->fnode);
+     else
+       ino = file_ino (de->fnode);
+     write_one_dirent (dirent, de->name, namelen, ino, lc);
+     brelse4 (&qbh);
+ 
+     return namelen;
+   }
+ } 
+ 
+ /* Send the given name and ino off to the user dirent struct at *dirent.
+    Blam it to lowercase if the mount option said to.
+ 
+    Note that Linux d_reclen is the length of the file name, and has nothing
+    to do with the length of the dirent record.  Why is that, do you ask?
+    I sure do.  Jeez. */
+ 
+ static void write_one_dirent (struct dirent *dirent, const unsigned char *name,
+ 			      unsigned namelen, ino_t ino, int lowercase)
+ {
+   unsigned n;
+ 
+   put_fs_long (ino, &dirent->d_ino);
+   put_fs_word (namelen, &dirent->d_reclen);
+ 
+   if (lowercase)
+     for (n = namelen; n != 0; ) {
+       unsigned t = name[--n];
+       if (t - 'A' < 26) t += 040;
+       put_fs_byte (t, &dirent->d_name[n]);
+     }
+   else
+     memcpy_tofs (dirent->d_name, name, namelen);
+ 
+   put_fs_byte (0, &dirent->d_name[namelen]);
+ }
+ 
+ /* Map the dir entry at subtree coordinates given by *posp, and
+    increment *posp to point to the following dir entry. */
+ 
+ static struct hpfs_dirent *map_pos_dirent (struct inode *inode, off_t *posp,
+ 					   struct quad_buffer_head *qbh)
+ {
+   unsigned pos, q, r;
+   dnode_secno dno;
+   struct hpfs_dirent *de;
+ 
+   /* Get the position code and split off the rightmost index r */
+ 
+   pos = *posp;
+   q = pos >> 6;
+   r = pos & 077;
+ 
+   /* Get the sector address of the dnode pointed to by the leading part q */
+ 
+   dno = dir_subdno (inode, q);
+   if (! dno) return 0;
+ 
+   /* Get the entry at index r in dnode q */
+ 
+   de = map_nth_dirent (inode->i_dev, dno, r, qbh);
+ 
+   /* If none, we're out of files in this dnode.  Ascend. */
+ 
+   if (! de) {
+     if (q == 0) return 0;
+     *posp = q + 1;
+     return map_pos_dirent (inode, posp, qbh);
+   }
+ 
+   /* If a subtree is here, descend. */
+ 
+   if (de->down)
+     *posp = pos << 6 | 1;
+   else
+     *posp = pos + 1;
+ 
+   /* Don't return the ^A^A and \377 entries. */
+ 
+   if (de->first || de->last) {
+     brelse4 (qbh);
+     return map_pos_dirent (inode, posp, qbh);
+   } else
+     return de;
+ }
+ 
+ /* Return the address of the dnode with subtree coordinates given by pos. */
+ 
+ static dnode_secno dir_subdno (struct inode *inode, unsigned pos)
+ {
+   struct hpfs_dirent *de;
+   struct quad_buffer_head qbh;
+ 
+   /* 0 is the root dnode */
+ 
+   if (pos == 0)
+     return inode->i_hpfs_dno;
+ 
+   /* we have one pos->dnode translation cached in the inode */
+ 
+   else if (pos == inode->i_hpfs_dpos)
+     return inode->i_hpfs_dsubdno;
+ 
+   /* otherwise go look */
+ 
+   else {
+     unsigned q = pos >> 6;
+     unsigned r = pos & 077;
+     dnode_secno dno;
+ 
+     /* dnode at position q */
+     dno = dir_subdno (inode, q);
+     if (dno == 0) return 0;
+ 
+     /* entry at index r */
+     de = map_nth_dirent (inode->i_dev, dno, r, &qbh);
+     if (! de || ! de->down) return 0;
+ 
+     /* get the dnode down pointer */
+     dno = de_down_pointer (de);
+     brelse4 (&qbh);
+ 
+     /* cache it for next time */
+     inode->i_hpfs_dpos = pos;
+     inode->i_hpfs_dsubdno = dno;
+     return dno;
+   }
+ }
+ 
+ /* Return the dir entry at index n in dnode dno, or 0 if there isn't one */
+ 
+ static struct hpfs_dirent *map_nth_dirent (dev_t dev, dnode_secno dno,
+ 					   int n,
+ 					   struct quad_buffer_head *qbh)
+ {
+   int i;
+   struct hpfs_dirent *de, *de_end;
+   struct dnode *dnode = map_dnode (dev, dno, qbh);
+ 
+   de = dnode_first_de (dnode);
+   de_end = dnode_end_de (dnode);
+ 
+   for (i = 1; de < de_end; i++, de = de_next_de (de)) {
+     if (i == n)
+       return de;
+     if (de->last || de->length == 0)
+       break;
+   }
+ 
+   brelse4 (qbh);
+   return 0;
+ }
+ 
+ static int hpfs_dir_read (struct inode *inode, struct file *filp,
+ 			  char *buf, int count)
+ {
+   return -EISDIR;
+ }
+ 
+ /* Return the dnode pointer in a directory fnode */
+ 
+ static dnode_secno fnode_dno (dev_t dev, ino_t ino)
+ {
+   struct buffer_head *bh;
+   struct fnode *fnode;
+   dnode_secno dno; 
+ 
+   fnode = map_fnode (dev, ino, &bh);
+   if (! fnode) return 0;
+ 
+   dno = fnode->u.external[0].disk_secno;
+   brelse (bh);
+   return dno;
+ }
+ 
+ /* Map an fnode into a buffer and return pointers to it and to the buffer. */
+ 
+ static void *map_fnode (dev_t dev, ino_t ino, struct buffer_head **bhp)
+ {
+   struct fnode *fnode;
+ 
+   if (ino == 0)
+     { printk ("HPFS: missing fnode\n"); return 0; }
+ 
+   fnode = map_sector (dev, ino_secno (ino), bhp);
+   if (fnode)
+     if (fnode->magic != FNODE_MAGIC)
+       { printk ("HPFS: map_fnode: bad fnode pointer\n");
+ 	brelse (*bhp); return 0; }
+   return fnode;
+ }
+ 
+ /* Map an anode into a buffer and return pointers to it and to the buffer. */
+ 
+ static void *map_anode (dev_t dev, unsigned secno, struct buffer_head **bhp)
+ {
+   struct anode *anode;
+ 
+   if (secno == 0)
+     { printk ("HPFS: missing anode\n"); return 0; }
+ 
+   anode = map_sector (dev, secno, bhp);
+   if (anode)
+     if (anode->magic != ANODE_MAGIC || anode->self != secno)
+       { printk ("HPFS: map_anode: bad anode pointer\n");
+ 	brelse (*bhp); return 0; }
+   return anode;
+ }
+ 
+ /* Map a dnode into a buffer and return pointers to it and to the buffer. */
+ 
+ static void *map_dnode (dev_t dev, unsigned secno,
+ 			struct quad_buffer_head *qbh)
+ {
+   struct dnode *dnode; 
+ 
+   if (secno == 0)
+     { printk ("HPFS: missing dnode\n"); return 0; }
+ 
+   dnode = map_4sectors (dev, secno, qbh);
+   if (dnode)
+     if (dnode->magic != DNODE_MAGIC || dnode->self != secno)
+       { printk ("HPFS: map_dnode: bad dnode pointer\n");
+ 	brelse4 (qbh); return 0; }
+   return dnode;
+ }
+ 
+ /* Map a sector into a buffer and return pointers to it and to the buffer. */
+ 
+ static void *map_sector (dev_t dev, unsigned secno, struct buffer_head **bhp)
+ {
+   struct buffer_head *bh;
+ 
+   if ((*bhp = bh = bread (dev, secno, 512)) != 0)
+     return bh->b_data;
+   else
+     { printk ("HPFS: map_sector: read error\n"); return 0; }
+ }
+ 
+ /* Map 4 sectors into a 4buffer and return pointers to it and to the buffer. */
+ 
+ static void *map_4sectors (dev_t dev, unsigned secno,
+ 			   struct quad_buffer_head *qbh)
+ {
+   struct buffer_head *bh;
+   char *data;
+ 
+   if (secno & 3)
+     { printk ("HPFS: map_4sectors: unaligned read\n"); return 0; }
+ 
+   qbh->data = data = kmalloc (2048, GFP_KERNEL);
+   if (! data) goto bail;
+ 
+   qbh->bh[0] = bh = breada (dev, secno, secno + 1, secno + 2, secno + 3, -1);
+   if (! bh) goto bail0;
+   memcpy (data, bh->b_data, 512);
+ 
+   qbh->bh[1] = bh = bread (dev, secno + 1, 512);
+   if (! bh) goto bail1;
+   memcpy (data + 512, bh->b_data, 512);
+ 
+   qbh->bh[2] = bh = bread (dev, secno + 2, 512);
+   if (! bh) goto bail2;
+   memcpy (data + 2 * 512, bh->b_data, 512);
+ 
+   qbh->bh[3] = bh = bread (dev, secno + 3, 512);
+   if (! bh) goto bail3;
+   memcpy (data + 3 * 512, bh->b_data, 512);
+   
+   return data;
+ 
+  bail3:
+   brelse (qbh->bh[2]);
+  bail2:
+   brelse (qbh->bh[1]);
+  bail1:
+   brelse (qbh->bh[0]);
+  bail0:
+   kfree_s (data, 2048);
+  bail:
+   printk ("HPFS: map_4sectors: read error\n");
+   return 0;
+ }
+ 
+ /* Deallocate a 4-buffer block */
+ 
+ static void brelse4 (struct quad_buffer_head *qbh)
+ {
+   brelse (qbh->bh[3]);
+   brelse (qbh->bh[2]);
+   brelse (qbh->bh[1]);
+   brelse (qbh->bh[0]);
+   kfree_s (qbh->data, 2048);
+ }
diff -rc2N linux-dist/include/linux/fs.h linux/include/linux/fs.h
*** linux-dist/include/linux/fs.h	Sun Sep 19 13:53:23 1993
--- linux/include/linux/fs.h	Mon Oct 18 19:49:24 1993
***************
*** 169,172 ****
--- 169,173 ----
  #include <linux/ext_fs_i.h>
  #include <linux/ext2_fs_i.h>
+ #include <linux/hpfs_fs_i.h>
  #include <linux/msdos_fs_i.h>
  #include <linux/iso_fs_i.h>
***************
*** 209,212 ****
--- 210,214 ----
  		struct ext_inode_info ext_i;
  		struct ext2_inode_info ext2_i;
+ 		struct hpfs_inode_info hpfs_i;
  		struct msdos_inode_info msdos_i;
  		struct iso_inode_info isofs_i;
***************
*** 242,245 ****
--- 244,248 ----
  #include <linux/ext_fs_sb.h>
  #include <linux/ext2_fs_sb.h>
+ #include <linux/hpfs_fs_sb.h>
  #include <linux/msdos_fs_sb.h>
  #include <linux/iso_fs_sb.h>
***************
*** 265,268 ****
--- 268,272 ----
  		struct ext_sb_info ext_sb;
  		struct ext2_sb_info ext2_sb;
+ 		struct hpfs_sb_info hpfs_sb;
  		struct msdos_sb_info msdos_sb;
  		struct isofs_sb_info isofs_sb;
diff -rc2N linux-dist/include/linux/hpfs_fs.h linux/include/linux/hpfs_fs.h
*** linux-dist/include/linux/hpfs_fs.h
--- linux/include/linux/hpfs_fs.h	Sat Oct 23 20:58:34 1993
***************
*** 0 ****
--- 1,12 ----
+ #ifndef _LINUX_HPFS_FS_H
+ #define _LINUX_HPFS_FS_H
+ 
+ /* HPFS magic number (word 0 of block 16) */
+ 
+ #define HPFS_SUPER_MAGIC 0xf995e849
+ 
+ /* The entry point for a VFS */
+ 
+ extern struct super_block *hpfs_read_super (struct super_block *, void *, int);
+ 
+ #endif
diff -rc2N linux-dist/include/linux/hpfs_fs_i.h linux/include/linux/hpfs_fs_i.h
*** linux-dist/include/linux/hpfs_fs_i.h
--- linux/include/linux/hpfs_fs_i.h	Mon Oct 18 19:49:24 1993
***************
*** 0 ****
--- 1,24 ----
+ #ifndef _HPFS_FS_I
+ #define _HPFS_FS_I
+ 
+ struct hpfs_inode_info {
+   ino_t i_parent_dir;		/* (directories) gives fnode of parent dir */
+   unsigned i_dno;		/* (directories) root dnode */
+   unsigned i_dpos;		/* (directories) temp for readdir */
+   unsigned i_dsubdno;		/* (directories) temp for readdir */
+   unsigned i_file_sec;		/* (files) minimalist cache of alloc info */
+   unsigned i_disk_sec;		/* (files) minimalist cache of alloc info */
+   unsigned i_n_secs;		/* (files) minimalist cache of alloc info */
+   unsigned i_conv : 2;		/* (files) crlf->newline hackery */
+ };
+ 
+ #define i_hpfs_dno u.hpfs_i.i_dno
+ #define i_hpfs_parent_dir u.hpfs_i.i_parent_dir
+ #define i_hpfs_n_secs u.hpfs_i.i_n_secs
+ #define i_hpfs_file_sec u.hpfs_i.i_file_sec
+ #define i_hpfs_disk_sec u.hpfs_i.i_disk_sec
+ #define i_hpfs_dpos u.hpfs_i.i_dpos
+ #define i_hpfs_dsubdno u.hpfs_i.i_dsubdno
+ #define i_hpfs_conv u.hpfs_i.i_conv
+ 
+ #endif
diff -rc2N linux-dist/include/linux/hpfs_fs_sb.h linux/include/linux/hpfs_fs_sb.h
*** linux-dist/include/linux/hpfs_fs_sb.h
--- linux/include/linux/hpfs_fs_sb.h	Mon Oct 18 19:49:24 1993
***************
*** 0 ****
--- 1,33 ----
+ #ifndef _HPFS_FS_SB
+ #define _HPFS_FS_SB
+ 
+ struct hpfs_sb_info
+ {
+   ino_t sb_root;		/* inode number of root dir */
+   unsigned sb_fs_size;		/* file system size, sectors */
+   unsigned sb_bitmaps;		/* sector number of bitmap list */
+   unsigned sb_dirband_size;	/* directory band size, dnodes */
+   unsigned sb_dmap;		/* sector number of dnode bit map */
+   unsigned sb_n_free;		/* free blocks for statfs, or -1 */
+   unsigned sb_n_free_dnodes;	/* free dnodes for statfs, or -1 */
+   uid_t sb_uid;			/* uid from mount options */
+   gid_t sb_gid;			/* gid from mount options */
+   umode_t sb_mode;		/* mode from mount options */
+   unsigned sb_lowercase : 1;	/* downcase filenames hackery */
+   unsigned sb_conv : 2;		/* crlf->newline hackery */
+ };
+ 
+ #define s_hpfs_root u.hpfs_sb.sb_root
+ #define s_hpfs_fs_size u.hpfs_sb.sb_fs_size
+ #define s_hpfs_bitmaps u.hpfs_sb.sb_bitmaps
+ #define s_hpfs_dirband_size u.hpfs_sb.sb_dirband_size
+ #define s_hpfs_dmap u.hpfs_sb.sb_dmap
+ #define s_hpfs_uid u.hpfs_sb.sb_uid
+ #define s_hpfs_gid u.hpfs_sb.sb_gid
+ #define s_hpfs_mode u.hpfs_sb.sb_mode
+ #define s_hpfs_n_free u.hpfs_sb.sb_n_free
+ #define s_hpfs_n_free_dnodes u.hpfs_sb.sb_n_free_dnodes
+ #define s_hpfs_lowercase u.hpfs_sb.sb_lowercase
+ #define s_hpfs_conv u.hpfs_sb.sb_conv
+ 
+ #endif
