add idl4k kernel firmware version 1.13.0.105

This commit is contained in:
Jaroslav Kysela
2015-03-26 17:22:37 +01:00
parent 5194d2792e
commit e9070cdc77
31064 changed files with 12769984 additions and 0 deletions

14
kernel/fs/hpfs/Kconfig Normal file
View File

@@ -0,0 +1,14 @@
config HPFS_FS
tristate "OS/2 HPFS file system support"
depends on BLOCK
help
OS/2 is IBM's operating system for PC's, the same as Warp, and HPFS
is the file system used for organizing files on OS/2 hard disk
partitions. Say Y if you want to be able to read files from and
write files to an OS/2 HPFS partition on your hard drive. OS/2
floppies however are in regular MSDOS format, so you don't need this
option in order to be able to read them. Read
<file:Documentation/filesystems/hpfs.txt>.
To compile this file system support as a module, choose M here: the
module will be called hpfs. If unsure, say N.

8
kernel/fs/hpfs/Makefile Normal file
View File

@@ -0,0 +1,8 @@
#
# Makefile for the Linux hpfs filesystem routines.
#
obj-$(CONFIG_HPFS_FS) += hpfs.o
hpfs-objs := alloc.o anode.o buffer.o dentry.o dir.o dnode.o ea.o file.o \
inode.o map.o name.o namei.o super.o

456
kernel/fs/hpfs/alloc.c Normal file
View File

@@ -0,0 +1,456 @@
/*
* linux/fs/hpfs/alloc.c
*
* Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
*
* HPFS bitmap operations
*/
#include "hpfs_fn.h"
static int hpfs_alloc_if_possible_nolock(struct super_block *s, secno sec);
/*
* Check if a sector is allocated in bitmap
* This is really slow. Turned on only if chk==2
*/
static int chk_if_allocated(struct super_block *s, secno sec, char *msg)
{
struct quad_buffer_head qbh;
unsigned *bmp;
if (!(bmp = hpfs_map_bitmap(s, sec >> 14, &qbh, "chk"))) goto fail;
if ((bmp[(sec & 0x3fff) >> 5] >> (sec & 0x1f)) & 1) {
hpfs_error(s, "sector '%s' - %08x not allocated in bitmap", msg, sec);
goto fail1;
}
hpfs_brelse4(&qbh);
if (sec >= hpfs_sb(s)->sb_dirband_start && sec < hpfs_sb(s)->sb_dirband_start + hpfs_sb(s)->sb_dirband_size) {
unsigned ssec = (sec - hpfs_sb(s)->sb_dirband_start) / 4;
if (!(bmp = hpfs_map_dnode_bitmap(s, &qbh))) goto fail;
if ((bmp[ssec >> 5] >> (ssec & 0x1f)) & 1) {
hpfs_error(s, "sector '%s' - %08x not allocated in directory bitmap", msg, sec);
goto fail1;
}
hpfs_brelse4(&qbh);
}
return 0;
fail1:
hpfs_brelse4(&qbh);
fail:
return 1;
}
/*
* Check if sector(s) have proper number and additionally check if they're
* allocated in bitmap.
*/
int hpfs_chk_sectors(struct super_block *s, secno start, int len, char *msg)
{
if (start + len < start || start < 0x12 ||
start + len > hpfs_sb(s)->sb_fs_size) {
hpfs_error(s, "sector(s) '%s' badly placed at %08x", msg, start);
return 1;
}
if (hpfs_sb(s)->sb_chk>=2) {
int i;
for (i = 0; i < len; i++)
if (chk_if_allocated(s, start + i, msg)) return 1;
}
return 0;
}
static secno alloc_in_bmp(struct super_block *s, secno near, unsigned n, unsigned forward)
{
struct quad_buffer_head qbh;
unsigned *bmp;
unsigned bs = near & ~0x3fff;
unsigned nr = (near & 0x3fff) & ~(n - 1);
/*unsigned mnr;*/
unsigned i, q;
int a, b;
secno ret = 0;
if (n != 1 && n != 4) {
hpfs_error(s, "Bad allocation size: %d", n);
return 0;
}
lock_super(s);
if (bs != ~0x3fff) {
if (!(bmp = hpfs_map_bitmap(s, near >> 14, &qbh, "aib"))) goto uls;
} else {
if (!(bmp = hpfs_map_dnode_bitmap(s, &qbh))) goto uls;
}
if (!tstbits(bmp, nr, n + forward)) {
ret = bs + nr;
goto rt;
}
/*if (!tstbits(bmp, nr + n, n + forward)) {
ret = bs + nr + n;
goto rt;
}*/
q = nr + n; b = 0;
while ((a = tstbits(bmp, q, n + forward)) != 0) {
q += a;
if (n != 1) q = ((q-1)&~(n-1))+n;
if (!b) {
if (q>>5 != nr>>5) {
b = 1;
q = nr & 0x1f;
}
} else if (q > nr) break;
}
if (!a) {
ret = bs + q;
goto rt;
}
nr >>= 5;
/*for (i = nr + 1; i != nr; i++, i &= 0x1ff) {*/
i = nr;
do {
if (!bmp[i]) goto cont;
if (n + forward >= 0x3f && bmp[i] != -1) goto cont;
q = i<<5;
if (i > 0) {
unsigned k = bmp[i-1];
while (k & 0x80000000) {
q--; k <<= 1;
}
}
if (n != 1) q = ((q-1)&~(n-1))+n;
while ((a = tstbits(bmp, q, n + forward)) != 0) {
q += a;
if (n != 1) q = ((q-1)&~(n-1))+n;
if (q>>5 > i) break;
}
if (!a) {
ret = bs + q;
goto rt;
}
cont:
i++, i &= 0x1ff;
} while (i != nr);
rt:
if (ret) {
if (hpfs_sb(s)->sb_chk && ((ret >> 14) != (bs >> 14) || (bmp[(ret & 0x3fff) >> 5] | ~(((1 << n) - 1) << (ret & 0x1f))) != 0xffffffff)) {
hpfs_error(s, "Allocation doesn't work! Wanted %d, allocated at %08x", n, ret);
ret = 0;
goto b;
}
bmp[(ret & 0x3fff) >> 5] &= ~(((1 << n) - 1) << (ret & 0x1f));
hpfs_mark_4buffers_dirty(&qbh);
}
b:
hpfs_brelse4(&qbh);
uls:
unlock_super(s);
return ret;
}
/*
* Allocation strategy: 1) search place near the sector specified
* 2) search bitmap where free sectors last found
* 3) search all bitmaps
* 4) search all bitmaps ignoring number of pre-allocated
* sectors
*/
secno hpfs_alloc_sector(struct super_block *s, secno near, unsigned n, int forward, int lock)
{
secno sec;
int i;
unsigned n_bmps;
struct hpfs_sb_info *sbi = hpfs_sb(s);
int f_p = 0;
int near_bmp;
if (forward < 0) {
forward = -forward;
f_p = 1;
}
if (lock) hpfs_lock_creation(s);
n_bmps = (sbi->sb_fs_size + 0x4000 - 1) >> 14;
if (near && near < sbi->sb_fs_size) {
if ((sec = alloc_in_bmp(s, near, n, f_p ? forward : forward/4))) goto ret;
near_bmp = near >> 14;
} else near_bmp = n_bmps / 2;
/*
if (b != -1) {
if ((sec = alloc_in_bmp(s, b<<14, n, f_p ? forward : forward/2))) {
b &= 0x0fffffff;
goto ret;
}
if (b > 0x10000000) if ((sec = alloc_in_bmp(s, (b&0xfffffff)<<14, n, f_p ? forward : 0))) goto ret;
*/
if (!f_p) if (forward > sbi->sb_max_fwd_alloc) forward = sbi->sb_max_fwd_alloc;
less_fwd:
for (i = 0; i < n_bmps; i++) {
if (near_bmp+i < n_bmps && ((sec = alloc_in_bmp(s, (near_bmp+i) << 14, n, forward)))) {
sbi->sb_c_bitmap = near_bmp+i;
goto ret;
}
if (!forward) {
if (near_bmp-i-1 >= 0 && ((sec = alloc_in_bmp(s, (near_bmp-i-1) << 14, n, forward)))) {
sbi->sb_c_bitmap = near_bmp-i-1;
goto ret;
}
} else {
if (near_bmp+i >= n_bmps && ((sec = alloc_in_bmp(s, (near_bmp+i-n_bmps) << 14, n, forward)))) {
sbi->sb_c_bitmap = near_bmp+i-n_bmps;
goto ret;
}
}
if (i == 1 && sbi->sb_c_bitmap != -1 && ((sec = alloc_in_bmp(s, (sbi->sb_c_bitmap) << 14, n, forward)))) {
goto ret;
}
}
if (!f_p) {
if (forward) {
sbi->sb_max_fwd_alloc = forward * 3 / 4;
forward /= 2;
goto less_fwd;
}
}
sec = 0;
ret:
if (sec && f_p) {
for (i = 0; i < forward; i++) {
if (!hpfs_alloc_if_possible_nolock(s, sec + i + 1)) {
hpfs_error(s, "Prealloc doesn't work! Wanted %d, allocated at %08x, can't allocate %d", forward, sec, i);
sec = 0;
break;
}
}
}
if (lock) hpfs_unlock_creation(s);
return sec;
}
static secno alloc_in_dirband(struct super_block *s, secno near, int lock)
{
unsigned nr = near;
secno sec;
struct hpfs_sb_info *sbi = hpfs_sb(s);
if (nr < sbi->sb_dirband_start)
nr = sbi->sb_dirband_start;
if (nr >= sbi->sb_dirband_start + sbi->sb_dirband_size)
nr = sbi->sb_dirband_start + sbi->sb_dirband_size - 4;
nr -= sbi->sb_dirband_start;
nr >>= 2;
if (lock) hpfs_lock_creation(s);
sec = alloc_in_bmp(s, (~0x3fff) | nr, 1, 0);
if (lock) hpfs_unlock_creation(s);
if (!sec) return 0;
return ((sec & 0x3fff) << 2) + sbi->sb_dirband_start;
}
/* Alloc sector if it's free */
static int hpfs_alloc_if_possible_nolock(struct super_block *s, secno sec)
{
struct quad_buffer_head qbh;
unsigned *bmp;
lock_super(s);
if (!(bmp = hpfs_map_bitmap(s, sec >> 14, &qbh, "aip"))) goto end;
if (bmp[(sec & 0x3fff) >> 5] & (1 << (sec & 0x1f))) {
bmp[(sec & 0x3fff) >> 5] &= ~(1 << (sec & 0x1f));
hpfs_mark_4buffers_dirty(&qbh);
hpfs_brelse4(&qbh);
unlock_super(s);
return 1;
}
hpfs_brelse4(&qbh);
end:
unlock_super(s);
return 0;
}
int hpfs_alloc_if_possible(struct super_block *s, secno sec)
{
int r;
hpfs_lock_creation(s);
r = hpfs_alloc_if_possible_nolock(s, sec);
hpfs_unlock_creation(s);
return r;
}
/* Free sectors in bitmaps */
void hpfs_free_sectors(struct super_block *s, secno sec, unsigned n)
{
struct quad_buffer_head qbh;
unsigned *bmp;
struct hpfs_sb_info *sbi = hpfs_sb(s);
/*printk("2 - ");*/
if (!n) return;
if (sec < 0x12) {
hpfs_error(s, "Trying to free reserved sector %08x", sec);
return;
}
lock_super(s);
sbi->sb_max_fwd_alloc += n > 0xffff ? 0xffff : n;
if (sbi->sb_max_fwd_alloc > 0xffffff) sbi->sb_max_fwd_alloc = 0xffffff;
new_map:
if (!(bmp = hpfs_map_bitmap(s, sec >> 14, &qbh, "free"))) {
unlock_super(s);
return;
}
new_tst:
if ((bmp[(sec & 0x3fff) >> 5] >> (sec & 0x1f) & 1)) {
hpfs_error(s, "sector %08x not allocated", sec);
hpfs_brelse4(&qbh);
unlock_super(s);
return;
}
bmp[(sec & 0x3fff) >> 5] |= 1 << (sec & 0x1f);
if (!--n) {
hpfs_mark_4buffers_dirty(&qbh);
hpfs_brelse4(&qbh);
unlock_super(s);
return;
}
if (!(++sec & 0x3fff)) {
hpfs_mark_4buffers_dirty(&qbh);
hpfs_brelse4(&qbh);
goto new_map;
}
goto new_tst;
}
/*
* Check if there are at least n free dnodes on the filesystem.
* Called before adding to dnode. If we run out of space while
* splitting dnodes, it would corrupt dnode tree.
*/
int hpfs_check_free_dnodes(struct super_block *s, int n)
{
int n_bmps = (hpfs_sb(s)->sb_fs_size + 0x4000 - 1) >> 14;
int b = hpfs_sb(s)->sb_c_bitmap & 0x0fffffff;
int i, j;
unsigned *bmp;
struct quad_buffer_head qbh;
if ((bmp = hpfs_map_dnode_bitmap(s, &qbh))) {
for (j = 0; j < 512; j++) {
unsigned k;
if (!bmp[j]) continue;
for (k = bmp[j]; k; k >>= 1) if (k & 1) if (!--n) {
hpfs_brelse4(&qbh);
return 0;
}
}
}
hpfs_brelse4(&qbh);
i = 0;
if (hpfs_sb(s)->sb_c_bitmap != -1) {
bmp = hpfs_map_bitmap(s, b, &qbh, "chkdn1");
goto chk_bmp;
}
chk_next:
if (i == b) i++;
if (i >= n_bmps) return 1;
bmp = hpfs_map_bitmap(s, i, &qbh, "chkdn2");
chk_bmp:
if (bmp) {
for (j = 0; j < 512; j++) {
unsigned k;
if (!bmp[j]) continue;
for (k = 0xf; k; k <<= 4)
if ((bmp[j] & k) == k) {
if (!--n) {
hpfs_brelse4(&qbh);
return 0;
}
}
}
hpfs_brelse4(&qbh);
}
i++;
goto chk_next;
}
void hpfs_free_dnode(struct super_block *s, dnode_secno dno)
{
if (hpfs_sb(s)->sb_chk) if (dno & 3) {
hpfs_error(s, "hpfs_free_dnode: dnode %08x not aligned", dno);
return;
}
if (dno < hpfs_sb(s)->sb_dirband_start ||
dno >= hpfs_sb(s)->sb_dirband_start + hpfs_sb(s)->sb_dirband_size) {
hpfs_free_sectors(s, dno, 4);
} else {
struct quad_buffer_head qbh;
unsigned *bmp;
unsigned ssec = (dno - hpfs_sb(s)->sb_dirband_start) / 4;
lock_super(s);
if (!(bmp = hpfs_map_dnode_bitmap(s, &qbh))) {
unlock_super(s);
return;
}
bmp[ssec >> 5] |= 1 << (ssec & 0x1f);
hpfs_mark_4buffers_dirty(&qbh);
hpfs_brelse4(&qbh);
unlock_super(s);
}
}
struct dnode *hpfs_alloc_dnode(struct super_block *s, secno near,
dnode_secno *dno, struct quad_buffer_head *qbh,
int lock)
{
struct dnode *d;
if (hpfs_count_one_bitmap(s, hpfs_sb(s)->sb_dmap) > FREE_DNODES_ADD) {
if (!(*dno = alloc_in_dirband(s, near, lock)))
if (!(*dno = hpfs_alloc_sector(s, near, 4, 0, lock))) return NULL;
} else {
if (!(*dno = hpfs_alloc_sector(s, near, 4, 0, lock)))
if (!(*dno = alloc_in_dirband(s, near, lock))) return NULL;
}
if (!(d = hpfs_get_4sectors(s, *dno, qbh))) {
hpfs_free_dnode(s, *dno);
return NULL;
}
memset(d, 0, 2048);
d->magic = DNODE_MAGIC;
d->first_free = 52;
d->dirent[0] = 32;
d->dirent[2] = 8;
d->dirent[30] = 1;
d->dirent[31] = 255;
d->self = *dno;
return d;
}
struct fnode *hpfs_alloc_fnode(struct super_block *s, secno near, fnode_secno *fno,
struct buffer_head **bh)
{
struct fnode *f;
if (!(*fno = hpfs_alloc_sector(s, near, 1, FNODE_ALLOC_FWD, 1))) return NULL;
if (!(f = hpfs_get_sector(s, *fno, bh))) {
hpfs_free_sectors(s, *fno, 1);
return NULL;
}
memset(f, 0, 512);
f->magic = FNODE_MAGIC;
f->ea_offs = 0xc4;
f->btree.n_free_nodes = 8;
f->btree.first_free = 8;
return f;
}
struct anode *hpfs_alloc_anode(struct super_block *s, secno near, anode_secno *ano,
struct buffer_head **bh)
{
struct anode *a;
if (!(*ano = hpfs_alloc_sector(s, near, 1, ANODE_ALLOC_FWD, 1))) return NULL;
if (!(a = hpfs_get_sector(s, *ano, bh))) {
hpfs_free_sectors(s, *ano, 1);
return NULL;
}
memset(a, 0, 512);
a->magic = ANODE_MAGIC;
a->self = *ano;
a->btree.n_free_nodes = 40;
a->btree.n_used_nodes = 0;
a->btree.first_free = 8;
return a;
}

491
kernel/fs/hpfs/anode.c Normal file
View File

