From 1513ff9b7899ab588401c89db0e99903dbf5f886 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Mon, 28 Nov 1994 11:59:19 +0000 Subject: Import of Linus's Linux 1.1.68 --- fs/hpfs/Makefile | 30 + fs/hpfs/README | 25 + fs/hpfs/hpfs.h | 498 +++++++++++++++ fs/hpfs/hpfs_fs.c | 1727 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 2280 insertions(+) create mode 100644 fs/hpfs/Makefile create mode 100644 fs/hpfs/README create mode 100644 fs/hpfs/hpfs.h create mode 100644 fs/hpfs/hpfs_fs.c (limited to 'fs/hpfs') 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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); +} -- cgit v1.2.3