home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 10 Tools
/
10-Tools.zip
/
hpfslin9.zip
/
hpfs-0.9.diff
< prev
next >
Wrap
Text File
|
1993-10-23
|
69KB
|
2,184 lines
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