@@ -0,0 +1,491 @@
/*
* linux/fs/hpfs/anode.c
*
* Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
*
* handling HPFS anode tree that contains file allocation info
*/
#include "hpfs_fn.h"
/* Find a sector in allocation tree */
secno hpfs_bplus_lookup(struct super_block *s, struct inode *inode,
struct bplus_header *btree, unsigned sec,
struct buffer_head *bh)
{
anode_secno a = -1;
struct anode *anode;
int i;
int c1, c2 = 0;
go_down:
if (hpfs_sb(s)->sb_chk) if (hpfs_stop_cycles(s, a, &c1, &c2, "hpfs_bplus_lookup")) return -1;
if (btree->internal) {
for (i = 0; i < btree->n_used_nodes; i++)
if (btree->u.internal[i].file_secno > sec) {
a = btree->u.internal[i].down;
brelse(bh);
if (!(anode = hpfs_map_anode(s, a, &bh))) return -1;
btree = &anode->btree;
goto go_down;
}
hpfs_error(s, "sector %08x not found in internal anode %08x", sec, a);
brelse(bh);
return -1;
}
for (i = 0; i < btree->n_used_nodes; i++)
if (btree->u.external[i].file_secno <= sec &&
btree->u.external[i].file_secno + btree->u.external[i].length > sec) {
a = btree->u.external[i].disk_secno + sec - btree->u.external[i].file_secno;
if (hpfs_sb(s)->sb_chk) if (hpfs_chk_sectors(s, a, 1, "data")) {
brelse(bh);
return -1;
}
if (inode) {
struct hpfs_inode_info *hpfs_inode = hpfs_i(inode);
hpfs_inode->i_file_sec = btree->u.external[i].file_secno;
hpfs_inode->i_disk_sec = btree->u.external[i].disk_secno;
hpfs_inode->i_n_secs = btree->u.external[i].length;
}
brelse(bh);
return a;
}
hpfs_error(s, "sector %08x not found in external anode %08x", sec, a);
brelse(bh);
return -1;
}
/* Add a sector to tree */
secno hpfs_add_sector_to_btree(struct super_block *s, secno node, int fnod, unsigned fsecno)
{
struct bplus_header *btree;
struct anode *anode = NULL, *ranode = NULL;
struct fnode *fnode;
anode_secno a, na = -1, ra, up = -1;
secno se;
struct buffer_head *bh, *bh1, *bh2;
int n;
unsigned fs;
int c1, c2 = 0;
if (fnod) {
if (!(fnode = hpfs_map_fnode(s, node, &bh))) return -1;
btree = &fnode->btree;
} else {
if (!(anode = hpfs_map_anode(s, node, &bh))) return -1;
btree = &anode->btree;
}
a = node;
go_down:
if ((n = btree->n_used_nodes - 1) < -!!fnod) {
hpfs_error(s, "anode %08x has no entries", a);
brelse(bh);
return -1;
}
if (btree->internal) {
a = btree->u.internal[n].down;
btree->u.internal[n].file_secno = -1;
mark_buffer_dirty(bh);
brelse(bh);
if (hpfs_sb(s)->sb_chk)
if (hpfs_stop_cycles(s, a, &c1, &c2, "hpfs_add_sector_to_btree #1")) return -1;
if (!(anode = hpfs_map_anode(s, a, &bh))) return -1;
btree = &anode->btree;
goto go_down;
}
if (n >= 0) {
if (btree->u.external[n].file_secno + btree->u.external[n].length != fsecno) {
hpfs_error(s, "allocated size %08x, trying to add sector %08x, %cnode %08x",
btree->u.external[n].file_secno + btree->u.external[n].length, fsecno,
fnod?'f':'a', node);
brelse(bh);
return -1;
}
if (hpfs_alloc_if_possible(s, se = btree->u.external[n].disk_secno + btree->u.external[n].length)) {
btree->u.external[n].length++;
mark_buffer_dirty(bh);
brelse(bh);
return se;
}
} else {
if (fsecno) {
hpfs_error(s, "empty file %08x, trying to add sector %08x", node, fsecno);
brelse(bh);
return -1;
}
se = !fnod ? node : (node + 16384) & ~16383;
}
if (!(se = hpfs_alloc_sector(s, se, 1, fsecno*ALLOC_M>ALLOC_FWD_MAX ? ALLOC_FWD_MAX : fsecno*ALLOC_M<ALLOC_FWD_MIN ? ALLOC_FWD_MIN : fsecno*ALLOC_M, 1))) {
brelse(bh);
return -1;
}
fs = n < 0 ? 0 : btree->u.external[n].file_secno + btree->u.external[n].length;
if (!btree->n_free_nodes) {
up = a != node ? anode->up : -1;
if (!(anode = hpfs_alloc_anode(s, a, &na, &bh1))) {
brelse(bh);
hpfs_free_sectors(s, se, 1);
return -1;
}
if (a == node && fnod) {
anode->up = node;
anode->btree.fnode_parent = 1;
anode->btree.n_used_nodes = btree->n_used_nodes;
anode->btree.first_free = btree->first_free;
anode->btree.n_free_nodes = 40 - anode->btree.n_used_nodes;
memcpy(&anode->u, &btree->u, btree->n_used_nodes * 12);
btree->internal = 1;
btree->n_free_nodes = 11;
btree->n_used_nodes = 1;
btree->first_free = (char *)&(btree->u.internal[1]) - (char *)btree;
btree->u.internal[0].file_secno = -1;
btree->u.internal[0].down = na;
mark_buffer_dirty(bh);
} else if (!(ranode = hpfs_alloc_anode(s, /*a*/0, &ra, &bh2))) {
brelse(bh);
brelse(bh1);
hpfs_free_sectors(s, se, 1);
hpfs_free_sectors(s, na, 1);
return -1;
}
brelse(bh);
bh = bh1;
btree = &anode->btree;
}
btree->n_free_nodes--; n = btree->n_used_nodes++;
btree->first_free += 12;
btree->u.external[n].disk_secno = se;
btree->u.external[n].file_secno = fs;
btree->u.external[n].length = 1;
mark_buffer_dirty(bh);
brelse(bh);
if ((a == node && fnod) || na == -1) return se;
c2 = 0;
while (up != -1) {
struct anode *new_anode;
if (hpfs_sb(s)->sb_chk)
if (hpfs_stop_cycles(s, up, &c1, &c2, "hpfs_add_sector_to_btree #2")) return -1;
if (up != node || !fnod) {
if (!(anode = hpfs_map_anode(s, up, &bh))) return -1;
btree = &anode->btree;
} else {
if (!(fnode = hpfs_map_fnode(s, up, &bh))) return -1;
btree = &fnode->btree;
}
if (btree->n_free_nodes) {
btree->n_free_nodes--; n = btree->n_used_nodes++;
btree->first_free += 8;
btree->u.internal[n].file_secno = -1;
btree->u.internal[n].down = na;
btree->u.internal[n-1].file_secno = fs;
mark_buffer_dirty(bh);
brelse(bh);
brelse(bh2);
hpfs_free_sectors(s, ra, 1);
if ((anode = hpfs_map_anode(s, na, &bh))) {
anode->up = up;
anode->btree.fnode_parent = up == node && fnod;
mark_buffer_dirty(bh);
brelse(bh);
}
return se;
}
up = up != node ? anode->up : -1;
btree->u.internal[btree->n_used_nodes - 1].file_secno = /*fs*/-1;
mark_buffer_dirty(bh);
brelse(bh);
a = na;
if ((new_anode = hpfs_alloc_anode(s, a, &na, &bh))) {
anode = new_anode;
/*anode->up = up != -1 ? up : ra;*/
anode->btree.internal = 1;
anode->btree.n_used_nodes = 1;
anode->btree.n_free_nodes = 59;
anode->btree.first_free = 16;
anode->btree.u.internal[0].down = a;
anode->btree.u.internal[0].file_secno = -1;
mark_buffer_dirty(bh);
brelse(bh);
if ((anode = hpfs_map_anode(s, a, &bh))) {
anode->up = na;
mark_buffer_dirty(bh);
brelse(bh);
}
} else na = a;
}
if ((anode = hpfs_map_anode(s, na, &bh))) {
anode->up = node;
if (fnod) anode->btree.fnode_parent = 1;
mark_buffer_dirty(bh);
brelse(bh);
}
if (!fnod) {
if (!(anode = hpfs_map_anode(s, node, &bh))) {
brelse(bh2);
return -1;
}
btree = &anode->btree;
} else {
if (!(fnode = hpfs_map_fnode(s, node, &bh))) {
brelse(bh2);
return -1;
}
btree = &fnode->btree;
}
ranode->up = node;
memcpy(&ranode->btree, btree, btree->first_free);
if (fnod) ranode->btree.fnode_parent = 1;
ranode->btree.n_free_nodes = (ranode->btree.internal ? 60 : 40) - ranode->btree.n_used_nodes;
if (ranode->btree.internal) for (n = 0; n < ranode->btree.n_used_nodes; n++) {
struct anode *unode;
if ((unode = hpfs_map_anode(s, ranode->u.internal[n].down, &bh1))) {
unode->up = ra;
unode->btree.fnode_parent = 0;
mark_buffer_dirty(bh1);
brelse(bh1);
}
}
btree->internal = 1;
btree->n_free_nodes = fnod ? 10 : 58;
btree->n_used_nodes = 2;
btree->first_free = (char *)&btree->u.internal[2] - (char *)btree;
btree->u.internal[0].file_secno = fs;
btree->u.internal[0].down = ra;
btree->u.internal[1].file_secno = -1;
btree->u.internal[1].down = na;
mark_buffer_dirty(bh);
brelse(bh);
mark_buffer_dirty(bh2);
brelse(bh2);
return se;
}
/*
* Remove allocation tree. Recursion would look much nicer but
* I want to avoid it because it can cause stack overflow.
*/
void hpfs_remove_btree(struct super_block *s, struct bplus_header *btree)
{
struct bplus_header *btree1 = btree;
struct anode *anode = NULL;
anode_secno ano = 0, oano;
struct buffer_head *bh;
int level = 0;
int pos = 0;
int i;
int c1, c2 = 0;
int d1, d2;
go_down:
d2 = 0;
while (btree1->internal) {
ano = btree1->u.internal[pos].down;
if (level) brelse(bh);
if (hpfs_sb(s)->sb_chk)
if (hpfs_stop_cycles(s, ano, &d1, &d2, "hpfs_remove_btree #1"))
return;
if (!(anode = hpfs_map_anode(s, ano, &bh))) return;
btree1 = &anode->btree;
level++;
pos = 0;
}
for (i = 0; i < btree1->n_used_nodes; i++)
hpfs_free_sectors(s, btree1->u.external[i].disk_secno, btree1->u.external[i].length);
go_up:
if (!level) return;
brelse(bh);
if (hpfs_sb(s)->sb_chk)
if (hpfs_stop_cycles(s, ano, &c1, &c2, "hpfs_remove_btree #2")) return;
hpfs_free_sectors(s, ano, 1);
oano = ano;
ano = anode->up;
if (--level) {
if (!(anode = hpfs_map_anode(s, ano, &bh))) return;
btree1 = &anode->btree;
} else btree1 = btree;
for (i = 0; i < btree1->n_used_nodes; i++) {
if (btree1->u.internal[i].down == oano) {
if ((pos = i + 1) < btree1->n_used_nodes)
goto go_down;
else
goto go_up;
}
}
hpfs_error(s,
"reference to anode %08x not found in anode %08x "
"(probably bad up pointer)",
oano, level ? ano : -1);
if (level)
brelse(bh);
}
/* Just a wrapper around hpfs_bplus_lookup .. used for reading eas */
static secno anode_lookup(struct super_block *s, anode_secno a, unsigned sec)
{
struct anode *anode;
struct buffer_head *bh;
if (!(anode = hpfs_map_anode(s, a, &bh))) return -1;
return hpfs_bplus_lookup(s, NULL, &anode->btree, sec, bh);
}
int hpfs_ea_read(struct super_block *s, secno a, int ano, unsigned pos,
unsigned len, char *buf)
{
struct buffer_head *bh;
char *data;
secno sec;
unsigned l;
while (len) {
if (ano) {
if ((sec = anode_lookup(s, a, pos >> 9)) == -1)
return -1;
} else sec = a + (pos >> 9);
if (hpfs_sb(s)->sb_chk) if (hpfs_chk_sectors(s, sec, 1, "ea #1")) return -1;
if (!(data = hpfs_map_sector(s, sec, &bh, (len - 1) >> 9)))
return -1;
l = 0x200 - (pos & 0x1ff); if (l > len) l = len;
memcpy(buf, data + (pos & 0x1ff), l);
brelse(bh);
buf += l; pos += l; len -= l;
}
return 0;
}
int hpfs_ea_write(struct super_block *s, secno a, int ano, unsigned pos,
unsigned len, char *buf)
{
struct buffer_head *bh;
char *data;
secno sec;
unsigned l;
while (len) {
if (ano) {
if ((sec = anode_lookup(s, a, pos >> 9)) == -1)
return -1;
} else sec = a + (pos >> 9);
if (hpfs_sb(s)->sb_chk) if (hpfs_chk_sectors(s, sec, 1, "ea #2")) return -1;
if (!(data = hpfs_map_sector(s, sec, &bh, (len - 1) >> 9)))
return -1;
l = 0x200 - (pos & 0x1ff); if (l > len) l = len;
memcpy(data + (pos & 0x1ff), buf, l);
mark_buffer_dirty(bh);
brelse(bh);
buf += l; pos += l; len -= l;
}
return 0;
}
void hpfs_ea_remove(struct super_block *s, secno a, int ano, unsigned len)
{
struct anode *anode;
struct buffer_head *bh;
if (ano) {
if (!(anode = hpfs_map_anode(s, a, &bh))) return;
hpfs_remove_btree(s, &anode->btree);
brelse(bh);
hpfs_free_sectors(s, a, 1);
} else hpfs_free_sectors(s, a, (len + 511) >> 9);
}
/* Truncate allocation tree. Doesn't join anodes - I hope it doesn't matter */
void hpfs_truncate_btree(struct super_block *s, secno f, int fno, unsigned secs)
{
struct fnode *fnode;
struct anode *anode;
struct buffer_head *bh;
struct bplus_header *btree;
anode_secno node = f;
int i, j, nodes;
int c1, c2 = 0;
if (fno) {
if (!(fnode = hpfs_map_fnode(s, f, &bh))) return;
btree = &fnode->btree;
} else {
if (!(anode = hpfs_map_anode(s, f, &bh))) return;
btree = &anode->btree;
}
if (!secs) {
hpfs_remove_btree(s, btree);
if (fno) {
btree->n_free_nodes = 8;
btree->n_used_nodes = 0;
btree->first_free = 8;
btree->internal = 0;
mark_buffer_dirty(bh);
} else hpfs_free_sectors(s, f, 1);
brelse(bh);
return;
}
while (btree->internal) {
nodes = btree->n_used_nodes + btree->n_free_nodes;
for (i = 0; i < btree->n_used_nodes; i++)
if (btree->u.internal[i].file_secno >= secs) goto f;
brelse(bh);
hpfs_error(s, "internal btree %08x doesn't end with -1", node);
return;
f:
for (j = i + 1; j < btree->n_used_nodes; j++)
hpfs_ea_remove(s, btree->u.internal[j].down, 1, 0);
btree->n_used_nodes = i + 1;
btree->n_free_nodes = nodes - btree->n_used_nodes;
btree->first_free = 8 + 8 * btree->n_used_nodes;
mark_buffer_dirty(bh);
if (btree->u.internal[i].file_secno == secs) {
brelse(bh);
return;
}
node = btree->u.internal[i].down;
brelse(bh);
if (hpfs_sb(s)->sb_chk)
if (hpfs_stop_cycles(s, node, &c1, &c2, "hpfs_truncate_btree"))
return;
if (!(anode = hpfs_map_anode(s, node, &bh))) return;
btree = &anode->btree;
}
nodes = btree->n_used_nodes + btree->n_free_nodes;
for (i = 0; i < btree->n_used_nodes; i++)
if (btree->u.external[i].file_secno + btree->u.external[i].length >= secs) goto ff;
brelse(bh);
return;
ff:
if (secs <= btree->u.external[i].file_secno) {
hpfs_error(s, "there is an allocation error in file %08x, sector %08x", f, secs);
if (i) i--;
}
else if (btree->u.external[i].file_secno + btree->u.external[i].length > secs) {
hpfs_free_sectors(s, btree->u.external[i].disk_secno + secs -
btree->u.external[i].file_secno, btree->u.external[i].length
- secs + btree->u.external[i].file_secno); /* I hope gcc optimizes this :-) */
btree->u.external[i].length = secs - btree->u.external[i].file_secno;
}
for (j = i + 1; j < btree->n_used_nodes; j++)
hpfs_free_sectors(s, btree->u.external[j].disk_secno, btree->u.external[j].length);
btree->n_used_nodes = i + 1;
btree->n_free_nodes = nodes - btree->n_used_nodes;
btree->first_free = 8 + 12 * btree->n_used_nodes;
mark_buffer_dirty(bh);
brelse(bh);
}
/* Remove file or directory and it's eas - note that directory must
be empty when this is called. */
void hpfs_remove_fnode(struct super_block *s, fnode_secno fno)
{
struct buffer_head *bh;
struct fnode *fnode;
struct extended_attribute *ea;
struct extended_attribute *ea_end;
if (!(fnode = hpfs_map_fnode(s, fno, &bh))) return;
if (!fnode->dirflag) hpfs_remove_btree(s, &fnode->btree);
else hpfs_remove_dtree(s, fnode->u.external[0].disk_secno);
ea_end = fnode_end_ea(fnode);
for (ea = fnode_ea(fnode); ea < ea_end; ea = next_ea(ea))
if (ea->indirect)
hpfs_ea_remove(s, ea_sec(ea), ea->anode, ea_len(ea));
hpfs_ea_ext_remove(s, fnode->ea_secno, fnode->ea_anode, fnode->ea_size_l);
brelse(bh);
hpfs_free_sectors(s, fno, 1);
}

175
kernel/fs/hpfs/buffer.c Normal file
View File

@@ -0,0 +1,175 @@
/*
* linux/fs/hpfs/buffer.c
*
* Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
*
* general buffer i/o
*/
#include <linux/sched.h>
#include "hpfs_fn.h"
void hpfs_lock_creation(struct super_block *s)
{
#ifdef DEBUG_LOCKS
printk("lock creation\n");
#endif
down(&hpfs_sb(s)->hpfs_creation_de);
}
void hpfs_unlock_creation(struct super_block *s)
{
#ifdef DEBUG_LOCKS
printk("unlock creation\n");
#endif
up(&hpfs_sb(s)->hpfs_creation_de);
}
/* Map a sector into a buffer and return pointers to it and to the buffer. */
void *hpfs_map_sector(struct super_block *s, unsigned secno, struct buffer_head **bhp,
int ahead)
{
struct buffer_head *bh;
cond_resched();
*bhp = bh = sb_bread(s, secno);
if (bh != NULL)
return bh->b_data;
else {
printk("HPFS: hpfs_map_sector: read error\n");
return NULL;
}
}
/* Like hpfs_map_sector but don't read anything */
void *hpfs_get_sector(struct super_block *s, unsigned secno, struct buffer_head **bhp)
{
struct buffer_head *bh;
/*return hpfs_map_sector(s, secno, bhp, 0);*/
cond_resched();
if ((*bhp = bh = sb_getblk(s, secno)) != NULL) {
if (!buffer_uptodate(bh)) wait_on_buffer(bh);
set_buffer_uptodate(bh);
return bh->b_data;
} else {
printk("HPFS: hpfs_get_sector: getblk failed\n");
return NULL;
}
}
/* Map 4 sectors into a 4buffer and return pointers to it and to the buffer. */
void *hpfs_map_4sectors(struct super_block *s, unsigned secno, struct quad_buffer_head *qbh,
int ahead)
{
struct buffer_head *bh;
char *data;
cond_resched();
if (secno & 3) {
printk("HPFS: hpfs_map_4sectors: unaligned read\n");
return NULL;
}
qbh->data = data = kmalloc(2048, GFP_NOFS);
if (!data) {
printk("HPFS: hpfs_map_4sectors: out of memory\n");
goto bail;
}
qbh->bh[0] = bh = sb_bread(s, secno);
if (!bh)
goto bail0;
memcpy(data, bh->b_data, 512);
qbh->bh[1] = bh = sb_bread(s, secno + 1);
if (!bh)
goto bail1;
memcpy(data + 512, bh->b_data, 512);
qbh->bh[2] = bh = sb_bread(s, secno + 2);
if (!bh)
goto bail2;
memcpy(data + 2 * 512, bh->b_data, 512);
qbh->bh[3] = bh = sb_bread(s, secno + 3);
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(data);
printk("HPFS: hpfs_map_4sectors: read error\n");
bail:
return NULL;
}
/* Don't read sectors */
void *hpfs_get_4sectors(struct super_block *s, unsigned secno,
struct quad_buffer_head *qbh)
{
cond_resched();
if (secno & 3) {
printk("HPFS: hpfs_get_4sectors: unaligned read\n");
return NULL;
}
/*return hpfs_map_4sectors(s, secno, qbh, 0);*/
if (!(qbh->data = kmalloc(2048, GFP_NOFS))) {
printk("HPFS: hpfs_get_4sectors: out of memory\n");
return NULL;
}
if (!(hpfs_get_sector(s, secno, &qbh->bh[0]))) goto bail0;
if (!(hpfs_get_sector(s, secno + 1, &qbh->bh[1]))) goto bail1;
if (!(hpfs_get_sector(s, secno + 2, &qbh->bh[2]))) goto bail2;
if (!(hpfs_get_sector(s, secno + 3, &qbh->bh[3]))) goto bail3;
memcpy(qbh->data, qbh->bh[0]->b_data, 512);
memcpy(qbh->data + 512, qbh->bh[1]->b_data, 512);
memcpy(qbh->data + 2*512, qbh->bh[2]->b_data, 512);
memcpy(qbh->data + 3*512, qbh->bh[3]->b_data, 512);
return qbh->data;
bail3: brelse(qbh->bh[2]);
bail2: brelse(qbh->bh[1]);
bail1: brelse(qbh->bh[0]);
bail0:
return NULL;
}
void hpfs_brelse4(struct quad_buffer_head *qbh)
{
brelse(qbh->bh[3]);
brelse(qbh->bh[2]);
brelse(qbh->bh[1]);
brelse(qbh->bh[0]);
kfree(qbh->data);
}
void hpfs_mark_4buffers_dirty(struct quad_buffer_head *qbh)
{
PRINTK(("hpfs_mark_4buffers_dirty\n"));
memcpy(qbh->bh[0]->b_data, qbh->data, 512);
memcpy(qbh->bh[1]->b_data, qbh->data + 512, 512);
memcpy(qbh->bh[2]->b_data, qbh->data + 2 * 512, 512);
memcpy(qbh->bh[3]->b_data, qbh->data + 3 * 512, 512);
mark_buffer_dirty(qbh->bh[0]);
mark_buffer_dirty(qbh->bh[1]);
mark_buffer_dirty(qbh->bh[2]);
mark_buffer_dirty(qbh->bh[3]);
}

