summaryrefslogtreecommitdiffstats
path: root/fs/hpfs
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1994-11-28 11:59:19 +0000
committer <ralf@linux-mips.org>1994-11-28 11:59:19 +0000
commit1513ff9b7899ab588401c89db0e99903dbf5f886 (patch)
treef69cc81a940a502ea23d664c3ffb2d215a479667 /fs/hpfs
Import of Linus's Linux 1.1.68
Diffstat (limited to 'fs/hpfs')
-rw-r--r--fs/hpfs/Makefile30
-rw-r--r--fs/hpfs/README25
-rw-r--r--fs/hpfs/hpfs.h498
-rw-r--r--fs/hpfs/hpfs_fs.c1727
4 files changed, 2280 insertions, 0 deletions
diff --git a/fs/hpfs/Makefile b/fs/hpfs/Makefile
new file mode 100644
index 000000000..94ab74d5d
--- /dev/null
+++ b/fs/hpfs/Makefile
@@ -0,0 +1,30 @@
+#
+# 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
+
+dep:
+ $(CPP) -M *.c > .depend
+
+#
+# include a dependency file if one exists
+#
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
diff --git a/fs/hpfs/README b/fs/hpfs/README
new file mode 100644
index 000000000..7e4fe88ca
--- /dev/null
+++ b/fs/hpfs/README
@@ -0,0 +1,25 @@
+Linux can read, but not write, OS/2 HPFS partitions.
+
+Mount options are the same as for msdos partitions.
+
+ uid=nnn All files in the partition will be owned by user id nnn.
+ gid=nnn All files in the partition will be in group nnn.
+ umask=nnn The permission mask (see umask(1)) for the partition.
+ conv=binary Data is returned exactly as is, with CRLF's. [default]
+ conv=text (Carriage return, line feed) is replaced with newline.
+ conv=auto Chooses, file by file, conv=binary or conv=text (by guessing)
+
+There is one mount option unique to HPFS.
+
+ case=lower Convert file names to lower case. [default]
+ case=asis Return file names as is, in mixed case.
+
+Case is not significant in filename matching, like real HPFS.
+
+
+Command line example
+ mkdir -p /os2/c
+ mount -t hpfs -o uid=100,gid=100 /dev/sda6 /os2/c
+
+/etc/fstab example
+ /dev/sdb5 /d/f hpfs ro,uid=402,gid=402,umask=002
diff --git a/fs/hpfs/hpfs.h b/fs/hpfs/hpfs.h
new file mode 100644
index 000000000..3121a415d
--- /dev/null
+++ b/fs/hpfs/hpfs.h
@@ -0,0 +1,498 @@
+/* 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 = N. of B. in 1.00390625 S.*/
+ 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: 1; /* 0 clean, 1 "improperly stopped" */
+ unsigned flag1234: 4; /* unknown flags */
+ unsigned fast: 1; /* partition was fast formatted */
+ unsigned flag6to31: 26; /* unknown flags */
+
+ 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 bet you can see it coming... */
+
+#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 initialized 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 doesn't use code pages 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. The fnode for a directory
+ contains one pointer, to the root dnode of the tree. 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 is
+ interesting 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 glitch 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 EA's with NEEDEA set */
+ 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 name, 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 secno ea_indirect_secno (struct extended_attribute *ea)
+{
+ unsigned *v = (void *) ea_value (ea);
+ return v[1];
+}
+
+/*
+ Local Variables:
+ comment-column: 40
+ End:
+*/
diff --git a/fs/hpfs/hpfs_fs.c b/fs/hpfs/hpfs_fs.c
new file mode 100644
index 000000000..c05cf56ab
--- /dev/null
+++ b/fs/hpfs/hpfs_fs.c
@@ -0,0 +1,1727 @@
+/*
+ * linux/fs/hpfs/hpfs_fs.c
+ * read-only HPFS
+ * version 1.0
+ *
+ * Chris Smith 1993
+ *
+ * Sources & references:
+ * Duncan, _Design ... of HPFS_, MSJ 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
+ */
+
+#include <linux/fs.h>
+#include <linux/hpfs_fs.h>
+#include <linux/errno.h>
+#include <linux/malloc.h>
+#include <linux/kernel.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 and bitmaps. For bmap to work, we must run the
+ * file system with 512-byte blocks. The 2k blocks are assembled in 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 seldom
+ * seen -- in fact noncontiguous files are seldom 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 NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de)))
+#define ROUND_UP(x) (((x)+3) & ~3)
+
+#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 *, char *);
+
+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 */
+};
+
+/* Four 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 unsigned char *name, unsigned len,
+ struct quad_buffer_head *qbh);
+static struct hpfs_dirent *map_pos_dirent(struct inode *inode, loff_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 struct fnode *map_fnode(dev_t dev, ino_t ino,
+ struct buffer_head **bhp);
+static struct anode *map_anode(dev_t dev, unsigned secno,
+ struct buffer_head **bhp);
+static struct dnode *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 wrong answers 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->fs->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 leave the dates blank, to be filled in from the dir entry.
+ *
+ * NOTE that there must be no sleeping from the return in this routine
+ * until lookup() finishes filling in the inode, otherwise the partly
+ * completed inode would be visible during the sleep.
+ *
+ * It is done in this strange and sinful way because 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 that way, but
+ * ls sounds like fsck.
+ */
+
+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. For free inode counts we report the count of dnodes in the
+ * directory band -- not exactly right but pretty analogous.
+ */
+
+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 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. Don't let read only be turned off.
+ */
+
+static int hpfs_remount_fs(struct super_block *s, int *flags, char *data)
+{
+ 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 - (off_t) 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 do that
+ */
+ 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 - (off_t) 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 specified directory 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;
+
+ /*
+ * Fill 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.
+ */
+ 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(const unsigned char *s1, const 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 unsigned 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 ((off_t) filp->f_pos) {
+ case 0:
+ write_one_dirent(dirent, ".", 1, inode->i_ino, lc);
+ filp->f_pos = -1;
+ return ROUND_UP(NAME_OFFSET(dirent) + 2);
+
+ case -1:
+ write_one_dirent(dirent, "..", 2,
+ inode->i_hpfs_parent_dir, lc);
+ filp->f_pos = 1;
+ return ROUND_UP(NAME_OFFSET(dirent) + 3);
+
+ 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 ROUND_UP(NAME_OFFSET(dirent) + namelen + 1);
+ }
+}
+
+/*
+ * 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.
+ */
+
+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, loff_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 struct fnode *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 struct anode *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 struct dnode *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, 512, 0, UINT_MAX);
+ 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);
+}