URL
https://opencores.org/ocsvn/or1k_old/or1k_old/trunk
Subversion Repositories or1k_old
Compare Revisions
- This comparison shows the changes necessary to convert path
/or1k_old/trunk/rc203soc/sw/uClinux/fs/hpfs
- from Rev 1765 to Rev 1782
- ↔ Reverse comparison
Rev 1765 → Rev 1782
/hpfs.h
0,0 → 1,500
/* 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 uppercasing tables. I don't know what |
these are for (CHKDSK, maybe?) -- OS/2 does not seem to use them |
itself. Linux doesn't use them either. */ |
|
/* 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]; /* upcase table 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 ix; /* code page index (of filename), see |
struct code_page_data */ |
unsigned char namelen, name[1]; /* file name */ |
/* dnode_secno down; btree down pointer, if present, |
follows name on next word boundary, or maybe it |
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: |
*/ |
/hpfs_caps.c
0,0 → 1,171
/* Capitalization rules for HPFS */ |
|
/* In OS/2, HPFS filenames preserve upper and lower case letter distinctions |
but filename matching ignores case. That is, creating a file "Foo" |
actually creates a file named "Foo" which can be looked up as "Foo", |
"foo", or "FOO", among other possibilities. |
|
Also, HPFS is internationalized -- a table giving the uppercase |
equivalent of every character is stored in the filesystem, so that |
any national character set may be used. If several different |
national character sets are in use, several tables are stored |
in the filesystem. |
|
It would be perfectly reasonable for Linux HPFS to act as a Unix |
filesystem and match "Foo" only if asked for "Foo" exactly. But |
the sort order of HPFS directories is case-insensitive, so Linux |
still has to know the capitalization rules used by OS/2. Because |
of this, it turns out to be more natural for us to be case-insensitive |
than not. |
|
Currently the standard character set used by Linux is Latin-1. |
Work is underway to permit people to use UTF-8 instead, therefore |
all code that depends on the character set is segregated here. |
|
(It would be wonderful if Linux HPFS could be independent of what |
character set is in use on the Linux side, but because of the |
necessary case folding this is impossible.) |
|
There is a map from Latin-1 into code page 850 for every printing |
character in Latin-1. The NLS documentation of OS/2 shows that |
everybody has 850 available unless they don't have Western latin |
chars available at all (so fitting them to Linux without Unicode |
is a doomed exercise). |
|
It is not clear exactly how HPFS.IFS handles the situation when |
multiple code pages are in use. Experiments show that |
|
- tables on the disk give uppercasing rules for the installed code pages |
|
- each directory entry is tagged with what code page was current |
when that name was created |
|
- doing just CHCP, without changing what's on the disk in any way, |
can change what DIR reports, and what name a case-folded match |
will match. |
|
This means, I think, that HPFS.IFS operates in the current code |
page, without regard to the uppercasing information recorded in |
the tables on the disk. It does record the uppercasing rules |
it used, perhaps for CHKDSK, but it does not appear to use them |
itself. |
|
So: Linux, a Latin-1 system, will operate in code page 850. We |
recode between 850 and Latin-1 when dealing with the names actually |
on the disk. We don't use the uppercasing tables either. |
|
In a hypothetical UTF-8 implementation, one reasonable way to |
proceed that matches OS/2 (for least surprise) is: do case |
translation in UTF-8, and recode to/from one of the code pages |
available on the mounted filesystem. Reject as invalid any name |
containing chars that can't be represented on disk by one of the |
code pages OS/2 is using. Recoding from on-disk names to UTF-8 |
could use the code page tags, though this is not what OS/2 does. */ |
|
|
static const unsigned char tb_cp850_to_latin1[128] = |
{ |
199, 252, 233, 226, 228, 224, 229, 231, |
234, 235, 232, 239, 238, 236, 196, 197, |
201, 230, 198, 244, 246, 242, 251, 249, |
255, 214, 220, 248, 163, 216, 215, 159, |
225, 237, 243, 250, 241, 209, 170, 186, |
191, 174, 172, 189, 188, 161, 171, 187, |
155, 156, 157, 144, 151, 193, 194, 192, |
169, 135, 128, 131, 133, 162, 165, 147, |
148, 153, 152, 150, 145, 154, 227, 195, |
132, 130, 137, 136, 134, 129, 138, 164, |
240, 208, 202, 203, 200, 158, 205, 206, |
207, 149, 146, 141, 140, 166, 204, 139, |
211, 223, 212, 210, 245, 213, 181, 254, |
222, 218, 219, 217, 253, 221, 175, 180, |
173, 177, 143, 190, 182, 167, 247, 184, |
176, 168, 183, 185, 179, 178, 142, 160, |
}; |
|
#if 0 |
static const unsigned char tb_latin1_to_cp850[128] = |
{ |
186, 205, 201, 187, 200, 188, 204, 185, |
203, 202, 206, 223, 220, 219, 254, 242, |
179, 196, 218, 191, 192, 217, 195, 180, |
194, 193, 197, 176, 177, 178, 213, 159, |
255, 173, 189, 156, 207, 190, 221, 245, |
249, 184, 166, 174, 170, 240, 169, 238, |
248, 241, 253, 252, 239, 230, 244, 250, |
247, 251, 167, 175, 172, 171, 243, 168, |
183, 181, 182, 199, 142, 143, 146, 128, |
212, 144, 210, 211, 222, 214, 215, 216, |
209, 165, 227, 224, 226, 229, 153, 158, |
157, 235, 233, 234, 154, 237, 232, 225, |
133, 160, 131, 198, 132, 134, 145, 135, |
138, 130, 136, 137, 141, 161, 140, 139, |
208, 164, 149, 162, 147, 228, 148, 246, |
155, 151, 163, 150, 129, 236, 231, 152, |
}; |
#endif |
|
#define A_GRAVE 0300 |
#define THORN 0336 |
#define MULTIPLY 0327 |
#define a_grave 0340 |
#define thorn 0376 |
#define divide 0367 |
|
static inline unsigned latin1_upcase (unsigned c) |
{ |
if (c - 'a' <= 'z' - 'a' |
|| (c - a_grave <= thorn - a_grave |
&& c != divide)) |
return c - 'a' + 'A'; |
else |
return c; |
} |
|
static inline unsigned latin1_downcase (unsigned c) |
{ |
if (c - 'A' <= 'Z' - 'A' |
|| (c - A_GRAVE <= THORN - A_GRAVE |
&& c != MULTIPLY)) |
return c + 'a' - 'A'; |
else |
return c; |
} |
|
#if 0 |
static inline unsigned latin1_to_cp850 (unsigned c) |
{ |
if ((signed) c - 128 >= 0) |
return tb_latin1_to_cp850[c - 128]; |
else |
return c; |
} |
#endif |
|
static inline unsigned cp850_to_latin1 (unsigned c) |
{ |
if ((signed) c - 128 >= 0) |
return tb_cp850_to_latin1[c - 128]; |
else |
return c; |
} |
|
unsigned hpfs_char_to_upper_linux (unsigned c) |
{ |
return latin1_upcase (cp850_to_latin1 (c)); |
} |
|
unsigned linux_char_to_upper_linux (unsigned c) |
{ |
return latin1_upcase (c); |
} |
|
unsigned hpfs_char_to_lower_linux (unsigned c) |
{ |
return latin1_downcase (cp850_to_latin1 (c)); |
} |
|
unsigned hpfs_char_to_linux (unsigned c) |
{ |
return cp850_to_latin1 (c); |
} |
/hpfs_fs.c
0,0 → 1,1772
/* |
* linux/fs/hpfs/hpfs_fs.c |
* read-only HPFS |
* version 1.0 |
* |
* Chris Smith 1993 |
* |
* Sources & references: |
* Duncan, _Design ... of HPFS_, MSJ 4(5) (C) 1989 Microsoft Corp |
* linux/fs/minix Copyright (C) 1991, 1992, 1993 Linus Torvalds |
* linux/fs/msdos Written 1992, 1993 by Werner Almesberger |
* linux/fs/isofs Copyright (C) 1991 Eric Youngdale |
*/ |
|
#include <linux/module.h> |
|
#include <linux/fs.h> |
#include <linux/hpfs_fs.h> |
#include <linux/errno.h> |
#include <linux/malloc.h> |
#include <linux/kernel.h> |
#include <linux/sched.h> |
#include <linux/locks.h> |
#include <linux/stat.h> |
#include <linux/string.h> |
#include <asm/bitops.h> |
#include <asm/segment.h> |
|
#include "hpfs.h" |
#include "hpfs_caps.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 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 *, int); |
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_file_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 */ |
generic_readpage, /* readpage */ |
NULL, /* writepage */ |
(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, |
void *dirent, filldir_t filldir); |
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, /* readpage */ |
NULL, /* writepage */ |
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, int *nocheck); |
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(kdev_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 dnode_secno dir_subdno(struct inode *inode, unsigned pos); |
static struct hpfs_dirent *map_nth_dirent(kdev_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(kdev_t dev, ino_t ino); |
static struct fnode *map_fnode(kdev_t dev, ino_t ino, |
struct buffer_head **bhp); |
static struct anode *map_anode(kdev_t dev, unsigned secno, |
struct buffer_head **bhp); |
static struct dnode *map_dnode(kdev_t dev, unsigned secno, |
struct quad_buffer_head *qbh); |
static void *map_sector(kdev_t dev, unsigned secno, struct buffer_head **bhp); |
static void *map_4sectors(kdev_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; |
kdev_t dev; |
uid_t uid; |
gid_t gid; |
umode_t umask; |
int lowercase; |
int conv; |
int dubious; |
int nocheck; |
|
MOD_INC_USE_COUNT; |
|
/* |
* Get the mount options |
*/ |
|
if (!parse_opts(options, &uid, &gid, &umask, &lowercase, &conv, |
&nocheck)) { |
printk("HPFS: syntax error in mount options. Not mounted.\n"); |
s->s_dev = 0; |
MOD_DEC_USE_COUNT; |
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 && !nocheck) |
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; |
MOD_DEC_USE_COUNT; |
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; |
MOD_DEC_USE_COUNT; |
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); |
MOD_DEC_USE_COUNT; |
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, int *nocheck) |
{ |
char *p, *rhs; |
|
*uid = current->uid; |
*gid = current->gid; |
*umask = current->fs->umask; |
*lowercase = 1; |
*conv = CONV_BINARY; |
*nocheck = 0; |
|
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 if (!strcmp(p,"nocheck")) |
*nocheck=1; |
else |
return 1; |
} |
|
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); |
MOD_DEC_USE_COUNT; |
} |
|
/* |
* 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, int bufsiz) |
{ |
struct statfs tmp; |
|
/* |
* 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 |
*/ |
tmp.f_type = s->s_magic; |
tmp.f_bsize = 512; |
tmp.f_blocks = s->s_hpfs_fs_size; |
tmp.f_bfree = s->s_hpfs_n_free; |
tmp.f_bavail = s->s_hpfs_n_free; |
tmp.f_files = s->s_hpfs_dirband_size; |
tmp.f_ffree = s->s_hpfs_n_free_dnodes; |
tmp.f_namelen = 254; |
memcpy_tofs(buf, &tmp, bufsiz); |
} |
|
/* |
* 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(kdev_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_user(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 = linux_char_to_upper_linux (*s1++); |
unsigned c2 = hpfs_char_to_upper_linux (*s2++); |
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. |
*/ |
brelse4(qbh); |
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 now can handle multiple dirents, although the current libc doesn't |
* use that. The way hpfs does this is pretty strange, as we need to do |
* the name translation etc before calling "filldir()". This is untested, |
* as I don't have any hpfs partitions to test against. Linus ] |
* |
* 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. |
*/ |
|
/* |
* Translate the given name: Blam it to lowercase if the mount option said to. |
*/ |
|
static void translate_hpfs_name(const unsigned char * from, int len, char * to, int lowercase) |
{ |
while (len > 0) { |
unsigned t = *from; |
len--; |
if (lowercase) |
t = hpfs_char_to_lower_linux (t); |
else |
t = hpfs_char_to_linux (t); |
*to = t; |
from++; |
to++; |
} |
} |
|
static int hpfs_readdir(struct inode *inode, struct file *filp, void * dirent, |
filldir_t filldir) |
{ |
struct quad_buffer_head qbh; |
struct hpfs_dirent *de; |
int namelen, lc; |
ino_t ino; |
char * tempname; |
long old_pos; |
|
if (inode == 0 |
|| inode->i_sb == 0 |
|| !S_ISDIR(inode->i_mode)) |
return -EBADF; |
|
tempname = (char *) __get_free_page(GFP_KERNEL); |
if (!tempname) |
return -ENOMEM; |
|
lc = inode->i_sb->s_hpfs_lowercase; |
switch ((long) filp->f_pos) { |
case -2: |
break; |
|
case 0: |
if (filldir(dirent, ".", 1, filp->f_pos, inode->i_ino) < 0) |
break; |
filp->f_pos = -1; |
/* fall through */ |
|
case -1: |
if (filldir(dirent, "..", 2, filp->f_pos, inode->i_hpfs_parent_dir) < 0) |
break; |
filp->f_pos = 1; |
/* fall through */ |
|
default: |
for (;;) { |
old_pos = filp->f_pos; |
de = map_pos_dirent(inode, &filp->f_pos, &qbh); |
if (!de) { |
filp->f_pos = -2; |
break; |
} |
namelen = de->namelen; |
translate_hpfs_name(de->name, namelen, tempname, lc); |
if (de->directory) |
ino = dir_ino(de->fnode); |
else |
ino = file_ino(de->fnode); |
brelse4(&qbh); |
if (filldir(dirent, tempname, namelen, old_pos, ino) < 0) { |
filp->f_pos = old_pos; |
break; |
} |
} |
} |
free_page((unsigned long) tempname); |
return 0; |
} |
|
/* |
* 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(kdev_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(kdev_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(kdev_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(kdev_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(kdev_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(kdev_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(kdev_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); |
} |
|
static struct file_system_type hpfs_fs_type = { |
hpfs_read_super, "hpfs", 1, NULL |
}; |
|
int init_hpfs_fs(void) |
{ |
return register_filesystem(&hpfs_fs_type); |
} |
|
#ifdef MODULE |
int init_module(void) |
{ |
int status; |
|
if ((status = init_hpfs_fs()) == 0) |
register_symtab(0); |
return status; |
} |
|
void cleanup_module(void) |
{ |
unregister_filesystem(&hpfs_fs_type); |
} |
|
#endif |
|
/hpfs_caps.h
0,0 → 1,4
unsigned hpfs_char_to_linux (unsigned c); |
unsigned hpfs_char_to_lower_linux (unsigned c); |
unsigned hpfs_char_to_upper_linux (unsigned c); |
unsigned linux_char_to_upper_linux (unsigned c); |
/.depend
0,0 → 1,15
hpfs_fs.o: \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/module.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/fs.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/hpfs_fs.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/errno.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/malloc.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/kernel.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/sched.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/locks.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/stat.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/string.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/bitops.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/segment.h \ |
hpfs.h \ |
hpfs_caps.h |
/Makefile
0,0 → 1,14
# |
# 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... |
|
O_TARGET := hpfs.o |
O_OBJS := hpfs_fs.o hpfs_caps.o |
M_OBJS := $(O_TARGET) |
|
include $(TOPDIR)/Rules.make |