60
kernel/fs/hpfs/dentry.c Normal file
View File

@@ -0,0 +1,60 @@
/*
* linux/fs/hpfs/dentry.c
*
* Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
*
* dcache operations
*/
#include "hpfs_fn.h"
/*
* Note: the dentry argument is the parent dentry.
*/
static int hpfs_hash_dentry(struct dentry *dentry, struct qstr *qstr)
{
unsigned long hash;
int i;
unsigned l = qstr->len;
if (l == 1) if (qstr->name[0]=='.') goto x;
if (l == 2) if (qstr->name[0]=='.' || qstr->name[1]=='.') goto x;
hpfs_adjust_length((char *)qstr->name, &l);
/*if (hpfs_chk_name((char *)qstr->name,&l))*/
/*return -ENAMETOOLONG;*/
/*return -ENOENT;*/
x:
hash = init_name_hash();
for (i = 0; i < l; i++)
hash = partial_name_hash(hpfs_upcase(hpfs_sb(dentry->d_sb)->sb_cp_table,qstr->name[i]), hash);
qstr->hash = end_name_hash(hash);
return 0;
}
static int hpfs_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b)
{
unsigned al=a->len;
unsigned bl=b->len;
hpfs_adjust_length((char *)a->name, &al);
/*hpfs_adjust_length((char *)b->name, &bl);*/
/* 'a' is the qstr of an already existing dentry, so the name
* must be valid. 'b' must be validated first.
*/
if (hpfs_chk_name((char *)b->name, &bl)) return 1;
if (hpfs_compare_names(dentry->d_sb, (char *)a->name, al, (char *)b->name, bl, 0)) return 1;
return 0;
}
static const struct dentry_operations hpfs_dentry_operations = {
.d_hash = hpfs_hash_dentry,
.d_compare = hpfs_compare_dentry,
};
void hpfs_set_dentry_operations(struct dentry *dentry)
{
dentry->d_op = &hpfs_dentry_operations;
}

325
kernel/fs/hpfs/dir.c Normal file
View File

@@ -0,0 +1,325 @@
/*
* linux/fs/hpfs/dir.c
*
* Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
*
* directory VFS functions
*/
#include <linux/smp_lock.h>
#include "hpfs_fn.h"
static int hpfs_dir_release(struct inode *inode, struct file *filp)
{
lock_kernel();
hpfs_del_pos(inode, &filp->f_pos);
/*hpfs_write_if_changed(inode);*/
unlock_kernel();
return 0;
}
/* This is slow, but it's not used often */
static loff_t hpfs_dir_lseek(struct file *filp, loff_t off, int whence)
{
loff_t new_off = off + (whence == 1 ? filp->f_pos : 0);
loff_t pos;
struct quad_buffer_head qbh;
struct inode *i = filp->f_path.dentry->d_inode;
struct hpfs_inode_info *hpfs_inode = hpfs_i(i);
struct super_block *s = i->i_sb;
lock_kernel();
/*printk("dir lseek\n");*/
if (new_off == 0 || new_off == 1 || new_off == 11 || new_off == 12 || new_off == 13) goto ok;
mutex_lock(&i->i_mutex);
pos = ((loff_t) hpfs_de_as_down_as_possible(s, hpfs_inode->i_dno) << 4) + 1;
while (pos != new_off) {
if (map_pos_dirent(i, &pos, &qbh)) hpfs_brelse4(&qbh);
else goto fail;
if (pos == 12) goto fail;
}
mutex_unlock(&i->i_mutex);
ok:
unlock_kernel();
return filp->f_pos = new_off;
fail:
mutex_unlock(&i->i_mutex);
/*printk("illegal lseek: %016llx\n", new_off);*/
unlock_kernel();
return -ESPIPE;
}
static int hpfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
{
struct inode *inode = filp->f_path.dentry->d_inode;
struct hpfs_inode_info *hpfs_inode = hpfs_i(inode);
struct quad_buffer_head qbh;
struct hpfs_dirent *de;
int lc;
long old_pos;
char *tempname;
int c1, c2 = 0;
int ret = 0;
lock_kernel();
if (hpfs_sb(inode->i_sb)->sb_chk) {
if (hpfs_chk_sectors(inode->i_sb, inode->i_ino, 1, "dir_fnode")) {
ret = -EFSERROR;
goto out;
}
if (hpfs_chk_sectors(inode->i_sb, hpfs_inode->i_dno, 4, "dir_dnode")) {
ret = -EFSERROR;
goto out;
}
}
if (hpfs_sb(inode->i_sb)->sb_chk >= 2) {
struct buffer_head *bh;
struct fnode *fno;
int e = 0;
if (!(fno = hpfs_map_fnode(inode->i_sb, inode->i_ino, &bh))) {
ret = -EIOERROR;
goto out;
}
if (!fno->dirflag) {
e = 1;
hpfs_error(inode->i_sb, "not a directory, fnode %08lx",
(unsigned long)inode->i_ino);
}
if (hpfs_inode->i_dno != fno->u.external[0].disk_secno) {
e = 1;
hpfs_error(inode->i_sb, "corrupted inode: i_dno == %08x, fnode -> dnode == %08x", hpfs_inode->i_dno, fno->u.external[0].disk_secno);
}
brelse(bh);
if (e) {
ret = -EFSERROR;
goto out;
}
}
lc = hpfs_sb(inode->i_sb)->sb_lowercase;
if (filp->f_pos == 12) { /* diff -r requires this (note, that diff -r */
filp->f_pos = 13; /* also fails on msdos filesystem in 2.0) */
goto out;
}
if (filp->f_pos == 13) {
ret = -ENOENT;
goto out;
}
while (1) {
again:
/* This won't work when cycle is longer than number of dirents
accepted by filldir, but what can I do?
maybe killall -9 ls helps */
if (hpfs_sb(inode->i_sb)->sb_chk)
if (hpfs_stop_cycles(inode->i_sb, filp->f_pos, &c1, &c2, "hpfs_readdir")) {
ret = -EFSERROR;
goto out;
}
if (filp->f_pos == 12)
goto out;
if (filp->f_pos == 3 || filp->f_pos == 4 || filp->f_pos == 5) {
printk("HPFS: warning: pos==%d\n",(int)filp->f_pos);
goto out;
}
if (filp->f_pos == 0) {
if (filldir(dirent, ".", 1, filp->f_pos, inode->i_ino, DT_DIR) < 0)
goto out;
filp->f_pos = 11;
}
if (filp->f_pos == 11) {
if (filldir(dirent, "..", 2, filp->f_pos, hpfs_inode->i_parent_dir, DT_DIR) < 0)
goto out;
filp->f_pos = 1;
}
if (filp->f_pos == 1) {
filp->f_pos = ((loff_t) hpfs_de_as_down_as_possible(inode->i_sb, hpfs_inode->i_dno) << 4) + 1;
hpfs_add_pos(inode, &filp->f_pos);
filp->f_version = inode->i_version;
}
old_pos = filp->f_pos;
if (!(de = map_pos_dirent(inode, &filp->f_pos, &qbh))) {
ret = -EIOERROR;
goto out;
}
if (de->first || de->last) {
if (hpfs_sb(inode->i_sb)->sb_chk) {
if (de->first && !de->last && (de->namelen != 2
|| de ->name[0] != 1 || de->name[1] != 1))
hpfs_error(inode->i_sb, "hpfs_readdir: bad ^A^A entry; pos = %08lx", old_pos);
if (de->last && (de->namelen != 1 || de ->name[0] != 255))
hpfs_error(inode->i_sb, "hpfs_readdir: bad \\377 entry; pos = %08lx", old_pos);
}
hpfs_brelse4(&qbh);
goto again;
}
tempname = hpfs_translate_name(inode->i_sb, de->name, de->namelen, lc, de->not_8x3);
if (filldir(dirent, tempname, de->namelen, old_pos, de->fnode, DT_UNKNOWN) < 0) {
filp->f_pos = old_pos;
if (tempname != (char *)de->name) kfree(tempname);
hpfs_brelse4(&qbh);
goto out;
}
if (tempname != (char *)de->name) kfree(tempname);
hpfs_brelse4(&qbh);
}
out:
unlock_kernel();
return ret;
}
/*
* 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.)
*
* - M.P.: this is over, sometimes we've got to read file's fnode for eas
* inode numbers are just fnode sector numbers; iget lock is used
* to tell read_inode to read fnode or not.
*/
struct dentry *hpfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
{
const char *name = dentry->d_name.name;
unsigned len = dentry->d_name.len;
struct quad_buffer_head qbh;
struct hpfs_dirent *de;
ino_t ino;
int err;
struct inode *result = NULL;
struct hpfs_inode_info *hpfs_result;
lock_kernel();
if ((err = hpfs_chk_name((char *)name, &len))) {
if (err == -ENAMETOOLONG) {
unlock_kernel();
return ERR_PTR(-ENAMETOOLONG);
}
goto end_add;
}
/*
* '.' and '..' will never be passed here.
*/
de = map_dirent(dir, hpfs_i(dir)->i_dno, (char *) name, len, NULL, &qbh);
/*
* This is not really a bailout, just means file not found.
*/
if (!de) goto end;
/*
* Get inode number, what we're after.
*/
ino = de->fnode;
/*
* Go find or make an inode.
*/
result = iget_locked(dir->i_sb, ino);
if (!result) {
hpfs_error(dir->i_sb, "hpfs_lookup: can't get inode");
goto bail1;
}
if (result->i_state & I_NEW) {
hpfs_init_inode(result);
if (de->directory)
hpfs_read_inode(result);
else if (de->ea_size && hpfs_sb(dir->i_sb)->sb_eas)
hpfs_read_inode(result);
else {
result->i_mode |= S_IFREG;
result->i_mode &= ~0111;
result->i_op = &hpfs_file_iops;
result->i_fop = &hpfs_file_ops;
result->i_nlink = 1;
}
unlock_new_inode(result);
}
hpfs_result = hpfs_i(result);
if (!de->directory) hpfs_result->i_parent_dir = dir->i_ino;
hpfs_decide_conv(result, (char *)name, len);
if (de->has_acl || de->has_xtd_perm) if (!(dir->i_sb->s_flags & MS_RDONLY)) {
hpfs_error(result->i_sb, "ACLs or XPERM found. This is probably HPFS386. This driver doesn't support it now. Send me some info on these structures");
goto bail1;
}
/*
* Fill in the info from the directory if this is a newly created
* inode.
*/
if (!result->i_ctime.tv_sec) {
if (!(result->i_ctime.tv_sec = local_to_gmt(dir->i_sb, de->creation_date)))
result->i_ctime.tv_sec = 1;
result->i_ctime.tv_nsec = 0;
result->i_mtime.tv_sec = local_to_gmt(dir->i_sb, de->write_date);
result->i_mtime.tv_nsec = 0;
result->i_atime.tv_sec = local_to_gmt(dir->i_sb, de->read_date);
result->i_atime.tv_nsec = 0;
hpfs_result->i_ea_size = de->ea_size;
if (!hpfs_result->i_ea_mode && de->read_only)
result->i_mode &= ~0222;
if (!de->directory) {
if (result->i_size == -1) {
result->i_size = de->file_size;
result->i_data.a_ops = &hpfs_aops;
hpfs_i(result)->mmu_private = result->i_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.
*/
result->i_blocks = 1 + ((result->i_size + 511) >> 9);
}
}
}
hpfs_brelse4(&qbh);
/*
* Made it.
*/
end:
end_add:
hpfs_set_dentry_operations(dentry);
unlock_kernel();
d_add(dentry, result);
return NULL;
/*
* Didn't.
*/
bail1:
hpfs_brelse4(&qbh);
/*bail:*/
unlock_kernel();
return ERR_PTR(-ENOENT);
}
const struct file_operations hpfs_dir_ops =
{
.llseek = hpfs_dir_lseek,
.read = generic_read_dir,
.readdir = hpfs_readdir,
.release = hpfs_dir_release,
.fsync = hpfs_file_fsync,
};

1085
kernel/fs/hpfs/dnode.c Normal file

File diff suppressed because it is too large Load Diff

364
kernel/fs/hpfs/ea.c Normal file
View File

@@ -0,0 +1,364 @@
/*
* linux/fs/hpfs/ea.c
*
* Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
*
* handling extended attributes
*/
#include "hpfs_fn.h"
/* Remove external extended attributes. ano specifies whether a is a
direct sector where eas starts or an anode */
void hpfs_ea_ext_remove(struct super_block *s, secno a, int ano, unsigned len)
{
unsigned pos = 0;
while (pos < len) {
char ex[4 + 255 + 1 + 8];
struct extended_attribute *ea = (struct extended_attribute *)ex;
if (pos + 4 > len) {
hpfs_error(s, "EAs don't end correctly, %s %08x, len %08x",
ano ? "anode" : "sectors", a, len);
return;
}
if (hpfs_ea_read(s, a, ano, pos, 4, ex)) return;
if (ea->indirect) {
if (ea->valuelen != 8) {
hpfs_error(s, "ea->indirect set while ea->valuelen!=8, %s %08x, pos %08x",
ano ? "anode" : "sectors", a, pos);
return;
}
if (hpfs_ea_read(s, a, ano, pos + 4, ea->namelen + 9, ex+4))
return;
hpfs_ea_remove(s, ea_sec(ea), ea->anode, ea_len(ea));
}
pos += ea->namelen + ea->valuelen + 5;
}
if (!ano) hpfs_free_sectors(s, a, (len+511) >> 9);
else {
struct buffer_head *bh;
struct anode *anode;
if ((anode = hpfs_map_anode(s, a, &bh))) {
hpfs_remove_btree(s, &anode->btree);
brelse(bh);
hpfs_free_sectors(s, a, 1);
}
}
}
static char *get_indirect_ea(struct super_block *s, int ano, secno a, int size)
{
char *ret;
if (!(ret = kmalloc(size + 1, GFP_NOFS))) {
printk("HPFS: out of memory for EA\n");
return NULL;
}
if (hpfs_ea_read(s, a, ano, 0, size, ret)) {
kfree(ret);
return NULL;
}
ret[size] = 0;
return ret;
}
static void set_indirect_ea(struct super_block *s, int ano, secno a, char *data,
int size)
{
hpfs_ea_write(s, a, ano, 0, size, data);
}
/* Read an extended attribute named 'key' into the provided buffer */
int hpfs_read_ea(struct super_block *s, struct fnode *fnode, char *key,
char *buf, int size)
{
unsigned pos;
int ano, len;
secno a;
struct extended_attribute *ea;
struct extended_attribute *ea_end = fnode_end_ea(fnode);
for (ea = fnode_ea(fnode); ea < ea_end; ea = next_ea(ea))
if (!strcmp(ea->name, key)) {
if (ea->indirect)
goto indirect;
if (ea->valuelen >= size)
return -EINVAL;
memcpy(buf, ea_data(ea), ea->valuelen);
buf[ea->valuelen] = 0;
return 0;
}
a = fnode->ea_secno;
len = fnode->ea_size_l;
ano = fnode->ea_anode;
pos = 0;
while (pos < len) {
char ex[4 + 255 + 1 + 8];
ea = (struct extended_attribute *)ex;
if (pos + 4 > len) {
hpfs_error(s, "EAs don't end correctly, %s %08x, len %08x",
ano ? "anode" : "sectors", a, len);
return -EIO;
}
if (hpfs_ea_read(s, a, ano, pos, 4, ex)) return -EIO;
if (hpfs_ea_read(s, a, ano, pos + 4, ea->namelen + 1 + (ea->indirect ? 8 : 0), ex + 4))
return -EIO;
if (!strcmp(ea->name, key)) {
if (ea->indirect)
goto indirect;
if (ea->valuelen >= size)
return -EINVAL;
if (hpfs_ea_read(s, a, ano, pos + 4 + ea->namelen + 1, ea->valuelen, buf))
return -EIO;
buf[ea->valuelen] = 0;
return 0;
}
pos += ea->namelen + ea->valuelen + 5;
}
return -ENOENT;
indirect:
if (ea_len(ea) >= size)
return -EINVAL;
if (hpfs_ea_read(s, ea_sec(ea), ea->anode, 0, ea_len(ea), buf))
return -EIO;
buf[ea_len(ea)] = 0;
return 0;
}
/* Read an extended attribute named 'key' */
char *hpfs_get_ea(struct super_block *s, struct fnode *fnode, char *key, int *size)
{
char *ret;
unsigned pos;
int ano, len;
secno a;
struct extended_attribute *ea;
struct extended_attribute *ea_end = fnode_end_ea(fnode);
for (ea = fnode_ea(fnode); ea < ea_end; ea = next_ea(ea))
if (!strcmp(ea->name, key)) {
if (ea->indirect)
return get_indirect_ea(s, ea->anode, ea_sec(ea), *size = ea_len(ea));
if (!(ret = kmalloc((*size = ea->valuelen) + 1, GFP_NOFS))) {
printk("HPFS: out of memory for EA\n");
return NULL;
}
memcpy(ret, ea_data(ea), ea->valuelen);
ret[ea->valuelen] = 0;
return ret;
}
a = fnode->ea_secno;
len = fnode->ea_size_l;
ano = fnode->ea_anode;
pos = 0;
while (pos < len) {
char ex[4 + 255 + 1 + 8];
ea = (struct extended_attribute *)ex;
if (pos + 4 > len) {
hpfs_error(s, "EAs don't end correctly, %s %08x, len %08x",
ano ? "anode" : "sectors", a, len);
return NULL;
}
if (hpfs_ea_read(s, a, ano, pos, 4, ex)) return NULL;
if (hpfs_ea_read(s, a, ano, pos + 4, ea->namelen + 1 + (ea->indirect ? 8 : 0), ex + 4))
return NULL;
if (!strcmp(ea->name, key)) {
if (ea->indirect)
return get_indirect_ea(s, ea->anode, ea_sec(ea), *size = ea_len(ea));
if (!(ret = kmalloc((*size = ea->valuelen) + 1, GFP_NOFS))) {
printk("HPFS: out of memory for EA\n");
return NULL;
}
if (hpfs_ea_read(s, a, ano, pos + 4 + ea->namelen + 1, ea->valuelen, ret)) {
kfree(ret);
return NULL;
}
ret[ea->valuelen] = 0;
return ret;
}
pos += ea->namelen + ea->valuelen + 5;
}
return NULL;
}
/*
* Update or create extended attribute 'key' with value 'data'. Note that
* when this ea exists, it MUST have the same size as size of data.
* This driver can't change sizes of eas ('cause I just don't need it).
*/
void hpfs_set_ea(struct inode *inode, struct fnode *fnode, char *key, char *data, int size)
{
fnode_secno fno = inode->i_ino;
struct super_block *s = inode->i_sb;
unsigned pos;
int ano, len;
secno a;
unsigned char h[4];
struct extended_attribute *ea;
struct extended_attribute *ea_end = fnode_end_ea(fnode);
for (ea = fnode_ea(fnode); ea < ea_end; ea = next_ea(ea))
if (!strcmp(ea->name, key)) {
if (ea->indirect) {
if (ea_len(ea) == size)
set_indirect_ea(s, ea->anode, ea_sec(ea), data, size);
} else if (ea->valuelen == size) {
memcpy(ea_data(ea), data, size);
}
return;
}
a = fnode->ea_secno;
len = fnode->ea_size_l;
ano = fnode->ea_anode;
pos = 0;
while (pos < len) {
char ex[4 + 255 + 1 + 8];
ea = (struct extended_attribute *)ex;
if (pos + 4 > len) {
hpfs_error(s, "EAs don't end correctly, %s %08x, len %08x",
ano ? "anode" : "sectors", a, len);
return;
}
if (hpfs_ea_read(s, a, ano, pos, 4, ex)) return;
if (hpfs_ea_read(s, a, ano, pos + 4, ea->namelen + 1 + (ea->indirect ? 8 : 0), ex + 4))
return;
if (!strcmp(ea->name, key)) {
if (ea->indirect) {
if (ea_len(ea) == size)
set_indirect_ea(s, ea->anode, ea_sec(ea), data, size);
}
else {
if (ea->valuelen == size)
hpfs_ea_write(s, a, ano, pos + 4 + ea->namelen + 1, size, data);
}
return;
}
pos += ea->namelen + ea->valuelen + 5;
}
if (!fnode->ea_offs) {
/*if (fnode->ea_size_s) {
hpfs_error(s, "fnode %08x: ea_size_s == %03x, ea_offs == 0",
inode->i_ino, fnode->ea_size_s);
return;
}*/
fnode->ea_offs = 0xc4;
}
if (fnode->ea_offs < 0xc4 || fnode->ea_offs + fnode->acl_size_s + fnode->ea_size_s > 0x200) {
hpfs_error(s, "fnode %08lx: ea_offs == %03x, ea_size_s == %03x",
(unsigned long)inode->i_ino,
fnode->ea_offs, fnode->ea_size_s);
return;
}
if ((fnode->ea_size_s || !fnode->ea_size_l) &&
fnode->ea_offs + fnode->acl_size_s + fnode->ea_size_s + strlen(key) + size + 5 <= 0x200) {
ea = fnode_end_ea(fnode);
*(char *)ea = 0;
ea->namelen = strlen(key);
ea->valuelen = size;
strcpy(ea->name, key);
memcpy(ea_data(ea), data, size);
fnode->ea_size_s += strlen(key) + size + 5;
goto ret;
}
/* Most the code here is 99.9993422% unused. I hope there are no bugs.
But what .. HPFS.IFS has also bugs in ea management. */
if (fnode->ea_size_s && !fnode->ea_size_l) {
secno n;
struct buffer_head *bh;
char *data;
if (!(n = hpfs_alloc_sector(s, fno, 1, 0, 1))) return;
if (!(data = hpfs_get_sector(s, n, &bh))) {
hpfs_free_sectors(s, n, 1);
return;
}
memcpy(data, fnode_ea(fnode), fnode->ea_size_s);
fnode->ea_size_l = fnode->ea_size_s;
fnode->ea_size_s = 0;
fnode->ea_secno = n;
fnode->ea_anode = 0;
mark_buffer_dirty(bh);
brelse(bh);
}
pos = fnode->ea_size_l + 5 + strlen(key) + size;
len = (fnode->ea_size_l + 511) >> 9;
if (pos >= 30000) goto bail;
while (((pos + 511) >> 9) > len) {
if (!len) {
if (!(fnode->ea_secno = hpfs_alloc_sector(s, fno, 1, 0, 1)))
goto bail;
fnode->ea_anode = 0;
len++;
} else if (!fnode->ea_anode) {
if (hpfs_alloc_if_possible(s, fnode->ea_secno + len)) {
len++;
} else {
/* Aargh... don't know how to create ea anodes :-( */
/*struct buffer_head *bh;
struct anode *anode;
anode_secno a_s;
if (!(anode = hpfs_alloc_anode(s, fno, &a_s, &bh)))
goto bail;
anode->up = fno;
anode->btree.fnode_parent = 1;
anode->btree.n_free_nodes--;
anode->btree.n_used_nodes++;
anode->btree.first_free += 12;
anode->u.external[0].disk_secno = fnode->ea_secno;
anode->u.external[0].file_secno = 0;
anode->u.external[0].length = len;
mark_buffer_dirty(bh);
brelse(bh);
fnode->ea_anode = 1;
fnode->ea_secno = a_s;*/
secno new_sec;
int i;
if (!(new_sec = hpfs_alloc_sector(s, fno, 1, 1 - ((pos + 511) >> 9), 1)))
goto bail;
for (i = 0; i < len; i++) {
struct buffer_head *bh1, *bh2;
void *b1, *b2;
if (!(b1 = hpfs_map_sector(s, fnode->ea_secno + i, &bh1, len - i - 1))) {
hpfs_free_sectors(s, new_sec, (pos + 511) >> 9);
goto bail;
}
if (!(b2 = hpfs_get_sector(s, new_sec + i, &bh2))) {
brelse(bh1);
hpfs_free_sectors(s, new_sec, (pos + 511) >> 9);
goto bail;
}
memcpy(b2, b1, 512);
brelse(bh1);
mark_buffer_dirty(bh2);
brelse(bh2);
}
hpfs_free_sectors(s, fnode->ea_secno, len);
fnode->ea_secno = new_sec;
len = (pos + 511) >> 9;
}
}
if (fnode->ea_anode) {
if (hpfs_add_sector_to_btree(s, fnode->ea_secno,
0, len) != -1) {
len++;
} else {
goto bail;
}
}
}
h[0] = 0;
h[1] = strlen(key);
h[2] = size & 0xff;
h[3] = size >> 8;
if (hpfs_ea_write(s, fnode->ea_secno, fnode->ea_anode, fnode->ea_size_l, 4, h)) goto bail;
if (hpfs_ea_write(s, fnode->ea_secno, fnode->ea_anode, fnode->ea_size_l + 4, h[1] + 1, key)) goto bail;
if (hpfs_ea_write(s, fnode->ea_secno, fnode->ea_anode, fnode->ea_size_l + 5 + h[1], size, data)) goto bail;
fnode->ea_size_l = pos;
ret:
hpfs_i(inode)->i_ea_size += 5 + strlen(key) + size;
return;
bail:
if (fnode->ea_secno)
if (fnode->ea_anode) hpfs_truncate_btree(s, fnode->ea_secno, 1, (fnode->ea_size_l + 511) >> 9);
else hpfs_free_sectors(s, fnode->ea_secno + ((fnode->ea_size_l + 511) >> 9), len - ((fnode->ea_size_l + 511) >> 9));
else fnode->ea_secno = fnode->ea_size_l = 0;
}

