home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 10 Tools
/
10-Tools.zip
/
hpfslinx.zip
/
hpfs-lnx.pat
< prev
next >
Wrap
Text File
|
1993-10-11
|
64KB
|
2,006 lines
diff -rc2N linux-dist/config.in linux/config.in
*** linux-dist/config.in Fri Sep 17 14:00:40 1993
--- linux/config.in Sun Oct 10 12:17:35 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' 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 Sun Oct 10 12:17:35 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 Sun Oct 10 12:17:35 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 Sun Oct 10 12:18:35 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 Sun Oct 10 12:17:36 1993
***************
*** 0 ****
--- 1,383 ----
+ /* The article
+
+ 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 article, 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 sig 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[450];
+ };
+
+
+ /* sector 10h */
+
+ /* 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 zero1; /* 0 */
+ secno huh4; /* 14 ?? 4 sectors long, has pointer
+ to band0 free space bitmap
+ and many zeros */
+ unsigned zero2; /* 0 */
+ secno huh6; /* 1c ?? 4 sectors long, all zeros.
+ 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 spare_dnodes; /* ?? 8 preallocated sectors near dir
+ band. the node splitting spares? */
+ unsigned pad[103]; /* 0 */
+ };
+
+
+ /* sector 11h */
+
+ /* The spare block has pointers to spare sectors. The following
+ struct is more than usually conjectural, since my disks hide the
+ existence of bad blocks. */
+
+ #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; /* 20 ?? 4 sectors long, looks like
+ secno from[n_spares];
+ secno to[n_spares];
+ the from array is all 0's,
+ the to array is initialized
+ to point to n_spares
+ preallocated empties */
+
+ unsigned n_spares_used; /* 0 ??? used spare count? */
+ unsigned n_spares; /* 64 ?? unused spare count?
+ total spare count? */
+ unsigned n_dnode_spares_1; /* 14 ??? 20 decimal */
+ unsigned n_dnode_spares_2; /* 14 ??? 20 decimal */
+ /* the above 2 words are probably
+ the number of dnode spares and
+ the number of free ones. */
+
+ secno weirdblock; /* 88 ?? first of 2 peculiar blocks */
+ unsigned n_weird; /* 2 ?? number of peculiar blocks */
+ unsigned hash1, hash2; /* ?? checksums of something? */
+
+ unsigned zero1[19]; /* 0 */
+
+ dnode_secno spares[20]; /* ?? spare dnodes? */
+ unsigned zero3[20]; /* ?? other half of hotfix map? */
+ unsigned zero2[57]; /* 0 */
+ };
+
+ /* The two peculiar blocks pointed to by the spare block stump me completely.
+ They start with what look like magic numbers: */
+
+ #define BIZZ1_MAGIC 0x494521f7
+ #define BIZZ2_MAGIC 0x894521f7
+
+ /* They have many other numbers, equally magic. The location of these
+ blocks varies with the size of the filesystem, their contents
+ doesn't. The first block is constant except for a couple of
+ pointers to the second block. It is mostly zero. The second block
+ is constant, period. It is fond of byte sequences like
+
+ cfcecdcc cbcac9c8 c7c7c5c4 c3c2c1c0 060 ........ ........
+
+ with some like
+
+ 9f9e9d9c 9d9a9959 ebeae399 e2929290 030 ........ Y.......
+
+ but it isn't, e.g., a permutation of the low-numbered blocks in the
+ partition. It isn't a permutation at all. It looks like two words
+ followed by 200h bytes, the bytes being a variation on x[i] = i but
+ with what may be word values stuck in here and there. Heaven knows
+ what it means, I just hope it's not important. */
+
+
+ /* 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.
+ What happens if the last band is < 4 sectors long I don't know.
+
+ The band-0 bitmap at 18 is pointed to by the first word of sector 14,
+ which is pointed to by the super block. The remaining words of
+ sector 14 don't point to the other bitmaps, though. On a wild guess,
+ the 4 sectors from 14-17 can point to alternate bitmap locations for
+ bands 0-511. Or maybe it's a table of offsets.
+
+ 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 to first free dirent */
+ unsigned increment_me; /* some kind of activity counter?
+ Neither HPFS.IFS nor CHKDSK cares
+ if you change this word */
+ fnode_secno up; /* fnode that points to directory's
+ root 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 flag7: 1;
+ 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 creation_date; /* ctime */
+ unsigned file_size; /* file length, bytes */
+ time_t read_date; /* atime */
+ time_t write_date; /* mtime */
+ 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. */
+ };
+
+
+ /* 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. (Triples, actually, 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_header
+ {
+ unsigned flag0: 1;
+ unsigned flag1: 1;
+ unsigned flag2: 1;
+ unsigned flag3: 1;
+ unsigned flag4: 1;
+ unsigned fnode_parent: 1; /* ?? parent is an fnode?
+ A particularly silly flag, but
+ that seems to be it */
+ 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 { internal[], leaf[] }; follows here */
+ };
+
+ struct bplus_internal_node
+ {
+ unsigned file_secno; /* subtree maps sectors < this */
+ anode_secno down; /* pointer to subtree */
+ };
+
+ struct bplus_leaf_node
+ {
+ unsigned file_secno; /* first file sector in extent */
+ unsigned length; /* length, sectors */
+ secno disk_secno; /* first corresponding disk sector */
+ };
+
+
+ /* 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[5];
+ unsigned short ea_size; /* length of fnode-resident ea's */
+ unsigned char zero3;
+ unsigned char dirflag; /* 1 -> directory */
+ /* if set, first & only extent
+ points to dnode. */
+
+ 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 zero4[5];
+ unsigned huhc4; /* c4 [ea's start at c4] */
+ unsigned zero5[2];
+ unsigned char ea[280]; /* zero or more EA's, packed on
+ byte boundaries */
+
+ unsigned zero6[9];
+ };
+
+
+ /* 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[2]; /* unused */
+ };
+
+
+ /* extended attribute in fnode */
+
+ struct fnode_ea {
+ unsigned indirect: 1; /* 1 -> value gives sector number
+ where real value starts */
+ unsigned flag1: 1; /* user flag, I think */
+ unsigned flag2: 1; /* user flag, I think */
+ unsigned flag3: 1; /* user flag, I think */
+ unsigned flag4: 1; /* user flag, I think */
+ unsigned flag5: 1; /* user flag, I think */
+ unsigned flag6: 1; /* user flag, I think */
+ unsigned flag7: 1; /* user flag, I think */
+ 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
+ */
+ };
+
+ /* Large values are handled by the indirect mechanism. I think one
+ contiguous sector run is required to hold the value. (?)
+
+ When too many EA's are defined, what seems to happen is that an EA
+ named .ASSOCTABLE appears whose value is a set of EA's. Another EA
+ named .CHECKSUM always seems to accompany it. ASSOCTABLE always has
+ a large value, CHECKSUM always doesn't. I don't know whether
+ this is an HPFS hack or a library hack. */
+
+ /*
+ 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 Sun Oct 10 12:18:53 1993
***************
*** 0 ****
--- 1,1391 ----
+ /*
+ * 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 */
+
+ /* HPFS is a mixture of 512-byte blocks and 2048-byte blocks. The 2k blocks
+ are used for directories (dnodes) and bitmaps. They are always aligned
+ and never aliased with 512-byte blocks, so it's no big deal keeping them
+ straight. However, Linux currently does not mix block sizes, so we run
+ the whole file system with 2k blocks.
+
+ However, I want du/df and friends to work right, so everything is reported
+ to the user as if the blocksize were 512 bytes.
+
+ 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 efficient 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. */
+
+ #include <stddef.h>
+ #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"
+
+ /* For details on the data structures see hpfs.h and the Duncan article.
+
+ Overview
+
+ [ The names of these data structures, except fnode, are not IBM's or
+ Microsoft'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 IBM or MS. Who know nothing about this. ]
+
+ [ Also, the following will make little sense if you haven't read the
+ Duncan article, 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 lists
+ 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
+ directory tree 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 extent lists. 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.
+
+ There is only one pointer to any dnode. If it is the root dnode of its
+ directory, the pointer is in the directory's fnode. If it is not the
+ root, the pointer is in the parent dnode.
+
+ The only real upward pointer around is in fnodes, which point to the
+ directory containing them (to its fnode). (So, in a directory's fnode,
+ this pointer is "..".) There are several pointers that seem to be for
+ consistency checks -- all dnodes point to the directory's fnode, and
+ several nodes contain pointers to themselves.
+
+ 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 avoiding fragmentation. */
+
+ /* notation */
+
+ #define little_ushort(x) (*(unsigned short *) &(x))
+ typedef void nonconst;
+
+ /* super block ops */
+
+ static void hpfs_read_inode (struct inode *);
+ static int hpfs_notify_change (int flags, struct inode *);
+ static void hpfs_write_inode (struct inode *);
+ static void hpfs_put_inode (struct inode *);
+ static void hpfs_put_super (struct super_block *);
+ static void hpfs_write_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,
+ hpfs_notify_change,
+ hpfs_write_inode,
+ hpfs_put_inode,
+ hpfs_put_super,
+ hpfs_write_super,
+ hpfs_statfs,
+ hpfs_remount_fs,
+ };
+
+ /* file ops */
+
+ static int hpfs_file_read (struct inode *, struct file *, char *, int);
+ static int hpfs_file_write (struct inode *, struct file *, char *, int);
+ static void hpfs_truncate (struct inode *);
+
+ static const struct file_operations hpfs_file_ops =
+ {
+ NULL, /* lseek - default */
+ hpfs_file_read, /* read */
+ hpfs_file_write, /* write */
+ NULL, /* readdir - bad */
+ NULL, /* select - default */
+ NULL, /* ioctl - default */
+ NULL, /* 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 */
+ NULL, /* bmap */
+ hpfs_truncate, /* truncate */
+ NULL, /* permission */
+ };
+
+
+ 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_create (struct inode *,const char *,int,int,struct inode **);
+ static int hpfs_lookup (struct inode *,const char *,int,struct inode **);
+ static int hpfs_unlink (struct inode *,const char *,int);
+ static int hpfs_mkdir (struct inode *,const char *,int,int);
+ static int hpfs_rmdir (struct inode *,const char *,int);
+ static int hpfs_rename (struct inode *,const char *,int,struct inode *,const char *,int);
+
+ 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 */
+ hpfs_create, /* create */
+ hpfs_lookup, /* lookup */
+ NULL, /* link */
+ hpfs_unlink, /* unlink */
+ NULL, /* symlink */
+ hpfs_mkdir, /* mkdir */
+ hpfs_rmdir, /* rmdir */
+ NULL, /* mknod */
+ hpfs_rename, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL, /* permission */
+ };
+
+ static int parse_opts (char *opts, uid_t *uid, gid_t *gid, umode_t *umask,
+ int *lowercase, int *conv);
+ 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 file_disk_secno (struct inode *inode, unsigned file_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 buffer_head **bhp);
+ static struct hpfs_dirent *map_pos_dirent (struct inode *inode, off_t *posp,
+ struct buffer_head **bhp);
+ 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 buffer_head **bhp);
+ 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 buffer_head **bhp);
+ static void *map_sector (dev_t dev, unsigned secno, struct buffer_head **bhp);
+ static void *map_4sectors (dev_t dev, unsigned secno,struct buffer_head **bhp);
+ static unsigned choose_conv (unsigned char *p, unsigned len);
+ static unsigned convcpy_tofs (unsigned char *out, unsigned char *in,
+ unsigned len);
+
+
+ /* make inode number for a file */
+
+ static ino_t file_ino (fnode_secno secno)
+ {
+ return secno << 1 | 1;
+ }
+
+ /* make inode number for a directory */
+
+ static ino_t dir_ino (fnode_secno secno)
+ {
+ return secno << 1;
+ }
+
+ /* get fnode address from an inode number */
+
+ static fnode_secno ino_secno (ino_t ino)
+ {
+ return ino >> 1;
+ }
+
+ /* test for directory's inode number */
+
+ static 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 */
+
+ /* 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;
+ dev_t dev;
+ uid_t uid;
+ gid_t gid;
+ umode_t umask;
+ int lowercase;
+ int conv;
+
+ /* 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, 2048);
+
+ /* fetch sectors 0, 16, 17 */
+
+ bootblock = map_sector (dev, 0, &bh0);
+ if (! bootblock)
+ { s->s_dev = 0; unlock_super (s); return 0; }
+
+ superblock = map_sector (dev, 16, &bh1);
+ if (! superblock)
+ { brelse (bh0); s->s_dev = 0; unlock_super (s); return 0; }
+
+ spareblock = map_sector (dev, 17, &bh2);
+ if (! spareblock)
+ { brelse (bh1); brelse (bh0); s->s_dev = 0; unlock_super (s); return 0; }
+
+ /* Check that this fs matches the ones I examined.
+ Probably some of these checks are unneeded, but the clueless do well
+ to be timid. */
+
+ if (little_ushort (bootblock->bytes_per_sector) != 512
+ || bootblock->sig_28h != 0x28
+ || memcmp (&bootblock->sig_hpfs, "HPFS ", 8)
+ || superblock->magic != SB_MAGIC
+ || superblock->huh202 != 0x202
+ || superblock->n_sectors != bootblock->n_sectors_l
+ || (superblock->n_sectors % 4) != 0
+ || spareblock->magic != SP_MAGIC
+ || spareblock->dirty != 0
+ || spareblock->n_spares_used != 0
+ || spareblock->n_dnode_spares_1 != spareblock->n_dnode_spares_2
+ || superblock->huh4 != 0x14
+ || superblock->huh6 != 0x1c
+ || superblock->zero1 || superblock->zero2
+ || superblock->zero3 || superblock->zero4) {
+ printk ("HPFS: hpfs_read_super: Not HPFS\n");
+ brelse (bh2); brelse (bh1); brelse (bh0);
+ s->s_dev = 0; unlock_super (s); return 0;
+ }
+
+ /* set fs read only */
+
+ s->s_flags |= MS_RDONLY;
+
+ /* fill in standard stuff */
+
+ s->s_magic = HPFS_SUPER_MAGIC;
+ s->s_blocksize = 2048;
+ s->s_blocksize_bits = 11;
+ 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_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 */
+
+ de = map_dirent (s->s_mounted, fnode_dno (dev, s->s_hpfs_root),
+ "\001\001", 2, &bh0);
+ if (! 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 = de->read_date;
+ s->s_mounted->i_mtime = de->write_date;
+ s->s_mounted->i_ctime = de->creation_date;
+
+ brelse (bh0);
+ return s;
+ }
+
+ /* 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.
+
+ (It really is evil, but consider the alternative: 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.)
+
+ (This will break someday if iget sleeps between calling us and
+ returning to lookup. Or if someone other than lookup calls iget.
+ That's not too big a danger since lookup is the only source of
+ i-numbers. Neither is a problem right now, 0.99.13.)
+
+ (An alternative would be to clone iget in this file so we can add
+ args to it. That's even more evil.) */
+
+ 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;
+ }
+ }
+
+ static int hpfs_notify_change (int flags, struct inode *inode)
+ {
+ return -EROFS;
+ }
+
+ static void hpfs_write_inode (struct inode *inode)
+ {
+ }
+
+ static void hpfs_put_inode (struct inode *inode)
+ {
+ }
+
+ static void hpfs_put_super (struct super_block *s)
+ {
+ lock_super (s);
+ s->s_dev = 0;
+ unlock_super (s);
+ }
+
+ static void hpfs_write_super (struct super_block *s)
+ {
+ }
+
+ /* statfs. We report results in 512-byte sectors, not our 2k buffer size.
+ For inode counts we report the count of dnodes in the directory band --
+ this is inaccurate because a dnode holds more than 1 file, and also
+ because when the directory band fills up HPFS just puts dnodes elsewhere.
+ But it's more interesting than zeros. */
+
+ static void hpfs_statfs (struct super_block *s, struct statfs *buf)
+ {
+ if (s->s_hpfs_n_free == -1) {
+ s->s_hpfs_n_free_dnodes = count_one_bitmap (s->s_dev, s->s_hpfs_dmap);
+ s->s_hpfs_n_free = count_bitmap (s);
+ }
+
+ 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 (255, &buf->f_namelen);
+ }
+
+ static int hpfs_remount_fs (struct super_block *s, int *flags)
+ {
+ 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 buffer_head *bh;
+ struct dnode *dnode;
+ struct hpfs_dirent *de;
+ struct hpfs_dirent *de_end;
+
+ dnode = map_dnode (inode->i_dev, dno, &bh);
+ if (! dnode) return;
+ de = (void *) dnode->dirent;
+ de_end = (void *) de + dnode->first_free;
+
+ (*n_dnodes)++;
+
+ for ( ; de < de_end; de = (void *) de + de->length) {
+ if (de->down)
+ count_dnodes (inode, *(dnode_secno *) ((void *) de + de->length - 4),
+ n_dnodes, n_subdirs);
+ if (de->directory && ! de->first)
+ (*n_subdirs)++;
+ if (de->last || de->length == 0)
+ break;
+ }
+
+ brelse (bh);
+ }
+
+ /* count the bits in the free space bit maps */
+
+ static unsigned count_bitmap (struct super_block *s)
+ {
+ unsigned n, fs_size = s->s_hpfs_fs_size;
+ unsigned count;
+
+ /* band 0 bitmap is in 18-1b */
+
+ count = count_one_bitmap (s->s_dev, 0x18);
+
+ /* remaining bitmaps are in the first or last 4 sectors of band */
+
+ for (n = 0x4000; n < fs_size; n += 0x4000)
+ if (n & 0x4000) {
+ unsigned t = n | 0x3ffc;
+ if (t > fs_size - 4) t = fs_size - 4;
+ count += count_one_bitmap (s->s_dev, t);
+ } else
+ count += count_one_bitmap (s->s_dev, n);
+
+ return count;
+ }
+
+ /* Read in one bit map, count the bits, return the count. */
+
+ static unsigned count_one_bitmap (dev_t dev, secno secno)
+ {
+ struct buffer_head *bh;
+ char *bits;
+ unsigned i, count;
+
+ bits = map_4sectors (dev, secno, &bh);
+ if (! bits) return 0;
+
+ count = 0;
+
+ for (i = 0; i < 8 * 2048; i++)
+ count += (test_bit (i, bits) != 0);
+ brelse (bh);
+
+ 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, file_disk_secno (inode, q), &bh);
+ if (! block) return -EIO;
+
+ /* but first decide if it has \r\n, if the user asked for 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 - 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;
+ }
+
+ static int hpfs_file_write (struct inode *inode, struct file *filp,
+ char *buf, int count)
+ {
+ return -EROFS;
+ }
+
+ static void hpfs_truncate (struct inode *inode)
+ {
+ }
+
+ /* Return the disk sector number containing a file sector. */
+
+ static secno file_disk_secno (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 = (void *) b + sizeof *b;
+ 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 = (void *) b + sizeof *b;
+ 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 a secret code 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 buffer_head *bh;
+ 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, &bh);
+ 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, &bh);
+ else
+ de = map_dirent (dir, dir->i_hpfs_dno, name, len, &bh);
+
+ /* 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 build 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 = de->read_date;
+ inode->i_mtime = de->write_date;
+ inode->i_ctime = de->creation_date;
+ if (! de->directory) {
+ inode->i_size = de->file_size;
+ inode->i_blocks = 1 + ((inode->i_size + 511) >> 9);
+ }
+ }
+
+ brelse (bh);
+
+ /* Made it. */
+
+ *result = inode;
+ iput (dir);
+ return 0;
+
+ /* Didn't. */
+ bail1:
+ brelse (bh);
+ bail:
+ iput (dir);
+ return -ENOENT;
+ }
+
+ /* Compare two counted strings ignoring case.
+ OS/2 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 buffer_head **bhp)
+ {
+ 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, bhp);
+ if (! dnode) return 0;
+
+ /* get pointers to start and end+1 of dir entries */
+ de = (void *) dnode->dirent;
+ de_end = (void *) de + dnode->first_free;
+
+ /* look through the entries for the name we're after */
+ for ( ; de < de_end; de = (void *) de + de->length) {
+
+ /* 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 < dir name -> not present. */
+ if (t < 0) {
+ /* if there is a subtree, search it. */
+ if (de->down) {
+ dnode_secno sub_dno = *(secno *) ((void *) de + de->length - 4);
+ brelse (*bhp);
+ return map_dirent (inode, sub_dno, name, len, bhp);
+ } 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 the code
+ between libc and here is *far* too hosed to permit anything more.
+ If it ever 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 buffer_head *bh;
+ 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, &bh);
+ 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);
+ brelse (bh);
+
+ 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 buffer_head **bhp)
+ {
+ unsigned pos, q, r;
+ dnode_secno dno;
+ struct hpfs_dirent *de;
+
+ /* Get the position code and split off the rightmost index */
+
+ pos = *posp;
+ q = pos >> 6;
+ r = pos & 077;
+
+ /* Get the sector address of the dnode pointed to by the leading part */
+
+ 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, bhp);
+
+ /* If none, we're out of files at this level. Ascend. */
+
+ if (! de) {
+ if (q == 0) return 0;
+ *posp = q + 1;
+ return map_pos_dirent (inode, posp, bhp);
+ }
+
+ /* 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) {
+ brelse (*bhp);
+ return map_pos_dirent (inode, posp, bhp);
+ } 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 buffer_head *bh;
+
+ /* 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, &bh);
+ if (! de || ! de->down) return 0;
+
+ /* get the dnode down pointer */
+ dno = *(dnode_secno *) ((void *) de + de->length - 4);
+ brelse (bh);
+
+ /* 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 buffer_head **bhp)
+ {
+ int i;
+ struct hpfs_dirent *de, *de_end;
+ struct dnode *dnode = map_dnode (dev, dno, bhp);
+
+ de = (void *) dnode->dirent;
+ de_end = (void *) de + dnode->first_free;
+
+ for (i = 1; de < de_end; i++, de = (void *) de + de->length) {
+ if (i == n)
+ return de;
+ if (de->last || de->length == 0)
+ break;
+ }
+
+ brelse (*bhp);
+ return 0;
+ }
+
+ static int hpfs_dir_read (struct inode *inode, struct file *filp,
+ char *buf, int count)
+ {
+ return -EISDIR;
+ }
+
+ static int hpfs_create (struct inode * dir, const char *name, int len,
+ int mode, struct inode **result)
+ {
+ return -EROFS;
+ }
+
+ static int hpfs_unlink (struct inode *dir, const char *name, int len)
+ {
+ return -EROFS;
+ }
+
+ static int hpfs_mkdir (struct inode *dir, const char *name, int len, int mode)
+ {
+ return -EROFS;
+ }
+
+ static int hpfs_rmdir (struct inode *dir, const char *name, int len)
+ {
+ return -EROFS;
+ }
+
+ static int hpfs_rename
+ (struct inode *old_dir, const char *old_name, int old_len,
+ struct inode *new_dir, const char *new_name, int new_len)
+ {
+ return -EROFS;
+ }
+
+ /* 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 || fnode->huhc4 != 0xc4)
+ { printk ("HPFS: map_fnode: in the weeds\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: in the weeds\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 buffer_head **bhp)
+ {
+ struct dnode *dnode;
+
+ if (secno == 0)
+ { printk ("HPFS: missing dnode\n"); return 0; }
+
+ dnode = map_4sectors (dev, secno, bhp);
+ if (dnode)
+ if (dnode->magic != DNODE_MAGIC || dnode->self != secno)
+ { printk ("HPFS: map_dnode: in the weeds\n"); brelse (*bhp); 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)
+ {
+ unsigned q, r;
+ struct buffer_head *bh;
+
+ q = secno >> 2;
+ r = (secno & 3) << 9;
+
+ if ((*bhp = bh = bread (dev, q, 2048)) != 0)
+ return bh->b_data + r;
+ else
+ { printk ("HPFS: map_sector: read error\n"); return 0; }
+ }
+
+ /* Map 4 sectors into a buffer and return pointers to it and to the buffer. */
+
+ static void *map_4sectors (dev_t dev, unsigned secno, struct buffer_head **bhp)
+ {
+ struct buffer_head *bh;
+
+ if (secno & 3)
+ { printk ("HPFS: map_4sectors: unaligned read\n"); return 0; }
+
+ if ((*bhp = bh = bread (dev, secno >> 2, 2048)) != 0)
+ return bh->b_data;
+ else
+ { printk ("HPFS: map_4sectors: read error\n"); return 0; }
+ }
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 Sun Oct 10 12:17:36 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 Sun Oct 10 12:17:36 1993
***************
*** 0 ****
--- 1,12 ----
+ #ifndef _LINUX_HPFS_FS_H
+ #define _LINUX_HPFS_FS_H
+
+ /* HPFS magic number (the real thing, 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 Sun Oct 10 12:17:36 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 Sun Oct 10 12:17:37 1993
***************
*** 0 ****
--- 1,31 ----
+ #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_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_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