148
kernel/fs/hpfs/file.c Normal file
View File

@@ -0,0 +1,148 @@
/*
* linux/fs/hpfs/file.c
*
* Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
*
* file VFS functions
*/
#include <linux/smp_lock.h>
#include "hpfs_fn.h"
#define BLOCKS(size) (((size) + 511) >> 9)
static int hpfs_file_release(struct inode *inode, struct file *file)
{
lock_kernel();
hpfs_write_if_changed(inode);
unlock_kernel();
return 0;
}
int hpfs_file_fsync(struct file *file, struct dentry *dentry, int datasync)
{
/*return file_fsync(file, dentry);*/
return 0; /* Don't fsync :-) */
}
/*
* generic_file_read often calls bmap with non-existing sector,
* so we must ignore such errors.
*/
static secno hpfs_bmap(struct inode *inode, unsigned file_secno)
{
struct hpfs_inode_info *hpfs_inode = hpfs_i(inode);
unsigned n, disk_secno;
struct fnode *fnode;
struct buffer_head *bh;
if (BLOCKS(hpfs_i(inode)->mmu_private) <= file_secno) return 0;
n = file_secno - hpfs_inode->i_file_sec;
if (n < hpfs_inode->i_n_secs) return hpfs_inode->i_disk_sec + n;
if (!(fnode = hpfs_map_fnode(inode->i_sb, inode->i_ino, &bh))) return 0;
disk_secno = hpfs_bplus_lookup(inode->i_sb, inode, &fnode->btree, file_secno, bh);
if (disk_secno == -1) return 0;
if (hpfs_chk_sectors(inode->i_sb, disk_secno, 1, "bmap")) return 0;
return disk_secno;
}
static void hpfs_truncate(struct inode *i)
{
if (IS_IMMUTABLE(i)) return /*-EPERM*/;
lock_kernel();
hpfs_i(i)->i_n_secs = 0;
i->i_blocks = 1 + ((i->i_size + 511) >> 9);
hpfs_i(i)->mmu_private = i->i_size;
hpfs_truncate_btree(i->i_sb, i->i_ino, 1, ((i->i_size + 511) >> 9));
hpfs_write_inode(i);
hpfs_i(i)->i_n_secs = 0;
unlock_kernel();
}
static int hpfs_get_block(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create)
{
secno s;
s = hpfs_bmap(inode, iblock);
if (s) {
map_bh(bh_result, inode->i_sb, s);
return 0;
}
if (!create) return 0;
if (iblock<<9 != hpfs_i(inode)->mmu_private) {
BUG();
return -EIO;
}
if ((s = hpfs_add_sector_to_btree(inode->i_sb, inode->i_ino, 1, inode->i_blocks - 1)) == -1) {
hpfs_truncate_btree(inode->i_sb, inode->i_ino, 1, inode->i_blocks - 1);
return -ENOSPC;
}
inode->i_blocks++;
hpfs_i(inode)->mmu_private += 512;
set_buffer_new(bh_result);
map_bh(bh_result, inode->i_sb, s);
return 0;
}
static int hpfs_writepage(struct page *page, struct writeback_control *wbc)
{
return block_write_full_page(page,hpfs_get_block, wbc);
}
static int hpfs_readpage(struct file *file, struct page *page)
{
return block_read_full_page(page,hpfs_get_block);
}
static int hpfs_write_begin(struct file *file, struct address_space *mapping,
loff_t pos, unsigned len, unsigned flags,
struct page **pagep, void **fsdata)
{
*pagep = NULL;
return cont_write_begin(file, mapping, pos, len, flags, pagep, fsdata,
hpfs_get_block,
&hpfs_i(mapping->host)->mmu_private);
}
static sector_t _hpfs_bmap(struct address_space *mapping, sector_t block)
{
return generic_block_bmap(mapping,block,hpfs_get_block);
}
const struct address_space_operations hpfs_aops = {
.readpage = hpfs_readpage,
.writepage = hpfs_writepage,
.sync_page = block_sync_page,
.write_begin = hpfs_write_begin,
.write_end = generic_write_end,
.bmap = _hpfs_bmap
};
static ssize_t hpfs_file_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
ssize_t retval;
retval = do_sync_write(file, buf, count, ppos);
if (retval > 0)
hpfs_i(file->f_path.dentry->d_inode)->i_dirty = 1;
return retval;
}
const struct file_operations hpfs_file_ops =
{
.llseek = generic_file_llseek,
.read = do_sync_read,
.aio_read = generic_file_aio_read,
.write = hpfs_file_write,
.aio_write = generic_file_aio_write,
.mmap = generic_file_mmap,
.release = hpfs_file_release,
.fsync = hpfs_file_fsync,
.splice_read = generic_file_splice_read,
};
const struct inode_operations hpfs_file_iops =
{
.truncate = hpfs_truncate,
.setattr = hpfs_setattr,
};

493
kernel/fs/hpfs/hpfs.h Normal file
View File

@@ -0,0 +1,493 @@
/*
* linux/fs/hpfs/hpfs.h
*
* HPFS structures by Chris Smith, 1993
*
* a little bit modified by Mikulas Patocka, 1998-1999
*/
/* 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 */
typedef u32 time32_t; /* 32-bit time_t type */
/* 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". */
#define BB_MAGIC 0xaa55
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.*/
char version; /* version of a filesystem usually 2 */
char funcversion; /* functional version - oldest version
of filesystem that can understand
this disk */
unsigned short int zero; /* 0 */
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 */
time32_t last_chkdsk; /* date last checked, 0 if never */
/*unsigned zero4;*/ /* 0 */
time32_t last_optimize; /* date last optimized, 0 if never */
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 */
char volume_name[32]; /* not used */
secno user_id_table; /* 8 preallocated sectors - user id */
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 sparedir_used: 1; /* spare dirblks used */
unsigned hotfixes_used: 1; /* hotfixes used */
unsigned bad_sector: 1; /* bad sector, corrupted disk (???) */
unsigned bad_bitmap: 1; /* bad bitmap */
unsigned fast: 1; /* partition was fast formatted */
unsigned old_wrote: 1; /* old version wrote to partion */
unsigned old_wrote_1: 1; /* old version wrote to partion (?) */
unsigned install_dasd_limits: 1; /* HPFS386 flags */
unsigned resynch_dasd_limits: 1;
unsigned dasd_limits_operational: 1;
unsigned multimedia_active: 1;
unsigned dce_acls_active: 1;
unsigned dasd_limits_dirty: 1;
unsigned flag67: 2;
unsigned char mm_contlgulty;
unsigned char unused;
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 super_crc; /* on HPFS386 and LAN Server this is
checksum of superblock, on normal
OS/2 unused */
unsigned spare_crc; /* on HPFS386 checksum of spareblock */
unsigned zero1[15]; /* unused */
dnode_secno spare_dnodes[100]; /* emergency free dnode list */
unsigned zero2[1]; /* 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 short index; /* index in c.p. array in that sector*/
unsigned short unknown; /* some unknown value; usually 0;
2 in Japanese version */
} 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 unknown; /* the same as in cp directory */
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 root_dnode:1; /* Is it root dnode? */
unsigned increment_me:31; /* 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 has_acl: 1;
unsigned down: 1; /* down pointer present (after name) */
unsigned last: 1; /* set on phony \377 entry */
unsigned has_ea: 1; /* entry has EA */
unsigned has_xtd_perm: 1; /* has extended perm list (???) */
unsigned has_explicit_acl: 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 */
time32_t write_date; /* mtime */
unsigned file_size; /* file length, bytes */
time32_t read_date; /* atime */
time32_t creation_date; /* ctime */
unsigned ea_size; /* total EA length, bytes */
unsigned char no_of_acls : 3; /* number of ACL's */
unsigned char reserver : 5;
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. */
};
/* 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 hbff: 1; /* high bit of first free entry offset */
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 binary_search: 1; /* suggest binary search (unused) */
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]; /* read history */
unsigned char len, name[15]; /* true length, truncated name */
fnode_secno up; /* pointer to file's directory fnode */
/*unsigned zero2[3];*/
secno acl_size_l;
secno acl_secno;
unsigned short acl_size_s;
char acl_anode;
char zero2; /* history bit count */
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 */
char user_id[16]; /* unused */
unsigned short ea_offs; /* offset from start of fnode
to first fnode-resident ea */
char dasd_limit_treshhold;
char dasd_limit_delta;
unsigned dasd_limit;
unsigned dasd_usage;
/*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[0];
/*
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.
*/
};
/*
Local Variables:
comment-column: 40
End:
*/

338
kernel/fs/hpfs/hpfs_fn.h Normal file
View File

@@ -0,0 +1,338 @@
/*
* linux/fs/hpfs/hpfs_fn.h
*
* Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
*
* function headers
*/
//#define DBG
//#define DEBUG_LOCKS
#include <linux/mutex.h>
#include <linux/pagemap.h>
#include <linux/buffer_head.h>
#include <linux/slab.h>
#include "hpfs.h"
#define EIOERROR EIO
#define EFSERROR EPERM
#define EMEMERROR ENOMEM
#define ANODE_ALLOC_FWD 512
#define FNODE_ALLOC_FWD 0
#define ALLOC_FWD_MIN 16
#define ALLOC_FWD_MAX 128
#define ALLOC_M 1
#define FNODE_RD_AHEAD 16
#define ANODE_RD_AHEAD 16
#define DNODE_RD_AHEAD 4
#define FREE_DNODES_ADD 58
#define FREE_DNODES_DEL 29
#define CHKCOND(x,y) if (!(x)) printk y
#ifdef DBG
#define PRINTK(x) printk x
#else
#undef PRINTK
#define PRINTK(x)
#endif
struct hpfs_inode_info {
loff_t mmu_private;
ino_t i_parent_dir; /* (directories) gives fnode of parent dir */
unsigned i_dno; /* (directories) root dnode */
unsigned i_dpos; /* (directories) temp for readdir */
unsigned i_dsubdno; /* (directories) temp for readdir */
unsigned i_file_sec; /* (files) minimalist cache of alloc info */
unsigned i_disk_sec; /* (files) minimalist cache of alloc info */
unsigned i_n_secs; /* (files) minimalist cache of alloc info */
unsigned i_ea_size; /* size of extended attributes */
unsigned i_conv : 2; /* (files) crlf->newline hackery */
unsigned i_ea_mode : 1; /* file's permission is stored in ea */
unsigned i_ea_uid : 1; /* file's uid is stored in ea */
unsigned i_ea_gid : 1; /* file's gid is stored in ea */
unsigned i_dirty : 1;
struct mutex i_mutex;
struct mutex i_parent_mutex;
loff_t **i_rddir_off;
struct inode vfs_inode;
};
struct hpfs_sb_info {
ino_t sb_root; /* inode number of root dir */
unsigned sb_fs_size; /* file system size, sectors */
unsigned sb_bitmaps; /* sector number of bitmap list */
unsigned sb_dirband_start; /* directory band start sector */
unsigned sb_dirband_size; /* directory band size, dnodes */
unsigned sb_dmap; /* sector number of dnode bit map */
unsigned sb_n_free; /* free blocks for statfs, or -1 */
unsigned sb_n_free_dnodes; /* free dnodes for statfs, or -1 */
uid_t sb_uid; /* uid from mount options */
gid_t sb_gid; /* gid from mount options */
umode_t sb_mode; /* mode from mount options */
unsigned sb_conv : 2; /* crlf->newline hackery */
unsigned sb_eas : 2; /* eas: 0-ignore, 1-ro, 2-rw */
unsigned sb_err : 2; /* on errs: 0-cont, 1-ro, 2-panic */
unsigned sb_chk : 2; /* checks: 0-no, 1-normal, 2-strict */
unsigned sb_lowercase : 1; /* downcase filenames hackery */
unsigned sb_was_error : 1; /* there was an error, set dirty flag */
unsigned sb_chkdsk : 2; /* chkdsk: 0-no, 1-on errs, 2-allways */
unsigned char *sb_cp_table; /* code page tables: */
/* 128 bytes uppercasing table & */
/* 128 bytes lowercasing table */
unsigned *sb_bmp_dir; /* main bitmap directory */
unsigned sb_c_bitmap; /* current bitmap */
unsigned sb_max_fwd_alloc; /* max forwad allocation */
struct semaphore hpfs_creation_de; /* when creating dirents, nobody else
can alloc blocks */
/*unsigned sb_mounting : 1;*/
int sb_timeshift;
};
/*
* conv= options
*/
#define CONV_BINARY 0 /* no conversion */
#define CONV_TEXT 1 /* crlf->newline */
#define CONV_AUTO 2 /* decide based on file contents */
/* Four 512-byte buffers and the 2k block obtained by concatenating them */
struct quad_buffer_head {
struct buffer_head *bh[4];
void *data;
};
/* The b-tree down pointer from a dir entry */
static inline dnode_secno de_down_pointer (struct hpfs_dirent *de)
{
CHKCOND(de->down,("HPFS: de_down_pointer: !de->down\n"));
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)
{
CHKCOND(dnode->first_free>=0x14 && dnode->first_free<=0xa00,("HPFS: dnode_end_de: dnode->first_free = %d\n",(int)dnode->first_free));
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)
{
CHKCOND(de->length>=0x20 && de->length<0x800,("HPFS: de_next_de: de->length = %d\n",(int)de->length));
return (void *) de + de->length;
}
static inline struct extended_attribute *fnode_ea(struct fnode *fnode)
{
return (struct extended_attribute *)((char *)fnode + fnode->ea_offs + fnode->acl_size_s);
}
static inline struct extended_attribute *fnode_end_ea(struct fnode *fnode)
{
return (struct extended_attribute *)((char *)fnode + fnode->ea_offs + fnode->acl_size_s + fnode->ea_size_s);
}
static inline struct extended_attribute *next_ea(struct extended_attribute *ea)
{
return (struct extended_attribute *)((char *)ea + 5 + ea->namelen + ea->valuelen);
}
static inline secno ea_sec(struct extended_attribute *ea)
{
return *(secno *)((char *)ea + 9 + ea->namelen);
}
static inline secno ea_len(struct extended_attribute *ea)
{
return *(secno *)((char *)ea + 5 + ea->namelen);
}
static inline char *ea_data(struct extended_attribute *ea)
{
return (char *)((char *)ea + 5 + ea->namelen);
}
static inline unsigned de_size(int namelen, secno down_ptr)
{
return ((0x1f + namelen + 3) & ~3) + (down_ptr ? 4 : 0);
}
static inline void copy_de(struct hpfs_dirent *dst, struct hpfs_dirent *src)
{
int a;
int n;
if (!dst || !src) return;
a = dst->down;
n = dst->not_8x3;
memcpy((char *)dst + 2, (char *)src + 2, 28);
dst->down = a;
dst->not_8x3 = n;
}
static inline unsigned tstbits(unsigned *bmp, unsigned b, unsigned n)
{
int i;
if ((b >= 0x4000) || (b + n - 1 >= 0x4000)) return n;
if (!((bmp[(b & 0x3fff) >> 5] >> (b & 0x1f)) & 1)) return 1;
for (i = 1; i < n; i++)
if (/*b+i < 0x4000 &&*/ !((bmp[((b+i) & 0x3fff) >> 5] >> ((b+i) & 0x1f)) & 1))
return i + 1;
return 0;
}
/* alloc.c */
int hpfs_chk_sectors(struct super_block *, secno, int, char *);
secno hpfs_alloc_sector(struct super_block *, secno, unsigned, int, int);
int hpfs_alloc_if_possible(struct super_block *, secno);
void hpfs_free_sectors(struct super_block *, secno, unsigned);
int hpfs_check_free_dnodes(struct super_block *, int);
void hpfs_free_dnode(struct super_block *, secno);
struct dnode *hpfs_alloc_dnode(struct super_block *, secno, dnode_secno *, struct quad_buffer_head *, int);
struct fnode *hpfs_alloc_fnode(struct super_block *, secno, fnode_secno *, struct buffer_head **);
struct anode *hpfs_alloc_anode(struct super_block *, secno, anode_secno *, struct buffer_head **);
/* anode.c */
secno hpfs_bplus_lookup(struct super_block *, struct inode *, struct bplus_header *, unsigned, struct buffer_head *);
secno hpfs_add_sector_to_btree(struct super_block *, secno, int, unsigned);
void hpfs_remove_btree(struct super_block *, struct bplus_header *);
int hpfs_ea_read(struct super_block *, secno, int, unsigned, unsigned, char *);
int hpfs_ea_write(struct super_block *, secno, int, unsigned, unsigned, char *);
void hpfs_ea_remove(struct super_block *, secno, int, unsigned);
void hpfs_truncate_btree(struct super_block *, secno, int, unsigned);
void hpfs_remove_fnode(struct super_block *, fnode_secno fno);
/* buffer.c */
void hpfs_lock_creation(struct super_block *);
void hpfs_unlock_creation(struct super_block *);
void *hpfs_map_sector(struct super_block *, unsigned, struct buffer_head **, int);
void *hpfs_get_sector(struct super_block *, unsigned, struct buffer_head **);
void *hpfs_map_4sectors(struct super_block *, unsigned, struct quad_buffer_head *, int);
void *hpfs_get_4sectors(struct super_block *, unsigned, struct quad_buffer_head *);
void hpfs_brelse4(struct quad_buffer_head *);
void hpfs_mark_4buffers_dirty(struct quad_buffer_head *);
/* dentry.c */
void hpfs_set_dentry_operations(struct dentry *);
/* dir.c */
struct dentry *hpfs_lookup(struct inode *, struct dentry *, struct nameidata *);
extern const struct file_operations hpfs_dir_ops;
/* dnode.c */
void hpfs_add_pos(struct inode *, loff_t *);
void hpfs_del_pos(struct inode *, loff_t *);
struct hpfs_dirent *hpfs_add_de(struct super_block *, struct dnode *, unsigned char *, unsigned, secno);
int hpfs_add_dirent(struct inode *, unsigned char *, unsigned, struct hpfs_dirent *, int);
int hpfs_remove_dirent(struct inode *, dnode_secno, struct hpfs_dirent *, struct quad_buffer_head *, int);
void hpfs_count_dnodes(struct super_block *, dnode_secno, int *, int *, int *);
dnode_secno hpfs_de_as_down_as_possible(struct super_block *, dnode_secno dno);
struct hpfs_dirent *map_pos_dirent(struct inode *, loff_t *, struct quad_buffer_head *);
struct hpfs_dirent *map_dirent(struct inode *, dnode_secno, char *, unsigned, dnode_secno *, struct quad_buffer_head *);
void hpfs_remove_dtree(struct super_block *, dnode_secno);
struct hpfs_dirent *map_fnode_dirent(struct super_block *, fnode_secno, struct fnode *, struct quad_buffer_head *);
/* ea.c */
void hpfs_ea_ext_remove(struct super_block *, secno, int, unsigned);
int hpfs_read_ea(struct super_block *, struct fnode *, char *, char *, int);
char *hpfs_get_ea(struct super_block *, struct fnode *, char *, int *);
void hpfs_set_ea(struct inode *, struct fnode *, char *, char *, int);
/* file.c */
int hpfs_file_fsync(struct file *, struct dentry *, int);
extern const struct file_operations hpfs_file_ops;
extern const struct inode_operations hpfs_file_iops;
extern const struct address_space_operations hpfs_aops;
/* inode.c */
void hpfs_init_inode(struct inode *);
void hpfs_read_inode(struct inode *);
void hpfs_write_inode(struct inode *);
void hpfs_write_inode_nolock(struct inode *);
int hpfs_setattr(struct dentry *, struct iattr *);
void hpfs_write_if_changed(struct inode *);
void hpfs_delete_inode(struct inode *);
/* map.c */
unsigned *hpfs_map_dnode_bitmap(struct super_block *, struct quad_buffer_head *);
unsigned *hpfs_map_bitmap(struct super_block *, unsigned, struct quad_buffer_head *, char *);
char *hpfs_load_code_page(struct super_block *, secno);
secno *hpfs_load_bitmap_directory(struct super_block *, secno bmp);
struct fnode *hpfs_map_fnode(struct super_block *s, ino_t, struct buffer_head **);
struct anode *hpfs_map_anode(struct super_block *s, anode_secno, struct buffer_head **);
struct dnode *hpfs_map_dnode(struct super_block *s, dnode_secno, struct quad_buffer_head *);
dnode_secno hpfs_fnode_dno(struct super_block *s, ino_t ino);
/* name.c */
unsigned char hpfs_upcase(unsigned char *, unsigned char);
int hpfs_chk_name(unsigned char *, unsigned *);
char *hpfs_translate_name(struct super_block *, unsigned char *, unsigned, int, int);
int hpfs_compare_names(struct super_block *, unsigned char *, unsigned, unsigned char *, unsigned, int);
int hpfs_is_name_long(unsigned char *, unsigned);
void hpfs_adjust_length(unsigned char *, unsigned *);
void hpfs_decide_conv(struct inode *, unsigned char *, unsigned);
/* namei.c */
extern const struct inode_operations hpfs_dir_iops;
extern const struct address_space_operations hpfs_symlink_aops;
static inline struct hpfs_inode_info *hpfs_i(struct inode *inode)
{
return list_entry(inode, struct hpfs_inode_info, vfs_inode);
}
static inline struct hpfs_sb_info *hpfs_sb(struct super_block *sb)
{
return sb->s_fs_info;
}
/* super.c */
void hpfs_error(struct super_block *, const char *, ...)
__attribute__((format (printf, 2, 3)));
int hpfs_stop_cycles(struct super_block *, int, int *, int *, char *);
unsigned hpfs_count_one_bitmap(struct super_block *, secno);
/*
* local time (HPFS) to GMT (Unix)
*/
static inline time_t local_to_gmt(struct super_block *s, time32_t t)
{
extern struct timezone sys_tz;
return t + sys_tz.tz_minuteswest * 60 + hpfs_sb(s)->sb_timeshift;
}
static inline time32_t gmt_to_local(struct super_block *s, time_t t)
{
extern struct timezone sys_tz;
return t - sys_tz.tz_minuteswest * 60 - hpfs_sb(s)->sb_timeshift;
}

305
kernel/fs/hpfs/inode.c Normal file
View File

@@ -0,0 +1,305 @@
/*
* linux/fs/hpfs/inode.c
*
* Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
*
* inode VFS functions
*/
#include <linux/smp_lock.h>
#include "hpfs_fn.h"
void hpfs_init_inode(struct inode *i)
{
struct super_block *sb = i->i_sb;
struct hpfs_inode_info *hpfs_inode = hpfs_i(i);
i->i_uid = hpfs_sb(sb)->sb_uid;
i->i_gid = hpfs_sb(sb)->sb_gid;
i->i_mode = hpfs_sb(sb)->sb_mode;
hpfs_inode->i_conv = hpfs_sb(sb)->sb_conv;
i->i_size = -1;
i->i_blocks = -1;
hpfs_inode->i_dno = 0;
hpfs_inode->i_n_secs = 0;
hpfs_inode->i_file_sec = 0;
hpfs_inode->i_disk_sec = 0;
hpfs_inode->i_dpos = 0;
hpfs_inode->i_dsubdno = 0;
hpfs_inode->i_ea_mode = 0;
hpfs_inode->i_ea_uid = 0;
hpfs_inode->i_ea_gid = 0;
hpfs_inode->i_ea_size = 0;
hpfs_inode->i_rddir_off = NULL;
hpfs_inode->i_dirty = 0;
i->i_ctime.tv_sec = i->i_ctime.tv_nsec = 0;
i->i_mtime.tv_sec = i->i_mtime.tv_nsec = 0;
i->i_atime.tv_sec = i->i_atime.tv_nsec = 0;
}
void hpfs_read_inode(struct inode *i)
{
struct buffer_head *bh;
struct fnode *fnode;
struct super_block *sb = i->i_sb;
struct hpfs_inode_info *hpfs_inode = hpfs_i(i);
unsigned char *ea;
int ea_size;
if (!(fnode = hpfs_map_fnode(sb, i->i_ino, &bh))) {
/*i->i_mode |= S_IFREG;
i->i_mode &= ~0111;
i->i_op = &hpfs_file_iops;
i->i_fop = &hpfs_file_ops;
i->i_nlink = 0;*/
make_bad_inode(i);
return;
}
if (hpfs_sb(i->i_sb)->sb_eas) {
if ((ea = hpfs_get_ea(i->i_sb, fnode, "UID", &ea_size))) {
if (ea_size == 2) {
i->i_uid = le16_to_cpu(*(__le16*)ea);
hpfs_inode->i_ea_uid = 1;
}
kfree(ea);
}
if ((ea = hpfs_get_ea(i->i_sb, fnode, "GID", &ea_size))) {
if (ea_size == 2) {
i->i_gid = le16_to_cpu(*(__le16*)ea);
hpfs_inode->i_ea_gid = 1;
}
kfree(ea);
}
if ((ea = hpfs_get_ea(i->i_sb, fnode, "SYMLINK", &ea_size))) {
kfree(ea);
i->i_mode = S_IFLNK | 0777;
i->i_op = &page_symlink_inode_operations;
i->i_data.a_ops = &hpfs_symlink_aops;
i->i_nlink = 1;
i->i_size = ea_size;
i->i_blocks = 1;
brelse(bh);
return;
}
if ((ea = hpfs_get_ea(i->i_sb, fnode, "MODE", &ea_size))) {
int rdev = 0;
umode_t mode = hpfs_sb(sb)->sb_mode;
if (ea_size == 2) {
mode = le16_to_cpu(*(__le16*)ea);
hpfs_inode->i_ea_mode = 1;
}
kfree(ea);
i->i_mode = mode;
if (S_ISBLK(mode) || S_ISCHR(mode)) {
if ((ea = hpfs_get_ea(i->i_sb, fnode, "DEV", &ea_size))) {
if (ea_size == 4)
rdev = le32_to_cpu(*(__le32*)ea);
kfree(ea);
}
}
if (S_ISBLK(mode) || S_ISCHR(mode) || S_ISFIFO(mode) || S_ISSOCK(mode)) {
brelse(bh);
i->i_nlink = 1;
i->i_size = 0;
i->i_blocks = 1;
init_special_inode(i, mode,
new_decode_dev(rdev));
return;
}
}
}
if (fnode->dirflag) {
unsigned n_dnodes, n_subdirs;
i->i_mode |= S_IFDIR;
i->i_op = &hpfs_dir_iops;
i->i_fop = &hpfs_dir_ops;
hpfs_inode->i_parent_dir = fnode->up;
hpfs_inode->i_dno = fnode->u.external[0].disk_secno;
if (hpfs_sb(sb)->sb_chk >= 2) {
struct buffer_head *bh0;
if (hpfs_map_fnode(sb, hpfs_inode->i_parent_dir, &bh0)) brelse(bh0);
}
n_dnodes = 0; n_subdirs = 0;
hpfs_count_dnodes(i->i_sb, hpfs_inode->i_dno, &n_dnodes, &n_subdirs, NULL);
i->i_blocks = 4 * n_dnodes;
i->i_size = 2048 * n_dnodes;
i->i_nlink = 2 + n_subdirs;
} else {
i->i_mode |= S_IFREG;
if (!hpfs_inode->i_ea_mode) i->i_mode &= ~0111;
i->i_op = &hpfs_file_iops;
i->i_fop = &hpfs_file_ops;
i->i_nlink = 1;
i->i_size = fnode->file_size;
i->i_blocks = ((i->i_size + 511) >> 9) + 1;
i->i_data.a_ops = &hpfs_aops;
hpfs_i(i)->mmu_private = i->i_size;
}
brelse(bh);
}
static void hpfs_write_inode_ea(struct inode *i, struct fnode *fnode)
{
struct hpfs_inode_info *hpfs_inode = hpfs_i(i);
/*if (fnode->acl_size_l || fnode->acl_size_s) {
Some unknown structures like ACL may be in fnode,
we'd better not overwrite them
hpfs_error(i->i_sb, "fnode %08x has some unknown HPFS386 stuctures", i->i_ino);
} else*/ if (hpfs_sb(i->i_sb)->sb_eas >= 2) {
__le32 ea;
if ((i->i_uid != hpfs_sb(i->i_sb)->sb_uid) || hpfs_inode->i_ea_uid) {
ea = cpu_to_le32(i->i_uid);
hpfs_set_ea(i, fnode, "UID", (char*)&ea, 2);
hpfs_inode->i_ea_uid = 1;
}
if ((i->i_gid != hpfs_sb(i->i_sb)->sb_gid) || hpfs_inode->i_ea_gid) {
ea = cpu_to_le32(i->i_gid);
hpfs_set_ea(i, fnode, "GID", (char *)&ea, 2);
hpfs_inode->i_ea_gid = 1;
}
if (!S_ISLNK(i->i_mode))
if ((i->i_mode != ((hpfs_sb(i->i_sb)->sb_mode & ~(S_ISDIR(i->i_mode) ? 0 : 0111))
| (S_ISDIR(i->i_mode) ? S_IFDIR : S_IFREG))
&& i->i_mode != ((hpfs_sb(i->i_sb)->sb_mode & ~(S_ISDIR(i->i_mode) ? 0222 : 0333))
| (S_ISDIR(i->i_mode) ? S_IFDIR : S_IFREG))) || hpfs_inode->i_ea_mode) {
ea = cpu_to_le32(i->i_mode);
/* sick, but legal */
hpfs_set_ea(i, fnode, "MODE", (char *)&ea, 2);
hpfs_inode->i_ea_mode = 1;
}
if (S_ISBLK(i->i_mode) || S_ISCHR(i->i_mode)) {
ea = cpu_to_le32(new_encode_dev(i->i_rdev));
hpfs_set_ea(i, fnode, "DEV", (char *)&ea, 4);
}
}
}
void hpfs_write_inode(struct inode *i)
{
struct hpfs_inode_info *hpfs_inode = hpfs_i(i);
struct inode *parent;
if (i->i_ino == hpfs_sb(i->i_sb)->sb_root) return;
if (hpfs_inode->i_rddir_off && !atomic_read(&i->i_count)) {
if (*hpfs_inode->i_rddir_off) printk("HPFS: write_inode: some position still there\n");
kfree(hpfs_inode->i_rddir_off);
hpfs_inode->i_rddir_off = NULL;
}
mutex_lock(&hpfs_inode->i_parent_mutex);
if (!i->i_nlink) {
mutex_unlock(&hpfs_inode->i_parent_mutex);
return;
}
parent = iget_locked(i->i_sb, hpfs_inode->i_parent_dir);
if (parent) {
hpfs_inode->i_dirty = 0;
if (parent->i_state & I_NEW) {
hpfs_init_inode(parent);
hpfs_read_inode(parent);
unlock_new_inode(parent);
}
mutex_lock(&hpfs_inode->i_mutex);
hpfs_write_inode_nolock(i);
mutex_unlock(&hpfs_inode->i_mutex);
iput(parent);
} else {
mark_inode_dirty(i);
}
mutex_unlock(&hpfs_inode->i_parent_mutex);
}
void hpfs_write_inode_nolock(struct inode *i)
{
struct hpfs_inode_info *hpfs_inode = hpfs_i(i);
struct buffer_head *bh;
struct fnode *fnode;
struct quad_buffer_head qbh;
struct hpfs_dirent *de;
if (i->i_ino == hpfs_sb(i->i_sb)->sb_root) return;
if (!(fnode = hpfs_map_fnode(i->i_sb, i->i_ino, &bh))) return;
if (i->i_ino != hpfs_sb(i->i_sb)->sb_root && i->i_nlink) {
if (!(de = map_fnode_dirent(i->i_sb, i->i_ino, fnode, &qbh))) {
brelse(bh);
return;
}
} else de = NULL;
if (S_ISREG(i->i_mode)) {
fnode->file_size = i->i_size;
if (de) de->file_size = i->i_size;
} else if (S_ISDIR(i->i_mode)) {
fnode->file_size = 0;
if (de) de->file_size = 0;
}
hpfs_write_inode_ea(i, fnode);
if (de) {
de->write_date = gmt_to_local(i->i_sb, i->i_mtime.tv_sec);
de->read_date = gmt_to_local(i->i_sb, i->i_atime.tv_sec);
de->creation_date = gmt_to_local(i->i_sb, i->i_ctime.tv_sec);
de->read_only = !(i->i_mode & 0222);
de->ea_size = hpfs_inode->i_ea_size;
hpfs_mark_4buffers_dirty(&qbh);
hpfs_brelse4(&qbh);
}
if (S_ISDIR(i->i_mode)) {
if ((de = map_dirent(i, hpfs_inode->i_dno, "\001\001", 2, NULL, &qbh))) {
de->write_date = gmt_to_local(i->i_sb, i->i_mtime.tv_sec);
de->read_date = gmt_to_local(i->i_sb, i->i_atime.tv_sec);
de->creation_date = gmt_to_local(i->i_sb, i->i_ctime.tv_sec);
de->read_only = !(i->i_mode & 0222);
de->ea_size = /*hpfs_inode->i_ea_size*/0;
de->file_size = 0;
hpfs_mark_4buffers_dirty(&qbh);
hpfs_brelse4(&qbh);
} else
hpfs_error(i->i_sb,
"directory %08lx doesn't have '.' entry",
(unsigned long)i->i_ino);
}
mark_buffer_dirty(bh);
brelse(bh);
}
int hpfs_setattr(struct dentry *dentry, struct iattr *attr)
{
struct inode *inode = dentry->d_inode;
int error = -EINVAL;
lock_kernel();
if (inode->i_ino == hpfs_sb(inode->i_sb)->sb_root)
goto out_unlock;
if ((attr->ia_valid & ATTR_SIZE) && attr->ia_size > inode->i_size)
goto out_unlock;
error = inode_change_ok(inode, attr);
if (error)
goto out_unlock;
error = inode_setattr(inode, attr);
if (error)
goto out_unlock;
hpfs_write_inode(inode);
out_unlock:
unlock_kernel();
return error;
}
void hpfs_write_if_changed(struct inode *inode)
{
struct hpfs_inode_info *hpfs_inode = hpfs_i(inode);
if (hpfs_inode->i_dirty)
hpfs_write_inode(inode);
}
void hpfs_delete_inode(struct inode *inode)
{
truncate_inode_pages(&inode->i_data, 0);
lock_kernel();
hpfs_remove_fnode(inode->i_sb, inode->i_ino);
unlock_kernel();
clear_inode(inode);
}

283
kernel/fs/hpfs/map.c Normal file
View File

@@ -0,0 +1,283 @@
/*
* linux/fs/hpfs/map.c
*
* Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
*
* mapping structures to memory with some minimal checks
*/
#include "hpfs_fn.h"
unsigned *hpfs_map_dnode_bitmap(struct super_block *s, struct quad_buffer_head *qbh)
{
return hpfs_map_4sectors(s, hpfs_sb(s)->sb_dmap, qbh, 0);
}
unsigned int *hpfs_map_bitmap(struct super_block *s, unsigned bmp_block,
struct quad_buffer_head *qbh, char *id)
{
secno sec;
if (hpfs_sb(s)->sb_chk) if (bmp_block * 16384 > hpfs_sb(s)->sb_fs_size) {
hpfs_error(s, "hpfs_map_bitmap called with bad parameter: %08x at %s", bmp_block, id);
return NULL;
}
sec = hpfs_sb(s)->sb_bmp_dir[bmp_block];
if (!sec || sec > hpfs_sb(s)->sb_fs_size-4) {
hpfs_error(s, "invalid bitmap block pointer %08x -> %08x at %s", bmp_block, sec, id);
return NULL;
}
return hpfs_map_4sectors(s, sec, qbh, 4);
}
/*
* Load first code page into kernel memory, return pointer to 256-byte array,
* first 128 bytes are uppercasing table for chars 128-255, next 128 bytes are
* lowercasing table
*/
char *hpfs_load_code_page(struct super_block *s, secno cps)
{
struct buffer_head *bh;
secno cpds;
unsigned cpi;
unsigned char *ptr;
unsigned char *cp_table;
int i;
struct code_page_data *cpd;
struct code_page_directory *cp = hpfs_map_sector(s, cps, &bh, 0);
if (!cp) return NULL;
if (cp->magic != CP_DIR_MAGIC) {
printk("HPFS: Code page directory magic doesn't match (magic = %08x)\n", cp->magic);
brelse(bh);
return NULL;
}
if (!cp->n_code_pages) {
printk("HPFS: n_code_pages == 0\n");
brelse(bh);
return NULL;
}
cpds = cp->array[0].code_page_data;
cpi = cp->array[0].index;
brelse(bh);
if (cpi >= 3) {
printk("HPFS: Code page index out of array\n");
return NULL;
}
if (!(cpd = hpfs_map_sector(s, cpds, &bh, 0))) return NULL;
if ((unsigned)cpd->offs[cpi] > 0x178) {
printk("HPFS: Code page index out of sector\n");
brelse(bh);
return NULL;
}
ptr = (char *)cpd + cpd->offs[cpi] + 6;
if (!(cp_table = kmalloc(256, GFP_KERNEL))) {
printk("HPFS: out of memory for code page table\n");
brelse(bh);
return NULL;
}
memcpy(cp_table, ptr, 128);
brelse(bh);
/* Try to build lowercasing table from uppercasing one */
for (i=128; i<256; i++) cp_table[i]=i;
for (i=128; i<256; i++) if (cp_table[i-128]!=i && cp_table[i-128]>=128)
cp_table[cp_table[i-128]] = i;
return cp_table;
}
secno *hpfs_load_bitmap_directory(struct super_block *s, secno bmp)
{
struct buffer_head *bh;
int n = (hpfs_sb(s)->sb_fs_size + 0x200000 - 1) >> 21;
int i;
secno *b;
if (!(b = kmalloc(n * 512, GFP_KERNEL))) {
printk("HPFS: can't allocate memory for bitmap directory\n");
return NULL;
}
for (i=0;i<n;i++) {
secno *d = hpfs_map_sector(s, bmp+i, &bh, n - i - 1);
if (!d) {
kfree(b);
return NULL;
}
memcpy((char *)b + 512 * i, d, 512);
brelse(bh);
}
return b;
}
/*
* Load fnode to memory
*/
struct fnode *hpfs_map_fnode(struct super_block *s, ino_t ino, struct buffer_head **bhp)
{
struct fnode *fnode;
if (hpfs_sb(s)->sb_chk) if (hpfs_chk_sectors(s, ino, 1, "fnode")) {
return NULL;
}
if ((fnode = hpfs_map_sector(s, ino, bhp, FNODE_RD_AHEAD))) {
if (hpfs_sb(s)->sb_chk) {
struct extended_attribute *ea;
struct extended_attribute *ea_end;
if (fnode->magic != FNODE_MAGIC) {
hpfs_error(s, "bad magic on fnode %08lx",
(unsigned long)ino);
goto bail;
}
if (!fnode->dirflag) {
if ((unsigned)fnode->btree.n_used_nodes + (unsigned)fnode->btree.n_free_nodes !=
(fnode->btree.internal ? 12 : 8)) {
hpfs_error(s,
"bad number of nodes in fnode %08lx",
(unsigned long)ino);
goto bail;
}
if (fnode->btree.first_free !=
8 + fnode->btree.n_used_nodes * (fnode->btree.internal ? 8 : 12)) {
hpfs_error(s,
"bad first_free pointer in fnode %08lx",
(unsigned long)ino);
goto bail;
}
}
if (fnode->ea_size_s && ((signed int)fnode->ea_offs < 0xc4 ||
(signed int)fnode->ea_offs + fnode->acl_size_s + fnode->ea_size_s > 0x200)) {
hpfs_error(s,
"bad EA info in fnode %08lx: ea_offs == %04x ea_size_s == %04x",
(unsigned long)ino,
fnode->ea_offs, fnode->ea_size_s);
goto bail;
}
ea = fnode_ea(fnode);
ea_end = fnode_end_ea(fnode);
while (ea != ea_end) {
if (ea > ea_end) {
hpfs_error(s, "bad EA in fnode %08lx",
(unsigned long)ino);
goto bail;
}
ea = next_ea(ea);
}
}
}
return fnode;
bail:
brelse(*bhp);
return NULL;
}
struct anode *hpfs_map_anode(struct super_block *s, anode_secno ano, struct buffer_head **bhp)
{
struct anode *anode;
if (hpfs_sb(s)->sb_chk) if (hpfs_chk_sectors(s, ano, 1, "anode")) return NULL;
if ((anode = hpfs_map_sector(s, ano, bhp, ANODE_RD_AHEAD)))
if (hpfs_sb(s)->sb_chk) {
if (anode->magic != ANODE_MAGIC || anode->self != ano) {
hpfs_error(s, "bad magic on anode %08x", ano);
goto bail;
}
if ((unsigned)anode->btree.n_used_nodes + (unsigned)anode->btree.n_free_nodes !=
(anode->btree.internal ? 60 : 40)) {
hpfs_error(s, "bad number of nodes in anode %08x", ano);
goto bail;
}
if (anode->btree.first_free !=
8 + anode->btree.n_used_nodes * (anode->btree.internal ? 8 : 12)) {
hpfs_error(s, "bad first_free pointer in anode %08x", ano);
goto bail;
}
}
return anode;
bail:
brelse(*bhp);
return NULL;
}
/*
* Load dnode to memory and do some checks
*/
struct dnode *hpfs_map_dnode(struct super_block *s, unsigned secno,
struct quad_buffer_head *qbh)
{
struct dnode *dnode;
if (hpfs_sb(s)->sb_chk) {
if (hpfs_chk_sectors(s, secno, 4, "dnode")) return NULL;
if (secno & 3) {
hpfs_error(s, "dnode %08x not byte-aligned", secno);
return NULL;
}
}
if ((dnode = hpfs_map_4sectors(s, secno, qbh, DNODE_RD_AHEAD)))
if (hpfs_sb(s)->sb_chk) {
unsigned p, pp = 0;
unsigned char *d = (char *)dnode;
int b = 0;
if (dnode->magic != DNODE_MAGIC) {
hpfs_error(s, "bad magic on dnode %08x", secno);
goto bail;
}
if (dnode->self != secno)
hpfs_error(s, "bad self pointer on dnode %08x self = %08x", secno, dnode->self);
/* Check dirents - bad dirents would cause infinite
loops or shooting to memory */
if (dnode->first_free > 2048/* || dnode->first_free < 84*/) {
hpfs_error(s, "dnode %08x has first_free == %08x", secno, dnode->first_free);
goto bail;
}
for (p = 20; p < dnode->first_free; p += d[p] + (d[p+1] << 8)) {
struct hpfs_dirent *de = (struct hpfs_dirent *)((char *)dnode + p);
if (de->length > 292 || (de->length < 32) || (de->length & 3) || p + de->length > 2048) {
hpfs_error(s, "bad dirent size in dnode %08x, dirent %03x, last %03x", secno, p, pp);
goto bail;
}
if (((31 + de->namelen + de->down*4 + 3) & ~3) != de->length) {
if (((31 + de->namelen + de->down*4 + 3) & ~3) < de->length && s->s_flags & MS_RDONLY) goto ok;
hpfs_error(s, "namelen does not match dirent size in dnode %08x, dirent %03x, last %03x", secno, p, pp);
goto bail;
}
ok:
if (hpfs_sb(s)->sb_chk >= 2) b |= 1 << de->down;
if (de->down) if (de_down_pointer(de) < 0x10) {
hpfs_error(s, "bad down pointer in dnode %08x, dirent %03x, last %03x", secno, p, pp);
goto bail;
}
pp = p;
}
if (p != dnode->first_free) {
hpfs_error(s, "size on last dirent does not match first_free; dnode %08x", secno);
goto bail;
}
if (d[pp + 30] != 1 || d[pp + 31] != 255) {
hpfs_error(s, "dnode %08x does not end with \\377 entry", secno);
goto bail;
}
if (b == 3) printk("HPFS: warning: unbalanced dnode tree, dnode %08x; see hpfs.txt 4 more info\n", secno);
}
return dnode;
bail:
hpfs_brelse4(qbh);
return NULL;
}
dnode_secno hpfs_fnode_dno(struct super_block *s, ino_t ino)
{
struct buffer_head *bh;
struct fnode *fnode;
dnode_secno dno;
fnode = hpfs_map_fnode(s, ino, &bh);
if (!fnode)
return 0;
dno = fnode->u.external[0].disk_secno;
brelse(bh);
return dno;
}

144
kernel/fs/hpfs/name.c Normal file
View File

@@ -0,0 +1,144 @@
/*
* linux/fs/hpfs/name.c
*
* Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
*
* operations with filenames
*/
#include "hpfs_fn.h"
static char *text_postfix[]={
".ASM", ".BAS", ".BAT", ".C", ".CC", ".CFG", ".CMD", ".CON", ".CPP", ".DEF",
".DOC", ".DPR", ".ERX", ".H", ".HPP", ".HTM", ".HTML", ".JAVA", ".LOG", ".PAS",
".RC", ".TEX", ".TXT", ".Y", ""};
static char *text_prefix[]={
"AUTOEXEC.", "CHANGES", "COPYING", "CONFIG.", "CREDITS", "FAQ", "FILE_ID.DIZ",
"MAKEFILE", "READ.ME", "README", "TERMCAP", ""};
void hpfs_decide_conv(struct inode *inode, unsigned char *name, unsigned len)
{
struct hpfs_inode_info *hpfs_inode = hpfs_i(inode);
int i;
if (hpfs_inode->i_conv != CONV_AUTO) return;
for (i = 0; *text_postfix[i]; i++) {
int l = strlen(text_postfix[i]);
if (l <= len)
if (!hpfs_compare_names(inode->i_sb, text_postfix[i], l, name + len - l, l, 0))
goto text;
}
for (i = 0; *text_prefix[i]; i++) {
int l = strlen(text_prefix[i]);
if (l <= len)
if (!hpfs_compare_names(inode->i_sb, text_prefix[i], l, name, l, 0))
goto text;
}
hpfs_inode->i_conv = CONV_BINARY;
return;
text:
hpfs_inode->i_conv = CONV_TEXT;
return;
}
static inline int not_allowed_char(unsigned char c)
{
return c<' ' || c=='"' || c=='*' || c=='/' || c==':' || c=='<' ||
c=='>' || c=='?' || c=='\\' || c=='|';
}
static inline int no_dos_char(unsigned char c)
{ /* Characters that are allowed in HPFS but not in DOS */
return c=='+' || c==',' || c==';' || c=='=' || c=='[' || c==']';
}
static inline unsigned char upcase(unsigned char *dir, unsigned char a)
{
if (a<128 || a==255) return a>='a' && a<='z' ? a - 0x20 : a;
if (!dir) return a;
return dir[a-128];
}
unsigned char hpfs_upcase(unsigned char *dir, unsigned char a)
{
return upcase(dir, a);
}
static inline unsigned char locase(unsigned char *dir, unsigned char a)
{
if (a<128 || a==255) return a>='A' && a<='Z' ? a + 0x20 : a;
if (!dir) return a;
return dir[a];
}
int hpfs_chk_name(unsigned char *name, unsigned *len)
{
int i;
if (*len > 254) return -ENAMETOOLONG;
hpfs_adjust_length(name, len);
if (!*len) return -EINVAL;
for (i = 0; i < *len; i++) if (not_allowed_char(name[i])) return -EINVAL;
if (*len == 1) if (name[0] == '.') return -EINVAL;
if (*len == 2) if (name[0] == '.' && name[1] == '.') return -EINVAL;
return 0;
}
char *hpfs_translate_name(struct super_block *s, unsigned char *from,
unsigned len, int lc, int lng)
{
char *to;
int i;
if (hpfs_sb(s)->sb_chk >= 2) if (hpfs_is_name_long(from, len) != lng) {
printk("HPFS: Long name flag mismatch - name ");
for (i=0; i<len; i++) printk("%c", from[i]);
printk(" misidentified as %s.\n", lng ? "short" : "long");
printk("HPFS: It's nothing serious. It could happen because of bug in OS/2.\nHPFS: Set checks=normal to disable this message.\n");
}
if (!lc) return from;
if (!(to = kmalloc(len, GFP_KERNEL))) {
printk("HPFS: can't allocate memory for name conversion buffer\n");
return from;
}
for (i = 0; i < len; i++) to[i] = locase(hpfs_sb(s)->sb_cp_table,from[i]);
return to;
}
int hpfs_compare_names(struct super_block *s, unsigned char *n1, unsigned l1,
unsigned char *n2, unsigned l2, int last)
{
unsigned l = l1 < l2 ? l1 : l2;
unsigned i;
if (last) return -1;
for (i = 0; i < l; i++) {
unsigned char c1 = upcase(hpfs_sb(s)->sb_cp_table,n1[i]);
unsigned char c2 = upcase(hpfs_sb(s)->sb_cp_table,n2[i]);
if (c1 < c2) return -1;
if (c1 > c2) return 1;
}
if (l1 < l2) return -1;
if (l1 > l2) return 1;
return 0;
}
int hpfs_is_name_long(unsigned char *name, unsigned len)
{
int i,j;
for (i = 0; i < len && name[i] != '.'; i++)
if (no_dos_char(name[i])) return 1;
if (!i || i > 8) return 1;
if (i == len) return 0;
for (j = i + 1; j < len; j++)
if (name[j] == '.' || no_dos_char(name[i])) return 1;
return j - i > 4;
}
/* OS/2 clears dots and spaces at the end of file name, so we have to */
void hpfs_adjust_length(unsigned char *name, unsigned *len)
{
if (!*len) return;
if (*len == 1 && name[0] == '.') return;
if (*len == 2 && name[0] == '.' && name[1] == '.') return;
while (*len && (name[*len - 1] == '.' || name[*len - 1] == ' '))
(*len)--;
}

674
kernel/fs/hpfs/namei.c Normal file
View File

@@ -0,0 +1,674 @@
/*
* linux/fs/hpfs/namei.c
*
* Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
*
* adding & removing files & directories
*/
#include <linux/sched.h>
#include <linux/smp_lock.h>
#include "hpfs_fn.h"
static int hpfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
{
const char *name = dentry->d_name.name;
unsigned len = dentry->d_name.len;
struct quad_buffer_head qbh0;
struct buffer_head *bh;
struct hpfs_dirent *de;
struct fnode *fnode;
struct dnode *dnode;
struct inode *result;
fnode_secno fno;
dnode_secno dno;
int r;
struct hpfs_dirent dee;
int err;
if ((err = hpfs_chk_name((char *)name, &len))) return err==-ENOENT ? -EINVAL : err;
lock_kernel();
err = -ENOSPC;
fnode = hpfs_alloc_fnode(dir->i_sb, hpfs_i(dir)->i_dno, &fno, &bh);
if (!fnode)
goto bail;
dnode = hpfs_alloc_dnode(dir->i_sb, fno, &dno, &qbh0, 1);
if (!dnode)
goto bail1;
memset(&dee, 0, sizeof dee);
dee.directory = 1;
if (!(mode & 0222)) dee.read_only = 1;
/*dee.archive = 0;*/
dee.hidden = name[0] == '.';
dee.fnode = fno;
dee.creation_date = dee.write_date = dee.read_date = gmt_to_local(dir->i_sb, get_seconds());
result = new_inode(dir->i_sb);
if (!result)
goto bail2;
hpfs_init_inode(result);
result->i_ino = fno;
hpfs_i(result)->i_parent_dir = dir->i_ino;
hpfs_i(result)->i_dno = dno;
result->i_ctime.tv_sec = result->i_mtime.tv_sec = result->i_atime.tv_sec = local_to_gmt(dir->i_sb, dee.creation_date);
result->i_ctime.tv_nsec = 0;
result->i_mtime.tv_nsec = 0;
result->i_atime.tv_nsec = 0;
hpfs_i(result)->i_ea_size = 0;
result->i_mode |= S_IFDIR;
result->i_op = &hpfs_dir_iops;
result->i_fop = &hpfs_dir_ops;
result->i_blocks = 4;
result->i_size = 2048;
result->i_nlink = 2;
if (dee.read_only)
result->i_mode &= ~0222;
mutex_lock(&hpfs_i(dir)->i_mutex);
r = hpfs_add_dirent(dir, (char *)name, len, &dee, 0);
if (r == 1)
goto bail3;
if (r == -1) {
err = -EEXIST;
goto bail3;
}
fnode->len = len;
memcpy(fnode->name, name, len > 15 ? 15 : len);
fnode->up = dir->i_ino;
fnode->dirflag = 1;
fnode->btree.n_free_nodes = 7;
fnode->btree.n_used_nodes = 1;
fnode->btree.first_free = 0x14;
fnode->u.external[0].disk_secno = dno;
fnode->u.external[0].file_secno = -1;
dnode->root_dnode = 1;
dnode->up = fno;
de = hpfs_add_de(dir->i_sb, dnode, "\001\001", 2, 0);
de->creation_date = de->write_date = de->read_date = gmt_to_local(dir->i_sb, get_seconds());
if (!(mode & 0222)) de->read_only = 1;
de->first = de->directory = 1;
/*de->hidden = de->system = 0;*/
de->fnode = fno;
mark_buffer_dirty(bh);
brelse(bh);
hpfs_mark_4buffers_dirty(&qbh0);
hpfs_brelse4(&qbh0);
inc_nlink(dir);
insert_inode_hash(result);
if (result->i_uid != current_fsuid() ||
result->i_gid != current_fsgid() ||
result->i_mode != (mode | S_IFDIR)) {
result->i_uid = current_fsuid();
result->i_gid = current_fsgid();
result->i_mode = mode | S_IFDIR;
hpfs_write_inode_nolock(result);
}
d_instantiate(dentry, result);
mutex_unlock(&hpfs_i(dir)->i_mutex);
unlock_kernel();
return 0;
bail3:
mutex_unlock(&hpfs_i(dir)->i_mutex);
iput(result);
bail2:
hpfs_brelse4(&qbh0);
hpfs_free_dnode(dir->i_sb, dno);
bail1:
brelse(bh);
hpfs_free_sectors(dir->i_sb, fno, 1);
bail:
unlock_kernel();
return err;
}
static int hpfs_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *nd)
{
const char *name = dentry->d_name.name;
unsigned len = dentry->d_name.len;
struct inode *result = NULL;
struct buffer_head *bh;
struct fnode *fnode;
fnode_secno fno;
int r;
struct hpfs_dirent dee;
int err;
if ((err = hpfs_chk_name((char *)name, &len)))
return err==-ENOENT ? -EINVAL : err;
lock_kernel();
err = -ENOSPC;
fnode = hpfs_alloc_fnode(dir->i_sb, hpfs_i(dir)->i_dno, &fno, &bh);
if (!fnode)
goto bail;
memset(&dee, 0, sizeof dee);
if (!(mode & 0222)) dee.read_only = 1;
dee.archive = 1;
dee.hidden = name[0] == '.';
dee.fnode = fno;
dee.creation_date = dee.write_date = dee.read_date = gmt_to_local(dir->i_sb, get_seconds());
result = new_inode(dir->i_sb);
if (!result)
goto bail1;
hpfs_init_inode(result);
result->i_ino = fno;
result->i_mode |= S_IFREG;
result->i_mode &= ~0111;
result->i_op = &hpfs_file_iops;
result->i_fop = &hpfs_file_ops;
result->i_nlink = 1;
hpfs_decide_conv(result, (char *)name, len);
hpfs_i(result)->i_parent_dir = dir->i_ino;
result->i_ctime.tv_sec = result->i_mtime.tv_sec = result->i_atime.tv_sec = local_to_gmt(dir->i_sb, dee.creation_date);
result->i_ctime.tv_nsec = 0;
result->i_mtime.tv_nsec = 0;
result->i_atime.tv_nsec = 0;
hpfs_i(result)->i_ea_size = 0;
if (dee.read_only)
result->i_mode &= ~0222;
result->i_blocks = 1;
result->i_size = 0;
result->i_data.a_ops = &hpfs_aops;
hpfs_i(result)->mmu_private = 0;
mutex_lock(&hpfs_i(dir)->i_mutex);
r = hpfs_add_dirent(dir, (char *)name, len, &dee, 0);
if (r == 1)
goto bail2;
if (r == -1) {
err = -EEXIST;
goto bail2;
}
fnode->len = len;
memcpy(fnode->name, name, len > 15 ? 15 : len);
fnode->up = dir->i_ino;
mark_buffer_dirty(bh);
brelse(bh);
insert_inode_hash(result);
if (result->i_uid != current_fsuid() ||
result->i_gid != current_fsgid() ||
result->i_mode != (mode | S_IFREG)) {
result->i_uid = current_fsuid();
result->i_gid = current_fsgid();
result->i_mode = mode | S_IFREG;
hpfs_write_inode_nolock(result);
}
d_instantiate(dentry, result);
mutex_unlock(&hpfs_i(dir)->i_mutex);
unlock_kernel();
return 0;
bail2:
mutex_unlock(&hpfs_i(dir)->i_mutex);
iput(result);
bail1:
brelse(bh);
hpfs_free_sectors(dir->i_sb, fno, 1);
bail:
unlock_kernel();
return err;
}
static int hpfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev)
{
const char *name = dentry->d_name.name;
unsigned len = dentry->d_name.len;
struct buffer_head *bh;
struct fnode *fnode;
fnode_secno fno;
int r;
struct hpfs_dirent dee;
struct inode *result = NULL;
int err;
if ((err = hpfs_chk_name((char *)name, &len))) return err==-ENOENT ? -EINVAL : err;
if (hpfs_sb(dir->i_sb)->sb_eas < 2) return -EPERM;
if (!new_valid_dev(rdev))
return -EINVAL;
lock_kernel();
err = -ENOSPC;
fnode = hpfs_alloc_fnode(dir->i_sb, hpfs_i(dir)->i_dno, &fno, &bh);
if (!fnode)
goto bail;
memset(&dee, 0, sizeof dee);
if (!(mode & 0222)) dee.read_only = 1;
dee.archive = 1;
dee.hidden = name[0] == '.';
dee.fnode = fno;
dee.creation_date = dee.write_date = dee.read_date = gmt_to_local(dir->i_sb, get_seconds());
result = new_inode(dir->i_sb);
if (!result)
goto bail1;
hpfs_init_inode(result);
result->i_ino = fno;
hpfs_i(result)->i_parent_dir = dir->i_ino;
result->i_ctime.tv_sec = result->i_mtime.tv_sec = result->i_atime.tv_sec = local_to_gmt(dir->i_sb, dee.creation_date);
result->i_ctime.tv_nsec = 0;
result->i_mtime.tv_nsec = 0;
result->i_atime.tv_nsec = 0;
hpfs_i(result)->i_ea_size = 0;
result->i_uid = current_fsuid();
result->i_gid = current_fsgid();
result->i_nlink = 1;
result->i_size = 0;
result->i_blocks = 1;
init_special_inode(result, mode, rdev);
mutex_lock(&hpfs_i(dir)->i_mutex);
r = hpfs_add_dirent(dir, (char *)name, len, &dee, 0);
if (r == 1)
goto bail2;
if (r == -1) {
err = -EEXIST;
goto bail2;
}
fnode->len = len;
memcpy(fnode->name, name, len > 15 ? 15 : len);
fnode->up = dir->i_ino;
mark_buffer_dirty(bh);
insert_inode_hash(result);
hpfs_write_inode_nolock(result);
d_instantiate(dentry, result);
mutex_unlock(&hpfs_i(dir)->i_mutex);
brelse(bh);
unlock_kernel();
return 0;
bail2:
mutex_unlock(&hpfs_i(dir)->i_mutex);
iput(result);
bail1:
brelse(bh);
hpfs_free_sectors(dir->i_sb, fno, 1);
bail:
unlock_kernel();
return err;
}
static int hpfs_symlink(struct inode *dir, struct dentry *dentry, const char *symlink)
{
const char *name = dentry->d_name.name;
unsigned len = dentry->d_name.len;
struct buffer_head *bh;
struct fnode *fnode;
fnode_secno fno;
int r;
struct hpfs_dirent dee;
struct inode *result;
int err;
if ((err = hpfs_chk_name((char *)name, &len))) return err==-ENOENT ? -EINVAL : err;
lock_kernel();
if (hpfs_sb(dir->i_sb)->sb_eas < 2) {
unlock_kernel();
return -EPERM;
}
err = -ENOSPC;
fnode = hpfs_alloc_fnode(dir->i_sb, hpfs_i(dir)->i_dno, &fno, &bh);
if (!fnode)
goto bail;
memset(&dee, 0, sizeof dee);
dee.archive = 1;
dee.hidden = name[0] == '.';
dee.fnode = fno;
dee.creation_date = dee.write_date = dee.read_date = gmt_to_local(dir->i_sb, get_seconds());
result = new_inode(dir->i_sb);
if (!result)
goto bail1;
result->i_ino = fno;
hpfs_init_inode(result);
hpfs_i(result)->i_parent_dir = dir->i_ino;
result->i_ctime.tv_sec = result->i_mtime.tv_sec = result->i_atime.tv_sec = local_to_gmt(dir->i_sb, dee.creation_date);
result->i_ctime.tv_nsec = 0;
result->i_mtime.tv_nsec = 0;
result->i_atime.tv_nsec = 0;
hpfs_i(result)->i_ea_size = 0;
result->i_mode = S_IFLNK | 0777;
result->i_uid = current_fsuid();
result->i_gid = current_fsgid();
result->i_blocks = 1;
result->i_nlink = 1;
result->i_size = strlen(symlink);
result->i_op = &page_symlink_inode_operations;
result->i_data.a_ops = &hpfs_symlink_aops;
mutex_lock(&hpfs_i(dir)->i_mutex);
r = hpfs_add_dirent(dir, (char *)name, len, &dee, 0);
if (r == 1)
goto bail2;
if (r == -1) {
err = -EEXIST;
goto bail2;
}
fnode->len = len;
memcpy(fnode->name, name, len > 15 ? 15 : len);
fnode->up = dir->i_ino;
hpfs_set_ea(result, fnode, "SYMLINK", (char *)symlink, strlen(symlink));
mark_buffer_dirty(bh);
brelse(bh);
insert_inode_hash(result);
hpfs_write_inode_nolock(result);
d_instantiate(dentry, result);
mutex_unlock(&hpfs_i(dir)->i_mutex);
unlock_kernel();
return 0;
bail2:
mutex_unlock(&hpfs_i(dir)->i_mutex);
iput(result);
bail1:
brelse(bh);
hpfs_free_sectors(dir->i_sb, fno, 1);
bail:
unlock_kernel();
return err;
}
static int hpfs_unlink(struct inode *dir, struct dentry *dentry)
{
const char *name = dentry->d_name.name;
unsigned len = dentry->d_name.len;
struct quad_buffer_head qbh;
struct hpfs_dirent *de;
struct inode *inode = dentry->d_inode;
dnode_secno dno;
fnode_secno fno;
int r;
int rep = 0;
int err;
lock_kernel();
hpfs_adjust_length((char *)name, &len);
again:
mutex_lock(&hpfs_i(inode)->i_parent_mutex);
mutex_lock(&hpfs_i(dir)->i_mutex);
err = -ENOENT;
de = map_dirent(dir, hpfs_i(dir)->i_dno, (char *)name, len, &dno, &qbh);
if (!de)
goto out;
err = -EPERM;
if (de->first)
goto out1;
err = -EISDIR;
if (de->directory)
goto out1;
fno = de->fnode;
r = hpfs_remove_dirent(dir, dno, de, &qbh, 1);
switch (r) {
case 1:
hpfs_error(dir->i_sb, "there was error when removing dirent");
err = -EFSERROR;
break;
case 2: /* no space for deleting, try to truncate file */
err = -ENOSPC;
if (rep++)
break;
mutex_unlock(&hpfs_i(dir)->i_mutex);
mutex_unlock(&hpfs_i(inode)->i_parent_mutex);
d_drop(dentry);
spin_lock(&dentry->d_lock);
if (atomic_read(&dentry->d_count) > 1 ||
generic_permission(inode, MAY_WRITE, NULL) ||
!S_ISREG(inode->i_mode) ||
get_write_access(inode)) {
spin_unlock(&dentry->d_lock);
d_rehash(dentry);
} else {
struct iattr newattrs;
spin_unlock(&dentry->d_lock);
/*printk("HPFS: truncating file before delete.\n");*/
newattrs.ia_size = 0;
newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME;
err = notify_change(dentry, &newattrs);
put_write_access(inode);
if (!err)
goto again;
}
unlock_kernel();
return -ENOSPC;
default:
drop_nlink(inode);
err = 0;
}
goto out;
out1:
hpfs_brelse4(&qbh);
out:
mutex_unlock(&hpfs_i(dir)->i_mutex);
mutex_unlock(&hpfs_i(inode)->i_parent_mutex);
unlock_kernel();
return err;
}
static int hpfs_rmdir(struct inode *dir, struct dentry *dentry)
{
const char *name = dentry->d_name.name;
unsigned len = dentry->d_name.len;
struct quad_buffer_head qbh;
struct hpfs_dirent *de;
struct inode *inode = dentry->d_inode;
dnode_secno dno;
fnode_secno fno;
int n_items = 0;
int err;
int r;
hpfs_adjust_length((char *)name, &len);
lock_kernel();
mutex_lock(&hpfs_i(inode)->i_parent_mutex);
mutex_lock(&hpfs_i(dir)->i_mutex);
err = -ENOENT;
de = map_dirent(dir, hpfs_i(dir)->i_dno, (char *)name, len, &dno, &qbh);
if (!de)
goto out;
err = -EPERM;
if (de->first)
goto out1;
err = -ENOTDIR;
if (!de->directory)
goto out1;
hpfs_count_dnodes(dir->i_sb, hpfs_i(inode)->i_dno, NULL, NULL, &n_items);
err = -ENOTEMPTY;
if (n_items)
goto out1;
fno = de->fnode;
r = hpfs_remove_dirent(dir, dno, de, &qbh, 1);
switch (r) {
case 1:
hpfs_error(dir->i_sb, "there was error when removing dirent");
err = -EFSERROR;
break;
case 2:
err = -ENOSPC;
break;
default:
drop_nlink(dir);
clear_nlink(inode);
err = 0;
}
goto out;
out1:
hpfs_brelse4(&qbh);
out:
mutex_unlock(&hpfs_i(dir)->i_mutex);
mutex_unlock(&hpfs_i(inode)->i_parent_mutex);
unlock_kernel();
return err;
}
static int hpfs_symlink_readpage(struct file *file, struct page *page)
{
char *link = kmap(page);
struct inode *i = page->mapping->host;
struct fnode *fnode;
struct buffer_head *bh;
int err;
err = -EIO;
lock_kernel();
if (!(fnode = hpfs_map_fnode(i->i_sb, i->i_ino, &bh)))
goto fail;
err = hpfs_read_ea(i->i_sb, fnode, "SYMLINK", link, PAGE_SIZE);
brelse(bh);
if (err)
goto fail;
unlock_kernel();
SetPageUptodate(page);
kunmap(page);
unlock_page(page);
return 0;
fail:
unlock_kernel();
SetPageError(page);
kunmap(page);
unlock_page(page);
return err;
}
const struct address_space_operations hpfs_symlink_aops = {
.readpage = hpfs_symlink_readpage
};
static int hpfs_rename(struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry)
{
char *old_name = (char *)old_dentry->d_name.name;
int old_len = old_dentry->d_name.len;
char *new_name = (char *)new_dentry->d_name.name;
int new_len = new_dentry->d_name.len;
struct inode *i = old_dentry->d_inode;
struct inode *new_inode = new_dentry->d_inode;
struct quad_buffer_head qbh, qbh1;
struct hpfs_dirent *dep, *nde;
struct hpfs_dirent de;
dnode_secno dno;
int r;
struct buffer_head *bh;
struct fnode *fnode;
int err;
if ((err = hpfs_chk_name((char *)new_name, &new_len))) return err;
err = 0;
hpfs_adjust_length((char *)old_name, &old_len);
lock_kernel();
/* order doesn't matter, due to VFS exclusion */
mutex_lock(&hpfs_i(i)->i_parent_mutex);
if (new_inode)
mutex_lock(&hpfs_i(new_inode)->i_parent_mutex);
mutex_lock(&hpfs_i(old_dir)->i_mutex);
if (new_dir != old_dir)
mutex_lock(&hpfs_i(new_dir)->i_mutex);
/* Erm? Moving over the empty non-busy directory is perfectly legal */
if (new_inode && S_ISDIR(new_inode->i_mode)) {
err = -EINVAL;
goto end1;
}
if (!(dep = map_dirent(old_dir, hpfs_i(old_dir)->i_dno, (char *)old_name, old_len, &dno, &qbh))) {
hpfs_error(i->i_sb, "lookup succeeded but map dirent failed");
err = -ENOENT;
goto end1;
}
copy_de(&de, dep);
de.hidden = new_name[0] == '.';
if (new_inode) {
int r;
if ((r = hpfs_remove_dirent(old_dir, dno, dep, &qbh, 1)) != 2) {
if ((nde = map_dirent(new_dir, hpfs_i(new_dir)->i_dno, (char *)new_name, new_len, NULL, &qbh1))) {
clear_nlink(new_inode);
copy_de(nde, &de);
memcpy(nde->name, new_name, new_len);
hpfs_mark_4buffers_dirty(&qbh1);
hpfs_brelse4(&qbh1);
goto end;
}
hpfs_error(new_dir->i_sb, "hpfs_rename: could not find dirent");
err = -EFSERROR;
goto end1;
}
err = r == 2 ? -ENOSPC : r == 1 ? -EFSERROR : 0;
goto end1;
}
if (new_dir == old_dir) hpfs_brelse4(&qbh);
hpfs_lock_creation(i->i_sb);
if ((r = hpfs_add_dirent(new_dir, new_name, new_len, &de, 1))) {
hpfs_unlock_creation(i->i_sb);
if (r == -1) hpfs_error(new_dir->i_sb, "hpfs_rename: dirent already exists!");
err = r == 1 ? -ENOSPC : -EFSERROR;
if (new_dir != old_dir) hpfs_brelse4(&qbh);
goto end1;
}
if (new_dir == old_dir)
if (!(dep = map_dirent(old_dir, hpfs_i(old_dir)->i_dno, (char *)old_name, old_len, &dno, &qbh))) {
hpfs_unlock_creation(i->i_sb);
hpfs_error(i->i_sb, "lookup succeeded but map dirent failed at #2");
err = -ENOENT;
goto end1;
}
if ((r = hpfs_remove_dirent(old_dir, dno, dep, &qbh, 0))) {
hpfs_unlock_creation(i->i_sb);
hpfs_error(i->i_sb, "hpfs_rename: could not remove dirent");
err = r == 2 ? -ENOSPC : -EFSERROR;
goto end1;
}
hpfs_unlock_creation(i->i_sb);
end:
hpfs_i(i)->i_parent_dir = new_dir->i_ino;
if (S_ISDIR(i->i_mode)) {
inc_nlink(new_dir);
drop_nlink(old_dir);
}
if ((fnode = hpfs_map_fnode(i->i_sb, i->i_ino, &bh))) {
fnode->up = new_dir->i_ino;
fnode->len = new_len;
memcpy(fnode->name, new_name, new_len>15?15:new_len);
if (new_len < 15) memset(&fnode->name[new_len], 0, 15 - new_len);
mark_buffer_dirty(bh);
brelse(bh);
}
hpfs_i(i)->i_conv = hpfs_sb(i->i_sb)->sb_conv;
hpfs_decide_conv(i, (char *)new_name, new_len);
end1:
if (old_dir != new_dir)
mutex_unlock(&hpfs_i(new_dir)->i_mutex);
mutex_unlock(&hpfs_i(old_dir)->i_mutex);
mutex_unlock(&hpfs_i(i)->i_parent_mutex);
if (new_inode)
mutex_unlock(&hpfs_i(new_inode)->i_parent_mutex);
unlock_kernel();
return err;
}
const struct inode_operations hpfs_dir_iops =
{
.create = hpfs_create,
.lookup = hpfs_lookup,
.unlink = hpfs_unlink,
.symlink = hpfs_symlink,
.mkdir = hpfs_mkdir,
.rmdir = hpfs_rmdir,
.mknod = hpfs_mknod,
.rename = hpfs_rename,
.setattr = hpfs_setattr,
};

721
kernel/fs/hpfs/super.c Normal file
View File

@@ -0,0 +1,721 @@
/*
* linux/fs/hpfs/super.c
*
* Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
*
* mounting, unmounting, error handling
*/
#include "hpfs_fn.h"
#include <linux/module.h>
#include <linux/parser.h>
#include <linux/init.h>
#include <linux/statfs.h>
#include <linux/magic.h>
#include <linux/sched.h>
#include <linux/smp_lock.h>
/* Mark the filesystem dirty, so that chkdsk checks it when os/2 booted */
static void mark_dirty(struct super_block *s)
{
if (hpfs_sb(s)->sb_chkdsk && !(s->s_flags & MS_RDONLY)) {
struct buffer_head *bh;
struct hpfs_spare_block *sb;
if ((sb = hpfs_map_sector(s, 17, &bh, 0))) {
sb->dirty = 1;
sb->old_wrote = 0;
mark_buffer_dirty(bh);
brelse(bh);
}
}
}
/* Mark the filesystem clean (mark it dirty for chkdsk if chkdsk==2 or if there
were errors) */
static void unmark_dirty(struct super_block *s)
{
struct buffer_head *bh;
struct hpfs_spare_block *sb;
if (s->s_flags & MS_RDONLY) return;
if ((sb = hpfs_map_sector(s, 17, &bh, 0))) {
sb->dirty = hpfs_sb(s)->sb_chkdsk > 1 - hpfs_sb(s)->sb_was_error;
sb->old_wrote = hpfs_sb(s)->sb_chkdsk >= 2 && !hpfs_sb(s)->sb_was_error;
mark_buffer_dirty(bh);
brelse(bh);
}
}
/* Filesystem error... */
static char err_buf[1024];
void hpfs_error(struct super_block *s, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
vsnprintf(err_buf, sizeof(err_buf), fmt, args);
va_end(args);
printk("HPFS: filesystem error: %s", err_buf);
if (!hpfs_sb(s)->sb_was_error) {
if (hpfs_sb(s)->sb_err == 2) {
printk("; crashing the system because you wanted it\n");
mark_dirty(s);
panic("HPFS panic");
} else if (hpfs_sb(s)->sb_err == 1) {
if (s->s_flags & MS_RDONLY) printk("; already mounted read-only\n");
else {
printk("; remounting read-only\n");
mark_dirty(s);
s->s_flags |= MS_RDONLY;
}
} else if (s->s_flags & MS_RDONLY) printk("; going on - but anything won't be destroyed because it's read-only\n");
else printk("; corrupted filesystem mounted read/write - your computer will explode within 20 seconds ... but you wanted it so!\n");
} else printk("\n");
hpfs_sb(s)->sb_was_error = 1;
}
/*
* A little trick to detect cycles in many hpfs structures and don't let the
* kernel crash on corrupted filesystem. When first called, set c2 to 0.
*
* BTW. chkdsk doesn't detect cycles correctly. When I had 2 lost directories
* nested each in other, chkdsk locked up happilly.
*/
int hpfs_stop_cycles(struct super_block *s, int key, int *c1, int *c2,
char *msg)
{
if (*c2 && *c1 == key) {
hpfs_error(s, "cycle detected on key %08x in %s", key, msg);
return 1;
}
(*c2)++;
if (!((*c2 - 1) & *c2)) *c1 = key;
return 0;
}
static void hpfs_put_super(struct super_block *s)
{
struct hpfs_sb_info *sbi = hpfs_sb(s);
lock_kernel();
kfree(sbi->sb_cp_table);
kfree(sbi->sb_bmp_dir);
unmark_dirty(s);
s->s_fs_info = NULL;
kfree(sbi);
unlock_kernel();
}
unsigned hpfs_count_one_bitmap(struct super_block *s, secno secno)
{
struct quad_buffer_head qbh;
unsigned *bits;
unsigned i, count;
if (!(bits = hpfs_map_4sectors(s, secno, &qbh, 4))) return 0;
count = 0;
for (i = 0; i < 2048 / sizeof(unsigned); i++) {
unsigned b;
if (!bits[i]) continue;
for (b = bits[i]; b; b>>=1) count += b & 1;
}
hpfs_brelse4(&qbh);
return count;
}
static unsigned count_bitmaps(struct super_block *s)
{
unsigned n, count, n_bands;
n_bands = (hpfs_sb(s)->sb_fs_size + 0x3fff) >> 14;
count = 0;
for (n = 0; n < n_bands; n++)
count += hpfs_count_one_bitmap(s, hpfs_sb(s)->sb_bmp_dir[n]);
return count;
}
static int hpfs_statfs(struct dentry *dentry, struct kstatfs *buf)
{
struct super_block *s = dentry->d_sb;
struct hpfs_sb_info *sbi = hpfs_sb(s);
u64 id = huge_encode_dev(s->s_bdev->bd_dev);
lock_kernel();
/*if (sbi->sb_n_free == -1) {*/
sbi->sb_n_free = count_bitmaps(s);
sbi->sb_n_free_dnodes = hpfs_count_one_bitmap(s, sbi->sb_dmap);
/*}*/
buf->f_type = s->s_magic;
buf->f_bsize = 512;
buf->f_blocks = sbi->sb_fs_size;
buf->f_bfree = sbi->sb_n_free;
buf->f_bavail = sbi->sb_n_free;
buf->f_files = sbi->sb_dirband_size / 4;
buf->f_ffree = sbi->sb_n_free_dnodes;
buf->f_fsid.val[0] = (u32)id;
buf->f_fsid.val[1] = (u32)(id >> 32);
buf->f_namelen = 254;
unlock_kernel();
return 0;
}
static struct kmem_cache * hpfs_inode_cachep;
static struct inode *hpfs_alloc_inode(struct super_block *sb)
{
struct hpfs_inode_info *ei;
ei = (struct hpfs_inode_info *)kmem_cache_alloc(hpfs_inode_cachep, GFP_NOFS);
if (!ei)
return NULL;
ei->vfs_inode.i_version = 1;
return &ei->vfs_inode;
}
static void hpfs_destroy_inode(struct inode *inode)
{
kmem_cache_free(hpfs_inode_cachep, hpfs_i(inode));
}
static void init_once(void *foo)
{
struct hpfs_inode_info *ei = (struct hpfs_inode_info *) foo;
mutex_init(&ei->i_mutex);
mutex_init(&ei->i_parent_mutex);
inode_init_once(&ei->vfs_inode);
}
static int init_inodecache(void)
{
hpfs_inode_cachep = kmem_cache_create("hpfs_inode_cache",
sizeof(struct hpfs_inode_info),
0, (SLAB_RECLAIM_ACCOUNT|
SLAB_MEM_SPREAD),
init_once);
if (hpfs_inode_cachep == NULL)
return -ENOMEM;
return 0;
}
static void destroy_inodecache(void)
{
kmem_cache_destroy(hpfs_inode_cachep);
}
/*
* A tiny parser for option strings, stolen from dosfs.
* Stolen again from read-only hpfs.
* And updated for table-driven option parsing.
*/
enum {
Opt_help, Opt_uid, Opt_gid, Opt_umask, Opt_case_lower, Opt_case_asis,
Opt_conv_binary, Opt_conv_text, Opt_conv_auto,
Opt_check_none, Opt_check_normal, Opt_check_strict,
Opt_err_cont, Opt_err_ro, Opt_err_panic,
Opt_eas_no, Opt_eas_ro, Opt_eas_rw,
Opt_chkdsk_no, Opt_chkdsk_errors, Opt_chkdsk_always,
Opt_timeshift, Opt_err,
};
static const match_table_t tokens = {
{Opt_help, "help"},
{Opt_uid, "uid=%u"},
{Opt_gid, "gid=%u"},
{Opt_umask, "umask=%o"},
{Opt_case_lower, "case=lower"},
{Opt_case_asis, "case=asis"},
{Opt_conv_binary, "conv=binary"},
{Opt_conv_text, "conv=text"},
{Opt_conv_auto, "conv=auto"},
{Opt_check_none, "check=none"},
{Opt_check_normal, "check=normal"},
{Opt_check_strict, "check=strict"},
{Opt_err_cont, "errors=continue"},
{Opt_err_ro, "errors=remount-ro"},
{Opt_err_panic, "errors=panic"},
{Opt_eas_no, "eas=no"},
{Opt_eas_ro, "eas=ro"},
{Opt_eas_rw, "eas=rw"},
{Opt_chkdsk_no, "chkdsk=no"},
{Opt_chkdsk_errors, "chkdsk=errors"},
{Opt_chkdsk_always, "chkdsk=always"},
{Opt_timeshift, "timeshift=%d"},
{Opt_err, NULL},
};
static int parse_opts(char *opts, uid_t *uid, gid_t *gid, umode_t *umask,
int *lowercase, int *conv, int *eas, int *chk, int *errs,
int *chkdsk, int *timeshift)
{
char *p;
int option;
if (!opts)
return 1;
/*printk("Parsing opts: '%s'\n",opts);*/
while ((p = strsep(&opts, ",")) != NULL) {
substring_t args[MAX_OPT_ARGS];
int token;
if (!*p)
continue;
token = match_token(p, tokens, args);
switch (token) {
case Opt_help:
return 2;
case Opt_uid:
if (match_int(args, &option))
return 0;
*uid = option;
break;
case Opt_gid:
if (match_int(args, &option))
return 0;
*gid = option;
break;
case Opt_umask:
if (match_octal(args, &option))
return 0;
*umask = option;
break;
case Opt_case_lower:
*lowercase = 1;
break;
case Opt_case_asis:
*lowercase = 0;
break;
case Opt_conv_binary:
*conv = CONV_BINARY;
break;
case Opt_conv_text:
*conv = CONV_TEXT;
break;
case Opt_conv_auto:
*conv = CONV_AUTO;
break;
case Opt_check_none:
*chk = 0;
break;
case Opt_check_normal:
*chk = 1;
break;
case Opt_check_strict:
*chk = 2;
break;
case Opt_err_cont:
*errs = 0;
break;
case Opt_err_ro:
*errs = 1;
break;
case Opt_err_panic:
*errs = 2;
break;
case Opt_eas_no:
*eas = 0;
break;
case Opt_eas_ro:
*eas = 1;
break;
case Opt_eas_rw:
*eas = 2;
break;
case Opt_chkdsk_no:
*chkdsk = 0;
break;
case Opt_chkdsk_errors:
*chkdsk = 1;
break;
case Opt_chkdsk_always:
*chkdsk = 2;
break;
case Opt_timeshift:
{
int m = 1;
char *rhs = args[0].from;
if (!rhs || !*rhs)
return 0;
if (*rhs == '-') m = -1;
if (*rhs == '+' || *rhs == '-') rhs++;
*timeshift = simple_strtoul(rhs, &rhs, 0) * m;
if (*rhs)
return 0;
break;
}
default:
return 0;
}
}
return 1;
}
static inline void hpfs_help(void)
{
printk("\n\
HPFS filesystem options:\n\
help do not mount and display this text\n\
uid=xxx set uid of files that don't have uid specified in eas\n\
gid=xxx set gid of files that don't have gid specified in eas\n\
umask=xxx set mode of files that don't have mode specified in eas\n\
case=lower lowercase all files\n\
case=asis do not lowercase files (default)\n\
conv=binary do not convert CR/LF -> LF (default)\n\
conv=auto convert only files with known text extensions\n\
conv=text convert all files\n\
check=none no fs checks - kernel may crash on corrupted filesystem\n\
check=normal do some checks - it should not crash (default)\n\
check=strict do extra time-consuming checks, used for debugging\n\
errors=continue continue on errors\n\
errors=remount-ro remount read-only if errors found (default)\n\
errors=panic panic on errors\n\
chkdsk=no do not mark fs for chkdsking even if there were errors\n\
chkdsk=errors mark fs dirty if errors found (default)\n\
chkdsk=always always mark fs dirty - used for debugging\n\
eas=no ignore extended attributes\n\
eas=ro read but do not write extended attributes\n\
eas=rw r/w eas => enables chmod, chown, mknod, ln -s (default)\n\
timeshift=nnn add nnn seconds to file times\n\
\n");
}
static int hpfs_remount_fs(struct super_block *s, int *flags, char *data)
{
uid_t uid;
gid_t gid;
umode_t umask;
int lowercase, conv, eas, chk, errs, chkdsk, timeshift;
int o;
struct hpfs_sb_info *sbi = hpfs_sb(s);
char *new_opts = kstrdup(data, GFP_KERNEL);
*flags |= MS_NOATIME;
lock_kernel();
lock_super(s);
uid = sbi->sb_uid; gid = sbi->sb_gid;
umask = 0777 & ~sbi->sb_mode;
lowercase = sbi->sb_lowercase; conv = sbi->sb_conv;
eas = sbi->sb_eas; chk = sbi->sb_chk; chkdsk = sbi->sb_chkdsk;
errs = sbi->sb_err; timeshift = sbi->sb_timeshift;
if (!(o = parse_opts(data, &uid, &gid, &umask, &lowercase, &conv,
&eas, &chk, &errs, &chkdsk, &timeshift))) {
printk("HPFS: bad mount options.\n");
goto out_err;
}
if (o == 2) {
hpfs_help();
goto out_err;
}
if (timeshift != sbi->sb_timeshift) {
printk("HPFS: timeshift can't be changed using remount.\n");
goto out_err;
}
unmark_dirty(s);
sbi->sb_uid = uid; sbi->sb_gid = gid;
sbi->sb_mode = 0777 & ~umask;
sbi->sb_lowercase = lowercase; sbi->sb_conv = conv;
sbi->sb_eas = eas; sbi->sb_chk = chk; sbi->sb_chkdsk = chkdsk;
sbi->sb_err = errs; sbi->sb_timeshift = timeshift;
if (!(*flags & MS_RDONLY)) mark_dirty(s);
replace_mount_options(s, new_opts);
unlock_super(s);
unlock_kernel();
return 0;
out_err:
unlock_super(s);
unlock_kernel();
kfree(new_opts);
return -EINVAL;
}
/* Super operations */
static const struct super_operations hpfs_sops =
{
.alloc_inode = hpfs_alloc_inode,
.destroy_inode = hpfs_destroy_inode,
.delete_inode = hpfs_delete_inode,
.put_super = hpfs_put_super,
.statfs = hpfs_statfs,
.remount_fs = hpfs_remount_fs,
.show_options = generic_show_options,
};
static int hpfs_fill_super(struct super_block *s, void *options, int silent)
{
struct buffer_head *bh0, *bh1, *bh2;
struct hpfs_boot_block *bootblock;
struct hpfs_super_block *superblock;
struct hpfs_spare_block *spareblock;
struct hpfs_sb_info *sbi;
struct inode *root;
uid_t uid;
gid_t gid;
umode_t umask;
int lowercase, conv, eas, chk, errs, chkdsk, timeshift;
dnode_secno root_dno;
struct hpfs_dirent *de = NULL;
struct quad_buffer_head qbh;
int o;
save_mount_options(s, options);
sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);
if (!sbi)
return -ENOMEM;
s->s_fs_info = sbi;
sbi->sb_bmp_dir = NULL;
sbi->sb_cp_table = NULL;
init_MUTEX(&sbi->hpfs_creation_de);
uid = current_uid();
gid = current_gid();
umask = current_umask();
lowercase = 0;
conv = CONV_BINARY;
eas = 2;
chk = 1;
errs = 1;
chkdsk = 1;
timeshift = 0;
if (!(o = parse_opts(options, &uid, &gid, &umask, &lowercase, &conv,
&eas, &chk, &errs, &chkdsk, &timeshift))) {
printk("HPFS: bad mount options.\n");
goto bail0;
}
if (o==2) {
hpfs_help();
goto bail0;
}
/*sbi->sb_mounting = 1;*/
sb_set_blocksize(s, 512);
sbi->sb_fs_size = -1;
if (!(bootblock = hpfs_map_sector(s, 0, &bh0, 0))) goto bail1;
if (!(superblock = hpfs_map_sector(s, 16, &bh1, 1))) goto bail2;
if (!(spareblock = hpfs_map_sector(s, 17, &bh2, 0))) goto bail3;
/* Check magics */
if (/*bootblock->magic != BB_MAGIC
||*/ superblock->magic != SB_MAGIC
|| spareblock->magic != SP_MAGIC) {
if (!silent) printk("HPFS: Bad magic ... probably not HPFS\n");
goto bail4;
}
/* Check version */
if (!(s->s_flags & MS_RDONLY) &&
superblock->funcversion != 2 && superblock->funcversion != 3) {
printk("HPFS: Bad version %d,%d. Mount readonly to go around\n",
(int)superblock->version, (int)superblock->funcversion);
printk("HPFS: please try recent version of HPFS driver at http://artax.karlin.mff.cuni.cz/~mikulas/vyplody/hpfs/index-e.cgi and if it still can't understand this format, contact author - mikulas@artax.karlin.mff.cuni.cz\n");
goto bail4;
}
s->s_flags |= MS_NOATIME;
/* Fill superblock stuff */
s->s_magic = HPFS_SUPER_MAGIC;
s->s_op = &hpfs_sops;
sbi->sb_root = superblock->root;
sbi->sb_fs_size = superblock->n_sectors;
sbi->sb_bitmaps = superblock->bitmaps;
sbi->sb_dirband_start = superblock->dir_band_start;
sbi->sb_dirband_size = superblock->n_dir_band;
sbi->sb_dmap = superblock->dir_band_bitmap;
sbi->sb_uid = uid;
sbi->sb_gid = gid;
sbi->sb_mode = 0777 & ~umask;
sbi->sb_n_free = -1;
sbi->sb_n_free_dnodes = -1;
sbi->sb_lowercase = lowercase;
sbi->sb_conv = conv;
sbi->sb_eas = eas;
sbi->sb_chk = chk;
sbi->sb_chkdsk = chkdsk;
sbi->sb_err = errs;
sbi->sb_timeshift = timeshift;
sbi->sb_was_error = 0;
sbi->sb_cp_table = NULL;
sbi->sb_c_bitmap = -1;
sbi->sb_max_fwd_alloc = 0xffffff;
/* Load bitmap directory */
if (!(sbi->sb_bmp_dir = hpfs_load_bitmap_directory(s, superblock->bitmaps)))
goto bail4;
/* Check for general fs errors*/
if (spareblock->dirty && !spareblock->old_wrote) {
if (errs == 2) {
printk("HPFS: Improperly stopped, not mounted\n");
goto bail4;
}
hpfs_error(s, "improperly stopped");
}
if (!(s->s_flags & MS_RDONLY)) {
spareblock->dirty = 1;
spareblock->old_wrote = 0;
mark_buffer_dirty(bh2);
}
if (spareblock->hotfixes_used || spareblock->n_spares_used) {
if (errs >= 2) {
printk("HPFS: Hotfixes not supported here, try chkdsk\n");
mark_dirty(s);
goto bail4;
}
hpfs_error(s, "hotfixes not supported here, try chkdsk");
if (errs == 0) printk("HPFS: Proceeding, but your filesystem will be probably corrupted by this driver...\n");
else printk("HPFS: This driver may read bad files or crash when operating on disk with hotfixes.\n");
}
if (spareblock->n_dnode_spares != spareblock->n_dnode_spares_free) {
if (errs >= 2) {
printk("HPFS: Spare dnodes used, try chkdsk\n");
mark_dirty(s);
goto bail4;
}
hpfs_error(s, "warning: spare dnodes used, try chkdsk");
if (errs == 0) printk("HPFS: Proceeding, but your filesystem could be corrupted if you delete files or directories\n");
}
if (chk) {
unsigned a;
if (superblock->dir_band_end - superblock->dir_band_start + 1 != superblock->n_dir_band ||
superblock->dir_band_end < superblock->dir_band_start || superblock->n_dir_band > 0x4000) {
hpfs_error(s, "dir band size mismatch: dir_band_start==%08x, dir_band_end==%08x, n_dir_band==%08x",
superblock->dir_band_start, superblock->dir_band_end, superblock->n_dir_band);
goto bail4;
}
a = sbi->sb_dirband_size;
sbi->sb_dirband_size = 0;
if (hpfs_chk_sectors(s, superblock->dir_band_start, superblock->n_dir_band, "dir_band") ||
hpfs_chk_sectors(s, superblock->dir_band_bitmap, 4, "dir_band_bitmap") ||
hpfs_chk_sectors(s, superblock->bitmaps, 4, "bitmaps")) {
mark_dirty(s);
goto bail4;
}
sbi->sb_dirband_size = a;
} else printk("HPFS: You really don't want any checks? You are crazy...\n");
/* Load code page table */
if (spareblock->n_code_pages)
if (!(sbi->sb_cp_table = hpfs_load_code_page(s, spareblock->code_page_dir)))
printk("HPFS: Warning: code page support is disabled\n");
brelse(bh2);
brelse(bh1);
brelse(bh0);
root = iget_locked(s, sbi->sb_root);
if (!root)
goto bail0;
hpfs_init_inode(root);
hpfs_read_inode(root);
unlock_new_inode(root);
s->s_root = d_alloc_root(root);
if (!s->s_root) {
iput(root);
goto bail0;
}
hpfs_set_dentry_operations(s->s_root);
/*
* find the root directory's . pointer & finish filling in the inode
*/
root_dno = hpfs_fnode_dno(s, sbi->sb_root);
if (root_dno)
de = map_dirent(root, root_dno, "\001\001", 2, NULL, &qbh);
if (!de)
hpfs_error(s, "unable to find root dir");
else {
root->i_atime.tv_sec = local_to_gmt(s, de->read_date);
root->i_atime.tv_nsec = 0;
root->i_mtime.tv_sec = local_to_gmt(s, de->write_date);
root->i_mtime.tv_nsec = 0;
root->i_ctime.tv_sec = local_to_gmt(s, de->creation_date);
root->i_ctime.tv_nsec = 0;
hpfs_i(root)->i_ea_size = de->ea_size;
hpfs_i(root)->i_parent_dir = root->i_ino;
if (root->i_size == -1)
root->i_size = 2048;
if (root->i_blocks == -1)
root->i_blocks = 5;
hpfs_brelse4(&qbh);
}
return 0;
bail4: brelse(bh2);
bail3: brelse(bh1);
bail2: brelse(bh0);
bail1:
bail0:
kfree(sbi->sb_bmp_dir);
kfree(sbi->sb_cp_table);
s->s_fs_info = NULL;
kfree(sbi);
return -EINVAL;
}
static int hpfs_get_sb(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data, struct vfsmount *mnt)
{
return get_sb_bdev(fs_type, flags, dev_name, data, hpfs_fill_super,
mnt);
}
static struct file_system_type hpfs_fs_type = {
.owner = THIS_MODULE,
.name = "hpfs",
.get_sb = hpfs_get_sb,
.kill_sb = kill_block_super,
.fs_flags = FS_REQUIRES_DEV,
};
static int __init init_hpfs_fs(void)
{
int err = init_inodecache();
if (err)
goto out1;
err = register_filesystem(&hpfs_fs_type);
if (err)
goto out;
return 0;
out:
destroy_inodecache();
out1:
return err;
}
static void __exit exit_hpfs_fs(void)
{
unregister_filesystem(&hpfs_fs_type);
destroy_inodecache();
}
module_init(init_hpfs_fs)
module_exit(exit_hpfs_fs)
MODULE_LICENSE("GPL");