diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1999-06-17 14:08:29 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1999-06-17 14:08:29 +0000 |
commit | 57d569635c05dc4ea9b9f1f8dcec69b9ddc989b2 (patch) | |
tree | 1f703abf7d95dcd50ee52da3b96eb1b4b2b4ea53 /fs | |
parent | 59223edaa18759982db0a8aced0e77457d10c68e (diff) |
The rest of 2.3.6.
Diffstat (limited to 'fs')
-rw-r--r-- | fs/hpfs/alloc.c | 434 | ||||
-rw-r--r-- | fs/hpfs/anode.c | 482 | ||||
-rw-r--r-- | fs/hpfs/buffer.c | 265 | ||||
-rw-r--r-- | fs/hpfs/dentry.c | 62 | ||||
-rw-r--r-- | fs/hpfs/dir.c | 253 | ||||
-rw-r--r-- | fs/hpfs/dnode.c | 1070 | ||||
-rw-r--r-- | fs/hpfs/ea.c | 305 | ||||
-rw-r--r-- | fs/hpfs/file.c | 195 | ||||
-rw-r--r-- | fs/hpfs/hpfs_fn.h | 318 | ||||
-rw-r--r-- | fs/hpfs/inode.c | 381 | ||||
-rw-r--r-- | fs/hpfs/map.c | 268 | ||||
-rw-r--r-- | fs/hpfs/mmap.c | 128 | ||||
-rw-r--r-- | fs/hpfs/name.c | 144 | ||||
-rw-r--r-- | fs/hpfs/namei.c | 549 | ||||
-rw-r--r-- | fs/hpfs/super.c | 598 | ||||
-rw-r--r-- | fs/proc/sysvipc.c | 138 |
16 files changed, 5590 insertions, 0 deletions
diff --git a/fs/hpfs/alloc.c b/fs/hpfs/alloc.c new file mode 100644 index 000000000..bca0497ae --- /dev/null +++ b/fs/hpfs/alloc.c @@ -0,0 +1,434 @@ +/* + * linux/fs/hpfs/alloc.c + * + * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999 + * + * HPFS bitmap operations + */ + +#include "hpfs_fn.h" + +/* + * 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 >= s->s_hpfs_dirband_start && sec < s->s_hpfs_dirband_start + s->s_hpfs_dirband_size) { + unsigned ssec = (sec - s->s_hpfs_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 > s->s_hpfs_fs_size) { + hpfs_error(s, "sector(s) '%s' badly placed at %08x", msg, start); + return 1; + } + if (s->s_hpfs_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, n + forward)) { + ret = bs + nr; + goto rt; + } + if (!tstbits(bmp, nr + 2*n, n + forward)) { + ret = bs + nr + n; + goto rt; + }*/ + q = nr + n; b = 0; + while ((a = tstbits(bmp, q, n + forward))) { + 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) { + if (!bmp[i]) continue; + if (n + forward >= 0x3f && bmp[i] != -1) continue; + 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))) { + q += a; + if (n != 1) q = ((q-1)&~(n-1))+n; + if (q>>5 > i) break; + } + if (!a) { + ret = bs + q; + goto rt; + } + } + rt: + if (ret) { + if (s->s_hpfs_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; + unsigned i; + unsigned n_bmps; + int b = s->s_hpfs_c_bitmap; + int f_p = 0; + if (forward < 0) { + forward = -forward; + f_p = 1; + } + if (lock) hpfs_lock_creation(s); + if (near && near < s->s_hpfs_fs_size) + if ((sec = alloc_in_bmp(s, near, n, f_p ? forward : forward/4))) goto ret; + 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; + } + n_bmps = (s->s_hpfs_fs_size + 0x4000 - 1) >> 14; + for (i = 0; i < n_bmps / 2; i++) { + if ((sec = alloc_in_bmp(s, (n_bmps/2+i) << 14, n, forward))) { + s->s_hpfs_c_bitmap = n_bmps/2+i; + goto ret; + } + if ((sec = alloc_in_bmp(s, (n_bmps/2-i-1) << 14, n, forward))) { + s->s_hpfs_c_bitmap = n_bmps/2-i-1; + goto ret; + } + } + if ((sec = alloc_in_bmp(s, (n_bmps-1) << 14, n, forward))) { + s->s_hpfs_c_bitmap = n_bmps-1; + goto ret; + } + if (!f_p) { + for (i = 0; i < n_bmps; i++) + if ((sec = alloc_in_bmp(s, i << 14, n, 0))) { + s->s_hpfs_c_bitmap = 0x10000000 + i; + goto ret; + } + } + 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; + if (nr < s->s_hpfs_dirband_start) + nr = s->s_hpfs_dirband_start; + if (nr >= s->s_hpfs_dirband_start + s->s_hpfs_dirband_size) + nr = s->s_hpfs_dirband_start + s->s_hpfs_dirband_size - 4; + nr -= s->s_hpfs_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) + s->s_hpfs_dirband_start; +} + +/* Alloc sector if it's free */ + +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; + /*printk("2 - ");*/ + if (!n) return; + if (sec < 0x12) { + hpfs_error(s, "Trying to free reserved sector %08x", sec); + return; + } + lock_super(s); + 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 = (s->s_hpfs_fs_size + 0x4000 - 1) >> 14; + int b = s->s_hpfs_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 (s->s_hpfs_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 (s->s_hpfs_chk) if (dno & 3) { + hpfs_error(s, "hpfs_free_dnode: dnode %08x not aligned", dno); + return; + } + if (dno < s->s_hpfs_dirband_start || + dno >= s->s_hpfs_dirband_start + s->s_hpfs_dirband_size) { + hpfs_free_sectors(s, dno, 4); + } else { + struct quad_buffer_head qbh; + unsigned *bmp; + unsigned ssec = (dno - s->s_hpfs_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, s->s_hpfs_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; +} diff --git a/fs/hpfs/anode.c b/fs/hpfs/anode.c new file mode 100644 index 000000000..f2c4a3532 --- /dev/null +++ b/fs/hpfs/anode.c @@ -0,0 +1,482 @@ +/* + * 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 (s->s_hpfs_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 (s->s_hpfs_chk) if (hpfs_chk_sectors(s, a, 1, "data")) { + brelse(bh); + return -1; + } + if (inode) { + inode->i_hpfs_file_sec = btree->u.external[i].file_secno; + inode->i_hpfs_disk_sec = btree->u.external[i].disk_secno; + inode->i_hpfs_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, 1); + brelse(bh); + if (s->s_hpfs_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, 1); + 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 = node; + } + 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, 1); + } 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, 1); + brelse(bh); + if ((a == node && fnod) || na == -1) return se; + c2 = 0; + while (up != -1) { + if (s->s_hpfs_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, 1); + 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, 1); + brelse(bh); + } + return se; + } + up = up != node ? anode->up : -1; + btree->u.internal[btree->n_used_nodes - 1].file_secno = /*fs*/-1; + if (up == -1) anode->up = ra; + mark_buffer_dirty(bh, 1); + brelse(bh); + a = na; + if ((anode = hpfs_alloc_anode(s, a, &na, &bh))) { + /*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, 1); + brelse(bh); + if ((anode = hpfs_map_anode(s, a, &bh))) { + anode->up = na; + mark_buffer_dirty(bh, 1); + 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, 1); + 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, 1); + 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, 1); + brelse(bh); + mark_buffer_dirty(bh2, 1); + 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 (s->s_hpfs_chk) + if (hpfs_stop_cycles(s, ano, &d1, &d2, "hpfs_remove_btree #1")) + return; + anode = hpfs_map_anode(s, ano, &bh); + 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; + if (s->s_hpfs_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; + brelse(bh); + if (--level) { + anode = hpfs_map_anode(s, ano, &bh); + 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 (s->s_hpfs_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 (s->s_hpfs_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, 1); + 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, 1); + } 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, 1); + if (btree->u.internal[i].file_secno == secs) { + brelse(bh); + return; + } + node = btree->u.internal[i].down; + brelse(bh); + if (s->s_hpfs_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, 1); + 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); +} diff --git a/fs/hpfs/buffer.c b/fs/hpfs/buffer.c new file mode 100644 index 000000000..8bcbf28fc --- /dev/null +++ b/fs/hpfs/buffer.c @@ -0,0 +1,265 @@ +/* + * linux/fs/hpfs/buffer.c + * + * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999 + * + * general buffer i/o + */ + +#include <linux/string.h> +#include "hpfs_fn.h" + +void hpfs_lock_creation(struct super_block *s) +{ +#ifdef DEBUG_LOCKS + printk("lock creation\n"); +#endif + while (s->s_hpfs_creation_de_lock) sleep_on(&s->s_hpfs_creation_de); + s->s_hpfs_creation_de_lock = 1; +} + +void hpfs_unlock_creation(struct super_block *s) +{ +#ifdef DEBUG_LOCKS + printk("unlock creation\n"); +#endif + s->s_hpfs_creation_de_lock = 0; + wake_up(&s->s_hpfs_creation_de); +} + +void hpfs_lock_iget(struct super_block *s, int mode) +{ +#ifdef DEBUG_LOCKS + printk("lock iget\n"); +#endif + while (s->s_hpfs_rd_inode) sleep_on(&s->s_hpfs_iget_q); + s->s_hpfs_rd_inode = mode; +} + +void hpfs_unlock_iget(struct super_block *s) +{ +#ifdef DEBUG_LOCKS + printk("unlock iget\n"); +#endif + s->s_hpfs_rd_inode = 0; + wake_up(&s->s_hpfs_iget_q); +} + +void hpfs_lock_inode(struct inode *i) +{ + if (i) down(&i->i_hpfs_sem); +} + +void hpfs_unlock_inode(struct inode *i) +{ + if (i) up(&i->i_hpfs_sem); +} + +void hpfs_lock_2inodes(struct inode *i1, struct inode *i2) +{ + if (!i1) { if (i2) down(&i2->i_hpfs_sem); return; } + if (!i2) { if (i1) down(&i1->i_hpfs_sem); return; } + if (i1->i_ino < i2->i_ino) { + down(&i1->i_hpfs_sem); + down(&i2->i_hpfs_sem); + } else if (i1->i_ino > i2->i_ino) { + down(&i2->i_hpfs_sem); + down(&i1->i_hpfs_sem); + } else down(&i1->i_hpfs_sem); +} + +void hpfs_unlock_2inodes(struct inode *i1, struct inode *i2) +{ + if (!i1) { if (i2) up(&i2->i_hpfs_sem); return; } + if (!i2) { if (i1) up(&i1->i_hpfs_sem); return; } + if (i1->i_ino < i2->i_ino) { + up(&i2->i_hpfs_sem); + up(&i1->i_hpfs_sem); + } else if (i1->i_ino > i2->i_ino) { + up(&i1->i_hpfs_sem); + up(&i2->i_hpfs_sem); + } else up(&i1->i_hpfs_sem); +} + +void hpfs_lock_3inodes(struct inode *i1, struct inode *i2, struct inode *i3) +{ + if (!i1) { hpfs_lock_2inodes(i2, i3); return; } + if (!i2) { hpfs_lock_2inodes(i1, i3); return; } + if (!i3) { hpfs_lock_2inodes(i1, i2); return; } + if (i1->i_ino < i2->i_ino && i1->i_ino < i3->i_ino) { + down(&i1->i_hpfs_sem); + hpfs_lock_2inodes(i2, i3); + } else if (i2->i_ino < i1->i_ino && i2->i_ino < i3->i_ino) { + down(&i2->i_hpfs_sem); + hpfs_lock_2inodes(i1, i3); + } else if (i3->i_ino < i1->i_ino && i3->i_ino < i2->i_ino) { + down(&i3->i_hpfs_sem); + hpfs_lock_2inodes(i1, i2); + } else if (i1->i_ino != i2->i_ino) hpfs_lock_2inodes(i1, i2); + else hpfs_lock_2inodes(i1, i3); +} + +void hpfs_unlock_3inodes(struct inode *i1, struct inode *i2, struct inode *i3) +{ + if (!i1) { hpfs_unlock_2inodes(i2, i3); return; } + if (!i2) { hpfs_unlock_2inodes(i1, i3); return; } + if (!i3) { hpfs_unlock_2inodes(i1, i2); return; } + if (i1->i_ino < i2->i_ino && i1->i_ino < i3->i_ino) { + hpfs_unlock_2inodes(i2, i3); + up(&i1->i_hpfs_sem); + } else if (i2->i_ino < i1->i_ino && i2->i_ino < i3->i_ino) { + hpfs_unlock_2inodes(i1, i3); + up(&i2->i_hpfs_sem); + } else if (i3->i_ino < i1->i_ino && i3->i_ino < i2->i_ino) { + hpfs_unlock_2inodes(i1, i2); + up(&i3->i_hpfs_sem); + } else if (i1->i_ino != i2->i_ino) hpfs_unlock_2inodes(i1, i2); + else hpfs_unlock_2inodes(i1, i3); +} + +/* 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) +{ + kdev_t dev = s->s_dev; + struct buffer_head *bh; + + if (!ahead || secno + ahead >= s->s_hpfs_fs_size) + *bhp = bh = bread(dev, secno, 512); + else *bhp = bh = breada(dev, secno, 512, 0, (ahead + 1) << 9); + 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);*/ + + if ((*bhp = bh = getblk(s->s_dev, secno, 512)) != NULL) { + mark_buffer_uptodate(bh, 1); + 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) +{ + kdev_t dev = s->s_dev; + struct buffer_head *bh; + char *data; + + if (secno & 3) { + printk("HPFS: hpfs_map_4sectors: unaligned read\n"); + return 0; + } + + qbh->data = data = (char *)kmalloc(2048, GFP_KERNEL); + if (!data) { + printk("HPFS: hpfs_map_4sectors: out of memory\n"); + goto bail; + } + + if (!ahead || secno + 4 + ahead > s->s_hpfs_fs_size) + qbh->bh[0] = bh = breada(dev, secno, 512, 0, 2048); + else qbh->bh[0] = bh = breada(dev, secno, 512, 0, (ahead + 4) << 9); + if (!bh) + goto bail0; + memcpy(data, bh->b_data, 512); + + qbh->bh[1] = bh = bread(dev, secno + 1, 512); + if (!bh) + goto bail1; + memcpy(data + 512, bh->b_data, 512); + + qbh->bh[2] = bh = bread(dev, secno + 2, 512); + if (!bh) + goto bail2; + memcpy(data + 2 * 512, bh->b_data, 512); + + qbh->bh[3] = bh = bread(dev, secno + 3, 512); + if (!bh) + goto bail3; + memcpy(data + 3 * 512, bh->b_data, 512); + + return data; + + bail3: + brelse(qbh->bh[2]); + bail2: + brelse(qbh->bh[1]); + bail1: + brelse(qbh->bh[0]); + bail0: + kfree_s(data, 2048); + 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) +{ + if (secno & 3) { + printk("HPFS: hpfs_get_4sectors: unaligned read\n"); + return 0; + } + + /*return hpfs_map_4sectors(s, secno, qbh, 0);*/ + if (!(qbh->data = kmalloc(2048, GFP_KERNEL))) { + 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_s(qbh->data, 2048); +} + +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],1); + mark_buffer_dirty(qbh->bh[1],1); + mark_buffer_dirty(qbh->bh[2],1); + mark_buffer_dirty(qbh->bh[3],1); +} diff --git a/fs/hpfs/dentry.c b/fs/hpfs/dentry.c new file mode 100644 index 000000000..b5c3cf2e8 --- /dev/null +++ b/fs/hpfs/dentry.c @@ -0,0 +1,62 @@ +/* + * 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. + */ + +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(dentry->d_sb->s_hpfs_cp_table,qstr->name[i]), hash); + qstr->hash = end_name_hash(hash); + + return 0; +} + +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; +} + +struct dentry_operations hpfs_dentry_operations = { + NULL, /* d_validate */ + hpfs_hash_dentry, /* d_hash */ + hpfs_compare_dentry, /* d_compare */ + NULL /* d_delete */ +}; + +void hpfs_set_dentry_operations(struct dentry *dentry) +{ + dentry->d_op = &hpfs_dentry_operations; +} diff --git a/fs/hpfs/dir.c b/fs/hpfs/dir.c new file mode 100644 index 000000000..8af35847d --- /dev/null +++ b/fs/hpfs/dir.c @@ -0,0 +1,253 @@ +/* + * linux/fs/hpfs/dir.c + * + * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999 + * + * directory VFS functions + */ + +#include "hpfs_fn.h" + +int hpfs_dir_read(struct file *filp, char *name, size_t len, loff_t *loff) +{ + return -EISDIR; +} + +int hpfs_dir_release(struct inode *inode, struct file *filp) +{ + hpfs_del_pos(inode, &filp->f_pos); + /*hpfs_write_if_changed(inode);*/ + return 0; +} + +int hpfs_readdir(struct file *filp, void * dirent, filldir_t filldir) +{ + struct inode *inode = filp->f_dentry->d_inode; + struct quad_buffer_head qbh; + struct hpfs_dirent *de; + int lc; + long old_pos; + char *tempname; + int c1, c2 = 0; + + if (inode->i_sb->s_hpfs_chk) { + if (hpfs_chk_sectors(inode->i_sb, inode->i_ino, 1, "dir_fnode")) + return -EFSERROR; + if (hpfs_chk_sectors(inode->i_sb, inode->i_hpfs_dno, 4, "dir_dnode")) + return -EFSERROR; + } + if (inode->i_sb->s_hpfs_chk >= 2) { + struct buffer_head *bh; + struct fnode *fno; + int e = 0; + if (!(fno = hpfs_map_fnode(inode->i_sb, inode->i_ino, &bh))) + return -EIOERROR; + if (!fno->dirflag) { + e = 1; + hpfs_error(inode->i_sb, "not a directory, fnode %08x",inode->i_ino); + } + if (inode->i_hpfs_dno != fno->u.external[0].disk_secno) { + e = 1; + hpfs_error(inode->i_sb, "corrupted inode: i_hpfs_dno == %08x, fnode -> dnode == %08x", inode->i_hpfs_dno, fno->u.external[0].disk_secno); + } + brelse(bh); + if (e) return -EFSERROR; + } + lc = inode->i_sb->s_hpfs_lowercase; + if (filp->f_pos == -2) { /* diff -r requires this (note, that diff -r */ + filp->f_pos = -3; /* also fails on msdos filesystem in 2.0) */ + return 0; + } + if (filp->f_pos == -3) return -ENOENT; + + hpfs_lock_inode(inode); + + 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 (inode->i_sb->s_hpfs_chk) + if (hpfs_stop_cycles(inode->i_sb, filp->f_pos, &c1, &c2, "hpfs_readdir")) { + hpfs_unlock_inode(inode); + return -EFSERROR; + } + if (filp->f_pos == -2) { + hpfs_unlock_inode(inode); + return 0; + } + if (filp->f_pos == 3 || filp->f_pos == 4 || filp->f_pos == 5) { + printk("HPFS: warning: pos==%d\n",(int)filp->f_pos); + hpfs_unlock_inode(inode); + return 0; + } + if (filp->f_pos == 0) { + if (filldir(dirent, ".", 1, filp->f_pos, inode->i_ino) < 0) { + hpfs_unlock_inode(inode); + return 0; + } + filp->f_pos = -1; + } + if (filp->f_pos == -1) { + if (filldir(dirent, "..", 2, filp->f_pos, inode->i_hpfs_parent_dir) < 0) { + hpfs_unlock_inode(inode); + return 0; + } + filp->f_pos = 1; + } + if (filp->f_pos == 1) { + filp->f_pos = ((loff_t) hpfs_de_as_down_as_possible(inode->i_sb, inode->i_hpfs_dno) << 4) + 1; + hpfs_add_pos(inode, &filp->f_pos); + filp->f_version = inode->i_version; + } + /*if (filp->f_version != inode->i_version) { + hpfs_unlock_inode(inode); + return -ENOENT; + }*/ + old_pos = filp->f_pos; + if (!(de = map_pos_dirent(inode, &filp->f_pos, &qbh))) { + hpfs_unlock_inode(inode); + return -EIOERROR; + } + if (de->first || de->last) { + if (inode->i_sb->s_hpfs_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 = %08x", old_pos); + if (de->last && (de->namelen != 1 || de ->name[0] != 255)) hpfs_error(inode->i_sb, "hpfs_readdir: bad \\377 entry; pos = %08x", 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) < 0) { + filp->f_pos = old_pos; + if (tempname != (char *)de->name) kfree(tempname); + hpfs_brelse4(&qbh); + hpfs_unlock_inode(inode); + return 0; + } + if (tempname != (char *)de->name) kfree(tempname); + hpfs_brelse4(&qbh); + } +} + +/* + * 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) +{ + 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; + + if ((err = hpfs_chk_name((char *)name, &len))) { + if (err == -ENAMETOOLONG) return ERR_PTR(-ENAMETOOLONG); + goto end_add; + } + + hpfs_lock_inode(dir); + /* + * '.' and '..' will never be passed here. + */ + + de = map_dirent(dir, dir->i_hpfs_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. + */ + + hpfs_lock_iget(dir->i_sb, de->directory || (de->ea_size && dir->i_sb->s_hpfs_eas) ? 1 : 2); + if (!(result = iget(dir->i_sb, ino))) { + hpfs_unlock_iget(dir->i_sb); + hpfs_error(result->i_sb, "hpfs_lookup: can't get inode"); + goto bail1; + } + if (!de->directory) result->i_hpfs_parent_dir = dir->i_ino; + hpfs_unlock_iget(dir->i_sb); + + 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) { + if (!(result->i_ctime = local_to_gmt(dir->i_sb, de->creation_date))) + result->i_ctime = 1; + result->i_mtime = local_to_gmt(dir->i_sb, de->write_date); + result->i_atime = local_to_gmt(dir->i_sb, de->read_date); + result->i_hpfs_ea_size = de->ea_size; + if (!result->i_hpfs_ea_mode && de->read_only) + result->i_mode &= ~0222; + if (!de->directory) { + if (result->i_size == -1) { + result->i_size = de->file_size; + /* + * i_blocks should count the fnode and any anodes. + * We count 1 for the fnode and don't bother about + * anodes -- the disk heads are on the directory band + * and we want them to stay there. + */ + result->i_blocks = 1 + ((result->i_size + 511) >> 9); + } + } + } + + hpfs_brelse4(&qbh); + + /* + * Made it. + */ + + end: + hpfs_unlock_inode(dir); + end_add: + hpfs_set_dentry_operations(dentry); + d_add(dentry, result); + return NULL; + + /* + * Didn't. + */ + bail1: + + hpfs_brelse4(&qbh); + + /*bail:*/ + + hpfs_unlock_inode(dir); + return ERR_PTR(-ENOENT); +} diff --git a/fs/hpfs/dnode.c b/fs/hpfs/dnode.c new file mode 100644 index 000000000..e4b4bbc91 --- /dev/null +++ b/fs/hpfs/dnode.c @@ -0,0 +1,1070 @@ +/* + * linux/fs/hpfs/dnode.c + * + * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999 + * + * handling directory dnode tree - adding, deleteing & searching for dirents + */ + +#include "hpfs_fn.h" + +static loff_t get_pos(struct dnode *d, struct hpfs_dirent *fde) +{ + struct hpfs_dirent *de; + struct hpfs_dirent *de_end = dnode_end_de(d); + int i = 1; + for (de = dnode_first_de(d); de < de_end; de = de_next_de(de)) { + if (de == fde) return ((loff_t) d->self << 4) | (loff_t)i; + i++; + } + printk("HPFS: get_pos: not_found\n"); + return ((loff_t)d->self << 4) | (loff_t)1; +} + +void hpfs_add_pos(struct inode *inode, loff_t *pos) +{ + int i = 0; + loff_t **ppos; + if (inode->i_hpfs_rddir_off) + for (; inode->i_hpfs_rddir_off[i]; i++) + if (inode->i_hpfs_rddir_off[i] == pos) return; + if (!(i&0x0f)) { + if (!(ppos = kmalloc((i+0x11) * sizeof(loff_t*), GFP_KERNEL))) { + printk("HPFS: out of memory for position list\n"); + return; + } + if (inode->i_hpfs_rddir_off) { + memcpy(ppos, inode->i_hpfs_rddir_off, i * sizeof(loff_t)); + kfree(inode->i_hpfs_rddir_off); + } + inode->i_hpfs_rddir_off = ppos; + } + inode->i_hpfs_rddir_off[i] = pos; + inode->i_hpfs_rddir_off[i + 1] = NULL; +} + +void hpfs_del_pos(struct inode *inode, loff_t *pos) +{ + loff_t **i, **j; + if (!inode->i_hpfs_rddir_off) goto not_f; + for (i = inode->i_hpfs_rddir_off; *i; i++) if (*i == pos) goto fnd; + goto not_f; + fnd: + for (j = i + 1; *j; j++) ; + *i = *(j - 1); + *(j - 1) = NULL; + if (j - 1 == inode->i_hpfs_rddir_off) { + kfree(inode->i_hpfs_rddir_off); + inode->i_hpfs_rddir_off = NULL; + } + return; + not_f: + /*printk("HPFS: warning: position pointer %p->%08x not found\n", pos, (int)*pos);*/ + return; +} + +static void for_all_poss(struct inode *inode, void (*f)(loff_t *, loff_t, loff_t), + loff_t p1, loff_t p2) +{ + loff_t **i; + if (!inode->i_hpfs_rddir_off) return; + for (i = inode->i_hpfs_rddir_off; *i; i++) (*f)(*i, p1, p2); + return; +} + +void hpfs_pos_subst(loff_t *p, loff_t f, loff_t t) +{ + if (*p == f) *p = t; +} + +/*void hpfs_hpfs_pos_substd(loff_t *p, loff_t f, loff_t t) +{ + if ((*p & ~0x3f) == (f & ~0x3f)) *p = (t & ~0x3f) | (*p & 0x3f); +}*/ + +void hpfs_pos_ins(loff_t *p, loff_t d, loff_t c) +{ + if ((*p & ~0x3f) == (d & ~0x3f) && (*p & 0x3f) >= (d & 0x3f)) { + int n = (*p & 0x3f) + c; + if (n > 0x3f) printk("HPFS: hpfs_pos_ins: %08x + %d\n", (int)*p, (int)c >> 8); + else *p = (*p & ~0x3f) | n; + } +} + +void hpfs_pos_del(loff_t *p, loff_t d, loff_t c) +{ + if ((*p & ~0x3f) == (d & ~0x3f) && (*p & 0x3f) >= (d & 0x3f)) { + int n = (*p & 0x3f) - c; + if (n < 1) printk("HPFS: hpfs_pos_ins: %08x - %d\n", (int)*p, (int)c >> 8); + else *p = (*p & ~0x3f) | n; + } +} + +static struct hpfs_dirent *dnode_pre_last_de(struct dnode *d) +{ + struct hpfs_dirent *de, *de_end, *dee = NULL, *deee = NULL; + de_end = dnode_end_de(d); + for (de = dnode_first_de(d); de < de_end; de = de_next_de(de)) { + deee = dee; dee = de; + } + return deee; +} + +static struct hpfs_dirent *dnode_last_de(struct dnode *d) +{ + struct hpfs_dirent *de, *de_end, *dee = NULL; + de_end = dnode_end_de(d); + for (de = dnode_first_de(d); de < de_end; de = de_next_de(de)) { + dee = de; + } + return dee; +} + +static void set_last_pointer(struct super_block *s, struct dnode *d, dnode_secno ptr) +{ + struct hpfs_dirent *de; + if (!(de = dnode_last_de(d))) { + hpfs_error(s, "set_last_pointer: empty dnode %08x", d->self); + return; + } + if (s->s_hpfs_chk) { + if (de->down) { + hpfs_error(s, "set_last_pointer: dnode %08x has already last pointer %08x", + d->self, de_down_pointer(de)); + return; + } + if (de->length != 32) { + hpfs_error(s, "set_last_pointer: bad last dirent in dnode %08x", d->self); + return; + } + } + if (ptr) { + if ((d->first_free += 4) > 2048) { + hpfs_error(s,"set_last_pointer: too long dnode %08x", d->self); + d->first_free -= 4; + return; + } + de->length = 36; + de->down = 1; + *(dnode_secno *)((char *)de + 32) = ptr; + } +} + +/* Add an entry to dnode and don't care if it grows over 2048 bytes */ + +struct hpfs_dirent *hpfs_add_de(struct super_block *s, struct dnode *d, unsigned char *name, + unsigned namelen, secno down_ptr) +{ + struct hpfs_dirent *de; + struct hpfs_dirent *de_end = dnode_end_de(d); + unsigned d_size = de_size(namelen, down_ptr); + for (de = dnode_first_de(d); de < de_end; de = de_next_de(de)) { + int c = hpfs_compare_names(s, name, namelen, de->name, de->namelen, de->last); + if (!c) { + hpfs_error(s, "name (%c,%d) already exists in dnode %08x", *name, namelen, d->self); + return NULL; + } + if (c < 0) break; + } + memmove((char *)de + d_size, de, (char *)de_end - (char *)de); + memset(de, 0, d_size); + if (down_ptr) { + *(int *)((char *)de + d_size - 4) = down_ptr; + de->down = 1; + } + de->length = d_size; + if (down_ptr) de->down = 1; + de->not_8x3 = hpfs_is_name_long(name, namelen); + de->namelen = namelen; + memcpy(de->name, name, namelen); + d->first_free += d_size; + return de; +} + +/* Delete dirent and don't care about it's subtree */ + +void hpfs_delete_de(struct super_block *s, struct dnode *d, struct hpfs_dirent *de) +{ + if (de->last) { + hpfs_error(s, "attempt to delete last dirent in dnode %08x", d->self); + return; + } + d->first_free -= de->length; + memmove(de, de_next_de(de), d->first_free + (char *)d - (char *)de); +} + +static void fix_up_ptrs(struct super_block *s, struct dnode *d) +{ + struct hpfs_dirent *de; + struct hpfs_dirent *de_end = dnode_end_de(d); + dnode_secno dno = d->self; + for (de = dnode_first_de(d); de < de_end; de = de_next_de(de)) + if (de->down) { + struct quad_buffer_head qbh; + struct dnode *dd; + if ((dd = hpfs_map_dnode(s, de_down_pointer(de), &qbh))) { + if (dd->up != dno || dd->root_dnode) { + dd->up = dno; + dd->root_dnode = 0; + hpfs_mark_4buffers_dirty(&qbh); + } + hpfs_brelse4(&qbh); + } + } +} + +/* Add an entry to dnode and do dnode splitting if required */ + +int hpfs_add_to_dnode(struct inode *i, dnode_secno dno, unsigned char *name, unsigned namelen, + struct hpfs_dirent *new_de, dnode_secno down_ptr) +{ + struct quad_buffer_head qbh, qbh1, qbh2; + struct dnode *d, *ad, *rd, *nd = NULL; + dnode_secno adno, rdno; + struct hpfs_dirent *de; + struct hpfs_dirent nde; + char *nname; + int h; + int pos; + struct buffer_head *bh; + struct fnode *fnode; + int c1, c2 = 0; + if (!(nname = kmalloc(256, GFP_KERNEL))) { + printk("HPFS: out of memory, can't add to dnode\n"); + return 1; + } + go_up: + if (namelen >= 256) { + hpfs_error(i->i_sb, "hpfs_add_to_dnode: namelen == %d", namelen); + if (nd) kfree(nd); + kfree(nname); + return 1; + } + if (!(d = hpfs_map_dnode(i->i_sb, dno, &qbh))) { + if (nd) kfree(nd); + kfree(nname); + return 1; + } + go_up_a: + if (i->i_sb->s_hpfs_chk) + if (hpfs_stop_cycles(i->i_sb, dno, &c1, &c2, "hpfs_add_to_dnode")) { + hpfs_brelse4(&qbh); + if (nd) kfree(nd); + kfree(nname); + return 1; + } + if (d->first_free + de_size(namelen, down_ptr) <= 2048) { + loff_t t; + copy_de(de=hpfs_add_de(i->i_sb, d, name, namelen, down_ptr), new_de); + t = get_pos(d, de); + for_all_poss(i, hpfs_pos_ins, t, 1); + for_all_poss(i, hpfs_pos_subst, 4, t); + for_all_poss(i, hpfs_pos_subst, 5, t + 1); + hpfs_mark_4buffers_dirty(&qbh); + hpfs_brelse4(&qbh); + if (nd) kfree(nd); + kfree(nname); + return 0; + } + if (!nd) if (!(nd = kmalloc(0x924, GFP_KERNEL))) { + /* 0x924 is a max size of dnode after adding a dirent with + max name length. We alloc this only once. There must + not be any error while splitting dnodes, otherwise the + whole directory, not only file we're adding, would + be lost. */ + printk("HPFS: out of memory for dnode splitting\n"); + hpfs_brelse4(&qbh); + kfree(nname); + return 1; + } + memcpy(nd, d, d->first_free); + copy_de(de = hpfs_add_de(i->i_sb, nd, name, namelen, down_ptr), new_de); + for_all_poss(i, hpfs_pos_ins, get_pos(nd, de), 1); + h = ((char *)dnode_last_de(nd) - (char *)nd) / 2 + 10; + if (!(ad = hpfs_alloc_dnode(i->i_sb, d->up, &adno, &qbh1, 0))) { + hpfs_error(i->i_sb, "unable to alloc dnode - dnode tree will be corrupted"); + hpfs_brelse4(&qbh); + kfree(nd); + kfree(nname); + return 1; + } + i->i_size += 2048; + i->i_blocks += 4; + pos = 1; + for (de = dnode_first_de(nd); (char *)de_next_de(de) - (char *)nd < h; de = de_next_de(de)) { + copy_de(hpfs_add_de(i->i_sb, ad, de->name, de->namelen, de->down ? de_down_pointer(de) : 0), de); + for_all_poss(i, hpfs_pos_subst, ((loff_t)dno << 4) | pos, ((loff_t)adno << 4) | pos); + pos++; + } + copy_de(new_de = &nde, de); + memcpy(name = nname, de->name, namelen = de->namelen); + for_all_poss(i, hpfs_pos_subst, ((loff_t)dno << 4) | pos, 4); + down_ptr = adno; + set_last_pointer(i->i_sb, ad, de->down ? de_down_pointer(de) : 0); + de = de_next_de(de); + memmove((char *)nd + 20, de, nd->first_free + (char *)nd - (char *)de); + nd->first_free -= (char *)de - (char *)nd - 20; + memcpy(d, nd, nd->first_free); + for_all_poss(i, hpfs_pos_del, (loff_t)dno << 4, pos); + fix_up_ptrs(i->i_sb, ad); + if (!d->root_dnode) { + dno = ad->up = d->up; + hpfs_mark_4buffers_dirty(&qbh); + hpfs_brelse4(&qbh); + hpfs_mark_4buffers_dirty(&qbh1); + hpfs_brelse4(&qbh1); + goto go_up; + } + if (!(rd = hpfs_alloc_dnode(i->i_sb, d->up, &rdno, &qbh2, 0))) { + hpfs_error(i->i_sb, "unable to alloc dnode - dnode tree will be corrupted"); + hpfs_brelse4(&qbh); + hpfs_brelse4(&qbh1); + kfree(nd); + kfree(nname); + return 1; + } + i->i_size += 2048; + i->i_blocks += 4; + rd->root_dnode = 1; + rd->up = d->up; + if (!(fnode = hpfs_map_fnode(i->i_sb, d->up, &bh))) { + hpfs_free_dnode(i->i_sb, rdno); + hpfs_brelse4(&qbh); + hpfs_brelse4(&qbh1); + hpfs_brelse4(&qbh2); + kfree(nd); + kfree(nname); + return 1; + } + fnode->u.external[0].disk_secno = rdno; + mark_buffer_dirty(bh, 1); + brelse(bh); + d->up = ad->up = i->i_hpfs_dno = rdno; + d->root_dnode = ad->root_dnode = 0; + hpfs_mark_4buffers_dirty(&qbh); + hpfs_brelse4(&qbh); + hpfs_mark_4buffers_dirty(&qbh1); + hpfs_brelse4(&qbh1); + qbh = qbh2; + set_last_pointer(i->i_sb, rd, dno); + dno = rdno; + d = rd; + goto go_up_a; +} + +/* + * Add an entry to directory btree. + * I hate such crazy directory structure. + * It's easy to read but terrible to write. + * I wrote this directory code 4 times. + * I hope, now it's finally bug-free. + */ + +int hpfs_add_dirent(struct inode *i, unsigned char *name, unsigned namelen, + struct hpfs_dirent *new_de, int cdepth) +{ + struct dnode *d; + struct hpfs_dirent *de, *de_end; + struct quad_buffer_head qbh; + dnode_secno dno; + int c; + int c1, c2 = 0; + dno = i->i_hpfs_dno; + down: + if (i->i_sb->s_hpfs_chk) + if (hpfs_stop_cycles(i->i_sb, dno, &c1, &c2, "hpfs_add_dirent")) return 1; + if (!(d = hpfs_map_dnode(i->i_sb, dno, &qbh))) return 1; + de_end = dnode_end_de(d); + for (de = dnode_first_de(d); de < de_end; de = de_next_de(de)) { + if (!(c = hpfs_compare_names(i->i_sb, name, namelen, de->name, de->namelen, de->last))) { + hpfs_brelse4(&qbh); + return -1; + } + if (c < 0) { + if (de->down) { + dno = de_down_pointer(de); + hpfs_brelse4(&qbh); + goto down; + } + break; + } + } + hpfs_brelse4(&qbh); + if (!cdepth) hpfs_lock_creation(i->i_sb); + if (hpfs_check_free_dnodes(i->i_sb, FREE_DNODES_ADD)) { + c = 1; + goto ret; + } + i->i_version = ++event; + c = hpfs_add_to_dnode(i, dno, name, namelen, new_de, 0); + ret: + if (!cdepth) hpfs_unlock_creation(i->i_sb); + return c; +} + +/* + * Find dirent with higher name in 'from' subtree and move it to 'to' dnode. + * Return the dnode we moved from (to be checked later if it's empty) + */ + +static secno move_to_top(struct inode *i, dnode_secno from, dnode_secno to) +{ + dnode_secno dno, ddno; + dnode_secno chk_up = to; + struct dnode *dnode; + struct quad_buffer_head qbh; + struct hpfs_dirent *de, *nde; + int a; + loff_t t; + int c1, c2 = 0; + dno = from; + while (1) { + if (i->i_sb->s_hpfs_chk) + if (hpfs_stop_cycles(i->i_sb, dno, &c1, &c2, "move_to_top")) + return 0; + if (!(dnode = hpfs_map_dnode(i->i_sb, dno, &qbh))) return 0; + if (i->i_sb->s_hpfs_chk) { + if (dnode->up != chk_up) { + hpfs_error(i->i_sb, "move_to_top: up pointer from %08x should be %08x, is %08x", + dno, chk_up, dnode->up); + hpfs_brelse4(&qbh); + return 0; + } + chk_up = dno; + } + if (!(de = dnode_last_de(dnode))) { + hpfs_error(i->i_sb, "move_to_top: dnode %08x has no last de", dno); + hpfs_brelse4(&qbh); + return 0; + } + if (!de->down) break; + dno = de_down_pointer(de); + hpfs_brelse4(&qbh); + } + while (!(de = dnode_pre_last_de(dnode))) { + dnode_secno up = dnode->up; + hpfs_brelse4(&qbh); + hpfs_free_dnode(i->i_sb, dno); + i->i_size -= 2048; + i->i_blocks -= 4; + for_all_poss(i, hpfs_pos_subst, ((loff_t)dno << 4) | 1, 5); + if (up == to) return to; + if (!(dnode = hpfs_map_dnode(i->i_sb, up, &qbh))) return 0; + if (dnode->root_dnode) { + hpfs_error(i->i_sb, "move_to_top: got to root_dnode while moving from %08x to %08x", from, to); + hpfs_brelse4(&qbh); + return 0; + } + de = dnode_last_de(dnode); + if (!de || !de->down) { + hpfs_error(i->i_sb, "move_to_top: dnode %08x doesn't point down to %08x", up, dno); + hpfs_brelse4(&qbh); + return 0; + } + dnode->first_free -= 4; + de->length -= 4; + de->down = 0; + hpfs_mark_4buffers_dirty(&qbh); + dno = up; + } + t = get_pos(dnode, de); + for_all_poss(i, hpfs_pos_subst, t, 4); + for_all_poss(i, hpfs_pos_subst, t + 1, 5); + if (!(nde = kmalloc(de->length, GFP_KERNEL))) { + hpfs_error(i->i_sb, "out of memory for dirent - directory will be corrupted"); + hpfs_brelse4(&qbh); + return 0; + } + memcpy(nde, de, de->length); + ddno = de->down ? de_down_pointer(de) : 0; + hpfs_delete_de(i->i_sb, dnode, de); + set_last_pointer(i->i_sb, dnode, ddno); + hpfs_mark_4buffers_dirty(&qbh); + hpfs_brelse4(&qbh); + a = hpfs_add_to_dnode(i, to, nde->name, nde->namelen, nde, from); + kfree(nde); + if (a) return 0; + return dno; +} + +/* + * Check if a dnode is empty and delete it from the tree + * (chkdsk doesn't like empty dnodes) + */ + +static void delete_empty_dnode(struct inode *i, dnode_secno dno) +{ + struct quad_buffer_head qbh; + struct dnode *dnode; + dnode_secno down, up, ndown; + int p; + struct hpfs_dirent *de; + int c1, c2 = 0; + try_it_again: + if (hpfs_stop_cycles(i->i_sb, dno, &c1, &c2, "delete_empty_dnode")) return; + if (!(dnode = hpfs_map_dnode(i->i_sb, dno, &qbh))) return; + if (dnode->first_free > 56) goto end; + if (dnode->first_free == 52 || dnode->first_free == 56) { + struct hpfs_dirent *de_end; + int root = dnode->root_dnode; + up = dnode->up; + de = dnode_first_de(dnode); + down = de->down ? de_down_pointer(de) : 0; + if (i->i_sb->s_hpfs_chk) if (root && !down) { + hpfs_error(i->i_sb, "delete_empty_dnode: root dnode %08x is empty", dno); + goto end; + } + hpfs_brelse4(&qbh); + hpfs_free_dnode(i->i_sb, dno); + i->i_size -= 2048; + i->i_blocks -= 4; + if (root) { + struct fnode *fnode; + struct buffer_head *bh; + struct dnode *d1; + struct quad_buffer_head qbh1; + if (i->i_sb->s_hpfs_chk) if (up != i->i_ino) { + hpfs_error(i->i_sb, "bad pointer to fnode, dnode %08x, pointing to %08x, should be %08x", dno, up, i->i_ino); + return; + } + if ((d1 = hpfs_map_dnode(i->i_sb, down, &qbh1))) { + d1->up = up; + d1->root_dnode = 1; + hpfs_mark_4buffers_dirty(&qbh1); + hpfs_brelse4(&qbh1); + } + if ((fnode = hpfs_map_fnode(i->i_sb, up, &bh))) { + fnode->u.external[0].disk_secno = down; + mark_buffer_dirty(bh, 1); + brelse(bh); + } + i->i_hpfs_dno = down; + for_all_poss(i, hpfs_pos_subst, ((loff_t)dno << 4) | 1, (loff_t) -2); + return; + } + if (!(dnode = hpfs_map_dnode(i->i_sb, up, &qbh))) return; + p = 1; + de_end = dnode_end_de(dnode); + for (de = dnode_first_de(dnode); de < de_end; de = de_next_de(de), p++) + if (de->down) if (de_down_pointer(de) == dno) goto fnd; + hpfs_error(i->i_sb, "delete_empty_dnode: pointer to dnode %08x not found in dnode %08x", dno, up); + goto end; + fnd: + for_all_poss(i, hpfs_pos_subst, ((loff_t)dno << 4) | 1, ((loff_t)up << 4) | p); + if (!down) { + de->down = 0; + de->length -= 4; + dnode->first_free -= 4; + memmove(de_next_de(de), (char *)de_next_de(de) + 4, + (char *)dnode + dnode->first_free - (char *)de_next_de(de)); + } else { + struct dnode *d1; + struct quad_buffer_head qbh1; + *(dnode_secno *) ((void *) de + de->length - 4) = down; + if ((d1 = hpfs_map_dnode(i->i_sb, down, &qbh1))) { + d1->up = up; + hpfs_mark_4buffers_dirty(&qbh1); + hpfs_brelse4(&qbh1); + } + } + } else { + hpfs_error(i->i_sb, "delete_empty_dnode: dnode %08x, first_free == %03x", dno, dnode->first_free); + goto end; + } + + if (!de->last) { + struct hpfs_dirent *de_next = de_next_de(de); + struct hpfs_dirent *de_cp; + struct dnode *d1; + struct quad_buffer_head qbh1; + if (!de_next->down) goto endm; + ndown = de_down_pointer(de_next); + if (!(de_cp = kmalloc(de->length, GFP_KERNEL))) { + printk("HPFS: out of memory for dtree balancing\n"); + goto endm; + } + memcpy(de_cp, de, de->length); + hpfs_delete_de(i->i_sb, dnode, de); + hpfs_mark_4buffers_dirty(&qbh); + hpfs_brelse4(&qbh); + for_all_poss(i, hpfs_pos_subst, ((loff_t)up << 4) | p, 4); + for_all_poss(i, hpfs_pos_del, ((loff_t)up << 4) | p, 1); + if (de_cp->down) if ((d1 = hpfs_map_dnode(i->i_sb, de_down_pointer(de_cp), &qbh1))) { + d1->up = ndown; + hpfs_mark_4buffers_dirty(&qbh1); + hpfs_brelse4(&qbh1); + } + hpfs_add_to_dnode(i, ndown, de_cp->name, de_cp->namelen, de_cp, de_cp->down ? de_down_pointer(de_cp) : 0); + /*printk("UP-TO-DNODE: %08x (ndown = %08x, down = %08x, dno = %08x)\n", up, ndown, down, dno);*/ + dno = up; + kfree(de_cp); + goto try_it_again; + } else { + struct hpfs_dirent *de_prev = dnode_pre_last_de(dnode); + struct hpfs_dirent *de_cp; + struct dnode *d1; + struct quad_buffer_head qbh1; + dnode_secno dlp; + if (!de_prev) { + hpfs_error(i->i_sb, "delete_empty_dnode: empty dnode %08x", up); + hpfs_mark_4buffers_dirty(&qbh); + hpfs_brelse4(&qbh); + dno = up; + goto try_it_again; + } + if (!de_prev->down) goto endm; + ndown = de_down_pointer(de_prev); + if ((d1 = hpfs_map_dnode(i->i_sb, ndown, &qbh1))) { + struct hpfs_dirent *del = dnode_last_de(d1); + dlp = del->down ? de_down_pointer(del) : 0; + if (!dlp && down) { + if (d1->first_free > 2044) { + if (i->i_sb->s_hpfs_chk >= 2) { + printk("HPFS: warning: unbalanced dnode tree, see hpfs.txt 4 more info\n"); + printk("HPFS: warning: terminating balancing operation\n"); + } + hpfs_brelse4(&qbh1); + goto endm; + } + if (i->i_sb->s_hpfs_chk >= 2) { + printk("HPFS: warning: unbalanced dnode tree, see hpfs.txt 4 more info\n"); + printk("HPFS: warning: goin'on\n"); + } + del->length += 4; + del->down = 1; + d1->first_free += 4; + } + if (dlp && !down) { + del->length -= 4; + del->down = 0; + d1->first_free -= 4; + } else if (down) + *(dnode_secno *) ((void *) del + del->length - 4) = down; + } else goto endm; + if (!(de_cp = kmalloc(de_prev->length, GFP_KERNEL))) { + printk("HPFS: out of memory for dtree balancing\n"); + hpfs_brelse4(&qbh1); + goto endm; + } + hpfs_mark_4buffers_dirty(&qbh1); + hpfs_brelse4(&qbh1); + memcpy(de_cp, de_prev, de_prev->length); + hpfs_delete_de(i->i_sb, dnode, de_prev); + if (!de_prev->down) { + de_prev->length += 4; + de_prev->down = 1; + dnode->first_free += 4; + } + *(dnode_secno *) ((void *) de_prev + de_prev->length - 4) = ndown; + hpfs_mark_4buffers_dirty(&qbh); + hpfs_brelse4(&qbh); + for_all_poss(i, hpfs_pos_subst, ((loff_t)up << 4) | (p - 1), 4); + for_all_poss(i, hpfs_pos_subst, ((loff_t)up << 4) | p, ((loff_t)up << 4) | (p - 1)); + if (down) if ((d1 = hpfs_map_dnode(i->i_sb, de_down_pointer(de), &qbh1))) { + d1->up = ndown; + hpfs_mark_4buffers_dirty(&qbh1); + hpfs_brelse4(&qbh1); + } + hpfs_add_to_dnode(i, ndown, de_cp->name, de_cp->namelen, de_cp, dlp); + dno = up; + kfree(de_cp); + goto try_it_again; + } + endm: + hpfs_mark_4buffers_dirty(&qbh); + end: + hpfs_brelse4(&qbh); +} + + +/* Delete dirent from directory */ + +int hpfs_remove_dirent(struct inode *i, dnode_secno dno, struct hpfs_dirent *de, + struct quad_buffer_head *qbh, int depth) +{ + struct dnode *dnode = qbh->data; + dnode_secno down = 0; + int lock = 0; + loff_t t; + if (de->first || de->last) { + hpfs_error(i->i_sb, "hpfs_remove_dirent: attempt to delete first or last dirent in dnode %08x", dno); + hpfs_brelse4(qbh); + return 1; + } + if (de->down) down = de_down_pointer(de); + if (depth && (de->down || (de == dnode_first_de(dnode) && de_next_de(de)->last))) { + lock = 1; + hpfs_lock_creation(i->i_sb); + if (hpfs_check_free_dnodes(i->i_sb, FREE_DNODES_DEL)) { + hpfs_brelse4(qbh); + hpfs_unlock_creation(i->i_sb); + return 2; + } + } + i->i_version = ++event; + for_all_poss(i, hpfs_pos_del, (t = get_pos(dnode, de)) + 1, 1); + hpfs_delete_de(i->i_sb, dnode, de); + hpfs_mark_4buffers_dirty(qbh); + hpfs_brelse4(qbh); + if (down) { + dnode_secno a = move_to_top(i, down, dno); + for_all_poss(i, hpfs_pos_subst, 5, t); + if (a) delete_empty_dnode(i, a); + if (lock) hpfs_unlock_creation(i->i_sb); + return !a; + } + delete_empty_dnode(i, dno); + if (lock) hpfs_unlock_creation(i->i_sb); + return 0; +} + +void hpfs_count_dnodes(struct super_block *s, dnode_secno dno, int *n_dnodes, + int *n_subdirs, int *n_items) +{ + struct dnode *dnode; + struct quad_buffer_head qbh; + struct hpfs_dirent *de; + dnode_secno ptr, odno = 0; + int c1, c2 = 0; + int d1, d2 = 0; + go_down: + if (n_dnodes) (*n_dnodes)++; + if (s->s_hpfs_chk) + if (hpfs_stop_cycles(s, dno, &c1, &c2, "hpfs_count_dnodes #1")) return; + ptr = 0; + go_up: + if (!(dnode = hpfs_map_dnode(s, dno, &qbh))) return; + if (s->s_hpfs_chk) if (odno && odno != -1 && dnode->up != odno) + hpfs_error(s, "hpfs_count_dnodes: bad up pointer; dnode %08x, down %08x points to %08x", odno, dno, dnode->up); + de = dnode_first_de(dnode); + if (ptr) while(1) { + if (de->down) if (de_down_pointer(de) == ptr) goto process_de; + if (de->last) { + hpfs_brelse4(&qbh); + hpfs_error(s, "hpfs_count_dnodes: pointer to dnode %08x not found in dnode %08x, got here from %08x", + ptr, dno, odno); + return; + } + de = de_next_de(de); + } + next_de: + if (de->down) { + odno = dno; + dno = de_down_pointer(de); + hpfs_brelse4(&qbh); + goto go_down; + } + process_de: + if (!de->first && !de->last && de->directory && n_subdirs) (*n_subdirs)++; + if (!de->first && !de->last && n_items) (*n_items)++; + if ((de = de_next_de(de)) < dnode_end_de(dnode)) goto next_de; + ptr = dno; + dno = dnode->up; + if (dnode->root_dnode) { + hpfs_brelse4(&qbh); + return; + } + hpfs_brelse4(&qbh); + if (s->s_hpfs_chk) + if (hpfs_stop_cycles(s, ptr, &d1, &d2, "hpfs_count_dnodes #2")) return; + odno = -1; + goto go_up; +} + +static struct hpfs_dirent *map_nth_dirent(struct super_block *s, dnode_secno dno, int n, + struct quad_buffer_head *qbh, struct dnode **dn) +{ + int i; + struct hpfs_dirent *de, *de_end; + struct dnode *dnode; + dnode = hpfs_map_dnode(s, dno, qbh); + if (!dnode) return NULL; + if (dn) *dn=dnode; + de = dnode_first_de(dnode); + de_end = dnode_end_de(dnode); + for (i = 1; de < de_end; i++, de = de_next_de(de)) { + if (i == n) { + return de; + } + if (de->last) break; + } + hpfs_brelse4(qbh); + hpfs_error(s, "map_nth_dirent: n too high; dnode = %08x, requested %08x", dno, n); + return NULL; +} + +dnode_secno hpfs_de_as_down_as_possible(struct super_block *s, dnode_secno dno) +{ + struct quad_buffer_head qbh; + dnode_secno d = dno; + dnode_secno up = 0; + struct hpfs_dirent *de; + int c1, c2 = 0; + + again: + if (s->s_hpfs_chk) + if (hpfs_stop_cycles(s, d, &c1, &c2, "hpfs_de_as_down_as_possible")) + return d; + if (!(de = map_nth_dirent(s, d, 1, &qbh, NULL))) return dno; + if (s->s_hpfs_chk) + if (up && ((struct dnode *)qbh.data)->up != up) + hpfs_error(s, "hpfs_de_as_down_as_possible: bad up pointer; dnode %08x, down %08x points to %08x", up, d, ((struct dnode *)qbh.data)->up); + if (!de->down) { + hpfs_brelse4(&qbh); + return d; + } + up = d; + d = de_down_pointer(de); + hpfs_brelse4(&qbh); + goto again; +} + +struct hpfs_dirent *map_pos_dirent(struct inode *inode, loff_t *posp, + struct quad_buffer_head *qbh) +{ + loff_t pos; + unsigned c; + dnode_secno dno; + struct hpfs_dirent *de, *d; + struct hpfs_dirent *up_de; + struct hpfs_dirent *end_up_de; + struct dnode *dnode; + struct dnode *up_dnode; + struct quad_buffer_head qbh0; + + pos = *posp; + dno = pos >> 6 << 2; + pos &= 077; + if (!(de = map_nth_dirent(inode->i_sb, dno, pos, qbh, &dnode))) + goto bail; + + /* Going to the next dirent */ + if ((d = de_next_de(de)) < dnode_end_de(dnode)) { + if (!(++*posp & 077)) { + hpfs_error(inode->i_sb, "map_pos_dirent: pos crossed dnode boundary; pos = %08x", *posp); + goto bail; + } + /* We're going down the tree */ + if (d->down) { + *posp = ((loff_t) hpfs_de_as_down_as_possible(inode->i_sb, de_down_pointer(d)) << 4) + 1; + } + + return de; + } + + /* Going up */ + if (dnode->root_dnode) goto bail; + + if (!(up_dnode = hpfs_map_dnode(inode->i_sb, dnode->up, &qbh0))) + goto bail; + + end_up_de = dnode_end_de(up_dnode); + c = 0; + for (up_de = dnode_first_de(up_dnode); up_de < end_up_de; + up_de = de_next_de(up_de)) { + if (!(++c & 077)) hpfs_error(inode->i_sb, + "map_pos_dirent: pos crossed dnode boundary; dnode = %08x", dnode->up); + if (up_de->down && de_down_pointer(up_de) == dno) { + *posp = ((loff_t) dnode->up << 4) + c; + hpfs_brelse4(&qbh0); + return de; + } + } + + hpfs_error(inode->i_sb, "map_pos_dirent: pointer to dnode %08x not found in parent dnode %08x", + dno, dnode->up); + hpfs_brelse4(&qbh0); + + bail: + *posp = -2; + return de; +} + +/* Find a dirent in tree */ + +struct hpfs_dirent *map_dirent(struct inode *inode, dnode_secno dno, char *name, unsigned len, + dnode_secno *dd, struct quad_buffer_head *qbh) +{ + struct dnode *dnode; + struct hpfs_dirent *de; + struct hpfs_dirent *de_end; + int c1, c2 = 0; + + if (!S_ISDIR(inode->i_mode)) hpfs_error(inode->i_sb, "map_dirent: not a directory\n"); + again: + if (inode->i_sb->s_hpfs_chk) + if (hpfs_stop_cycles(inode->i_sb, dno, &c1, &c2, "map_dirent")) return NULL; + if (!(dnode = hpfs_map_dnode(inode->i_sb, dno, qbh))) return NULL; + + de_end = dnode_end_de(dnode); + for (de = dnode_first_de(dnode); de < de_end; de = de_next_de(de)) { + int t = hpfs_compare_names(inode->i_sb, name, len, de->name, de->namelen, de->last); + if (!t) { + if (dd) *dd = dno; + return de; + } + if (t < 0) { + if (de->down) { + dno = de_down_pointer(de); + hpfs_brelse4(qbh); + goto again; + } + break; + } + } + hpfs_brelse4(qbh); + return NULL; +} + +/* + * Remove empty directory. In normal cases it is only one dnode with two + * entries, but we must handle also such obscure cases when it's a tree + * of empty dnodes. + */ + +void hpfs_remove_dtree(struct super_block *s, dnode_secno dno) +{ + struct quad_buffer_head qbh; + struct dnode *dnode; + struct hpfs_dirent *de; + dnode_secno d1, d2, rdno = dno; + while (1) { + if (!(dnode = hpfs_map_dnode(s, dno, &qbh))) return; + de = dnode_first_de(dnode); + if (de->last) { + if (de->down) d1 = de_down_pointer(de); + else goto error; + hpfs_brelse4(&qbh); + hpfs_free_dnode(s, dno); + dno = d1; + } else break; + } + if (!de->first) goto error; + d1 = de->down ? de_down_pointer(de) : 0; + de = de_next_de(de); + if (!de->last) goto error; + d2 = de->down ? de_down_pointer(de) : 0; + hpfs_brelse4(&qbh); + hpfs_free_dnode(s, dno); + do { + while (d1) { + if (!(dnode = hpfs_map_dnode(s, dno = d1, &qbh))) return; + de = dnode_first_de(dnode); + if (!de->last) goto error; + d1 = de->down ? de_down_pointer(de) : 0; + hpfs_brelse4(&qbh); + hpfs_free_dnode(s, dno); + } + d1 = d2; + d2 = 0; + } while (d1); + return; + error: + hpfs_brelse4(&qbh); + hpfs_free_dnode(s, dno); + hpfs_error(s, "directory %08x is corrupted or not empty", rdno); +} + +/* + * Find dirent for specified fnode. Use truncated 15-char name in fnode as + * a help for searching. + */ + +struct hpfs_dirent *map_fnode_dirent(struct super_block *s, fnode_secno fno, + struct fnode *f, struct quad_buffer_head *qbh) +{ + char *name1; + char *name2; + int name1len, name2len; + struct dnode *d; + dnode_secno dno, downd; + struct fnode *upf; + struct buffer_head *bh; + struct hpfs_dirent *de, *de_end; + int c; + int c1, c2 = 0; + int d1, d2 = 0; + name1 = f->name; + if (!(name2 = kmalloc(256, GFP_KERNEL))) { + printk("HPFS: out of memory, can't map dirent\n"); + return NULL; + } + if (f->len <= 15) + memcpy(name2, name1, name1len = name2len = f->len); + else { + memcpy(name2, name1, 15); + memset(name2 + 15, 0xff, 256 - 15); + /*name2[15] = 0xff;*/ + name1len = 15; name2len = 256; + } + if (!(upf = hpfs_map_fnode(s, f->up, &bh))) { + kfree(name2); + return NULL; + } + if (!upf->dirflag) { + brelse(bh); + hpfs_error(s, "fnode %08x has non-directory parent %08x", fno, f->up); + kfree(name2); + return NULL; + } + dno = upf->u.external[0].disk_secno; + brelse(bh); + go_down: + downd = 0; + go_up: + if (!(d = hpfs_map_dnode(s, dno, qbh))) { + kfree(name2); + return NULL; + } + de_end = dnode_end_de(d); + de = dnode_first_de(d); + if (downd) { + while (de < de_end) { + if (de->down) if (de_down_pointer(de) == downd) goto f; + de = de_next_de(de); + } + hpfs_error(s, "pointer to dnode %08x not found in dnode %08x", downd, dno); + hpfs_brelse4(qbh); + kfree(name2); + return NULL; + } + next_de: + if (de->fnode == fno) { + kfree(name2); + return de; + } + c = hpfs_compare_names(s, name1, name1len, de->name, de->namelen, de->last); + if (c < 0 && de->down) { + dno = de_down_pointer(de); + hpfs_brelse4(qbh); + if (s->s_hpfs_chk) + if (hpfs_stop_cycles(s, dno, &c1, &c2, "map_fnode_dirent #1")) { + kfree(name2); + return NULL; + } + goto go_down; + } + f: + if (de->fnode == fno) { + kfree(name2); + return de; + } + c = hpfs_compare_names(s, name2, name2len, de->name, de->namelen, de->last); + if (c < 0 && !de->last) goto not_found; + if ((de = de_next_de(de)) < de_end) goto next_de; + if (d->root_dnode) goto not_found; + downd = dno; + dno = d->up; + hpfs_brelse4(qbh); + if (s->s_hpfs_chk) + if (hpfs_stop_cycles(s, downd, &d1, &d2, "map_fnode_dirent #2")) { + kfree(name2); + return NULL; + } + goto go_up; + not_found: + hpfs_brelse4(qbh); + hpfs_error(s, "dirent for fnode %08x not found", fno); + kfree(name2); + return NULL; +} diff --git a/fs/hpfs/ea.c b/fs/hpfs/ea.c new file mode 100644 index 000000000..6dac91fd8 --- /dev/null +++ b/fs/hpfs/ea.c @@ -0,0 +1,305 @@ +/* + * linux/fs/hpfs/ea.c + * + * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999 + * + * handling extended attributes + */ + +#include <linux/string.h> +#include "hpfs_fn.h" + +/* Remove external extended attributes. ano specifies wheter a is a + direct sector where eas start 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_KERNEL))) { + 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' */ + +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_KERNEL))) { + 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_KERNEL))) { + 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_size_s) { + /*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->ea_size_s > 0x200) { + hpfs_error(s, "fnode %08x: ea_offs == %03x, ea_size_s == %03x", + inode->i_ino, fnode->ea_offs, fnode->ea_size_s); + return; + } + if ((fnode->ea_size_s || !fnode->ea_size_l) && + fnode->ea_offs + fnode->ea_size_s + strlen(key) + size + 5 <= 0x200) { + /* I'm not sure ... maybe we overwrite ACL here. I have no info + on it right now :-( */ + 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, 1); + 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, 1); + 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, 1); + 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: + inode->i_hpfs_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; +} + diff --git a/fs/hpfs/file.c b/fs/hpfs/file.c new file mode 100644 index 000000000..7cea8dcdc --- /dev/null +++ b/fs/hpfs/file.c @@ -0,0 +1,195 @@ +/* + * linux/fs/hpfs/file.c + * + * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999 + * + * file VFS functions + */ + +#include <linux/string.h> +#include "hpfs_fn.h" + +int hpfs_open(struct inode *i, struct file *f) +{ + hpfs_lock_inode(i); + hpfs_unlock_inode(i); /* make sure nobody is deleting the file */ + if (!i->i_nlink) return -ENOENT; + return 0; +} + +int hpfs_file_release(struct inode *inode, struct file *file) +{ + hpfs_write_if_changed(inode); + return 0; +} + +int hpfs_file_fsync(struct file *file, struct dentry *dentry) +{ + /*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. + */ + +secno hpfs_bmap(struct inode *inode, unsigned file_secno) +{ + unsigned n, disk_secno; + struct fnode *fnode; + struct buffer_head *bh; + if (((inode->i_size + 511) >> 9) <= file_secno) return 0; + n = file_secno - inode->i_hpfs_file_sec; + if (n < inode->i_hpfs_n_secs) return inode->i_hpfs_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; +} + +void hpfs_truncate(struct inode *i) +{ + if (IS_IMMUTABLE(i)) return /*-EPERM*/; + i->i_hpfs_n_secs = 0; + hpfs_truncate_btree(i->i_sb, i->i_ino, 1, ((i->i_size + 511) >> 9)); + i->i_blocks = 1 + ((i->i_size + 511) >> 9); + hpfs_write_inode(i); +} + +ssize_t hpfs_file_read(struct file *filp, char *buf, size_t count, loff_t *ppos) +{ + struct inode *inode = filp->f_dentry->d_inode; + int i,j; + int a = generic_file_read(filp, buf, count, ppos); + if (inode->i_hpfs_conv != CONV_TEXT || a < 0) { + return a; + } + for (i = 0, j = 0; i < a; i++) { + char c; + int error; + if ((error = get_user(c, buf + i))) return error; + if (c != '\r') { + if (i != j) put_user(c, buf + j); + j++; + } + } + return j; +} + +ssize_t hpfs_file_write(struct file *filp, const char *buf, size_t count, + loff_t *ppos) +{ + struct inode *i = filp->f_dentry->d_inode; + int carry, error = 0; + const char *start = buf; + if (!i) return -EINVAL; + if (!S_ISREG(i->i_mode)) return -EINVAL; + if (IS_IMMUTABLE(i)) return -EPERM; + if (filp->f_flags & O_APPEND) *ppos = i->i_size; + if (count <= 0) return 0; + if ((unsigned)(*ppos+count) >= 0x80000000U || (unsigned)count >= 0x80000000U) return -EFBIG; + carry = 0; + while (count || carry) { + int ii, add = 0; + secno sec = 0; /* Go away, uninitialized variable warning */ + int offset, size, written; + char ch; + struct buffer_head *bh; + char *data; + offset = *ppos & 0x1ff; + size = count > 0x200 - offset ? 0x200 - offset : count; + if ((*ppos >> 9) < ((i->i_size + 0x1ff) >> 9)) { + i->i_hpfs_n_secs = 0; + if (!(sec = hpfs_bmap(i, *ppos >> 9))) { + hpfs_error(i->i_sb, "bmap failed, file %08x, fsec %08x", + i->i_ino, *ppos >> 9); + error =- EFSERROR; + break; + } + } else for (ii = (i->i_size + 0x1ff) >> 9, add = 1; ii <= *ppos >> 9; ii++) { + if ((sec = hpfs_add_sector_to_btree(i->i_sb, i->i_ino, 1, ii)) == -1) { + hpfs_truncate(i); + return -ENOSPC; + } + if (*ppos != i->i_size) + if ((data = hpfs_get_sector(i->i_sb, sec, &bh))) { + memset(data, 0, 512); + mark_buffer_dirty(bh, 0); + brelse(bh); + } + i->i_size = 0x200 * ii + 1; + i->i_blocks++; + /*mark_inode_dirty(i);*/i->i_hpfs_dirty = 1; + if (i->i_sb->s_hpfs_chk >= 2) { + secno bsec; + bsec = hpfs_bmap(i, ii); + if (sec != bsec) { + hpfs_error(i->i_sb, "sec == %08x, bmap returns %08x", sec, bsec); + error = -EFSERROR; + break; + } + } + PRINTK(("file_write: added %08x\n", sec)); + } + if (!sec || sec == 15) { + hpfs_error(i->i_sb, "bmap returned empty sector"); + error = -EFSERROR; + break; + } + if (i->i_sb->s_hpfs_chk) + if (hpfs_chk_sectors(i->i_sb, sec, 1, "data")) { + error = -EFSERROR; + break; + } + if ((!offset && size == 0x200) || add) + data = hpfs_get_sector(i->i_sb, sec, &bh); + else data = hpfs_map_sector(i->i_sb, sec, &bh, 0); + if (!data) { + error = -EIO; + break; + } + if (i->i_hpfs_conv != CONV_TEXT) { + memcpy_fromfs(data + offset, buf, written = size); + buf += size; + } else { + int left; + char *to; + /* LF->CR/LF conversion, stolen from fat fs */ + written = left = 0x200 - offset; + to = (char *) bh->b_data + (*ppos & 0x1ff); + if (carry) { + *to++ = '\n'; + left--; + carry = 0; + } + for (size = 0; size < count && left; size++) { + if ((error = get_user(ch, buf++))) break; + if (ch == '\n') { + *to++ = '\r'; + left--; + } + if (!left) carry = 1; + else { + *to++ = ch; + left--; + } + } + written -= left; + } + update_vm_cache(i, *ppos, bh->b_data + (*ppos & 0x1ff), written); + *ppos += written; + if (*ppos > i->i_size) { + i->i_size = *ppos; + /*mark_inode_dirty(i);*/i->i_hpfs_dirty = 1; + } + mark_buffer_dirty(bh, 0); + brelse(bh); + count -= size; + } + if (start == buf) return error; + i->i_mtime = CURRENT_TIME; + /*mark_inode_dirty(i);*/i->i_hpfs_dirty = 1; + return buf - start; +} diff --git a/fs/hpfs/hpfs_fn.h b/fs/hpfs/hpfs_fn.h new file mode 100644 index 000000000..cdb2c709e --- /dev/null +++ b/fs/hpfs/hpfs_fn.h @@ -0,0 +1,318 @@ +/* + * 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/fs.h> +#include <linux/hpfs_fs.h> +#include <linux/errno.h> +#include <linux/malloc.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/locks.h> +#include <linux/stat.h> +#include <linux/string.h> +#include <asm/bitops.h> +#include <asm/segment.h> +#include <asm/uaccess.h> + +#include <stdarg.h> + +#include "hpfs.h" + +#define memcpy_tofs memcpy +#define memcpy_fromfs memcpy + +#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 + +typedef void nonconst; /* What this is for ? */ + +/* + * local time (HPFS) to GMT (Unix) + */ + +extern inline time_t local_to_gmt(struct super_block *s, time_t t) +{ + extern struct timezone sys_tz; + return t + sys_tz.tz_minuteswest * 60 - (sys_tz.tz_dsttime ? 3600 : 0) +s->s_hpfs_timeshift; +} + +extern inline time_t gmt_to_local(struct super_block *s, time_t t) +{ + extern struct timezone sys_tz; + return t - sys_tz.tz_minuteswest * 60 + (sys_tz.tz_dsttime ? 3600 : 0) -s->s_hpfs_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 */ + +extern 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 */ + +extern inline struct hpfs_dirent *dnode_first_de (struct dnode *dnode) +{ + return (void *) dnode->dirent; +} + +/* The end+1 of the dir entries */ + +extern 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 */ + +extern 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; +} + +extern inline struct extended_attribute *fnode_ea(struct fnode *fnode) +{ + return (struct extended_attribute *)((char *)fnode + fnode->ea_offs); +} + +extern inline struct extended_attribute *fnode_end_ea(struct fnode *fnode) +{ + return (struct extended_attribute *)((char *)fnode + fnode->ea_offs + fnode->ea_size_s); +} + +extern inline struct extended_attribute *next_ea(struct extended_attribute *ea) +{ + return (struct extended_attribute *)((char *)ea + 5 + ea->namelen + ea->valuelen); +} + +extern inline secno ea_sec(struct extended_attribute *ea) +{ + return *(secno *)((char *)ea + 9 + ea->namelen); +} + +extern inline secno ea_len(struct extended_attribute *ea) +{ + return *(secno *)((char *)ea + 5 + ea->namelen); +} + +extern inline char *ea_data(struct extended_attribute *ea) +{ + return (char *)((char *)ea + 5 + ea->namelen); +} + +extern inline unsigned de_size(int namelen, secno down_ptr) +{ + return ((0x1f + namelen + 3) & ~3) + (down_ptr ? 4 : 0); +} + +extern inline void copy_de(struct hpfs_dirent *dst, struct hpfs_dirent *src) +{ + int a = dst->down; + int n = dst->not_8x3; + if (!dst || !src) return; + memcpy((char *)dst + 2, (char *)src + 2, 28); + dst->down = a; + dst->not_8x3 = n; +} + +extern 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_nolock(struct super_block *, secno); +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_lock_iget(struct super_block *, int); +void hpfs_unlock_iget(struct super_block *); +void hpfs_lock_inode(struct inode *); +void hpfs_unlock_inode(struct inode *); +void hpfs_lock_2inodes(struct inode *, struct inode *); +void hpfs_unlock_2inodes(struct inode *, struct inode *); +void hpfs_lock_3inodes(struct inode *, struct inode *, struct inode *); +void hpfs_unlock_3inodes(struct inode *, struct inode *, struct inode *); +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 */ + +int hpfs_dir_read(struct file *, char *, size_t, loff_t *); +int hpfs_dir_release(struct inode *, struct file *); +int hpfs_readdir(struct file *, void *, filldir_t); +struct dentry *hpfs_lookup(struct inode *, struct dentry *); + +/* 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); +void hpfs_delete_de(struct super_block *, struct dnode *, struct hpfs_dirent *); +int hpfs_add_to_dnode(struct inode *, dnode_secno, unsigned char *, unsigned, struct hpfs_dirent *, dnode_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); +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_release(struct inode *, struct file *); +int hpfs_open(struct inode *, struct file *); +int hpfs_file_fsync(struct file *, struct dentry *); +secno hpfs_bmap(struct inode *, unsigned); +void hpfs_truncate(struct inode *); +ssize_t hpfs_file_read(struct file *, char *, size_t, loff_t *); +ssize_t hpfs_file_write(struct file *, const char *, size_t, loff_t *); + +/* inode.c */ + +void hpfs_read_inode(struct inode *); +void hpfs_write_inode_ea(struct inode *, struct fnode *); +void hpfs_write_inode(struct inode *); +void hpfs_write_inode_nolock(struct inode *); +int hpfs_notify_change(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); + +/* mmap.c */ + +int hpfs_mmap(struct file *, struct vm_area_struct *); + +/* 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 */ + +int hpfs_mkdir(struct inode *, struct dentry *, int); +int hpfs_create(struct inode *, struct dentry *, int); +int hpfs_mknod(struct inode *, struct dentry *, int, int); +int hpfs_symlink(struct inode *, struct dentry *, const char *); +int hpfs_unlink(struct inode *, struct dentry *); +int hpfs_rmdir(struct inode *, struct dentry *); +int hpfs_readlink(struct dentry *, char *, int); +struct dentry *hpfs_follow_link(struct dentry *, struct dentry *, unsigned int); +int hpfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *); + +/* super.c */ + +void hpfs_error(struct super_block *, char *, ...); +int hpfs_stop_cycles(struct super_block *, int, int *, int *, char *); +int hpfs_remount_fs(struct super_block *, int *, char *); +void hpfs_put_super(struct super_block *); +unsigned hpfs_count_one_bitmap(struct super_block *, secno); +int hpfs_statfs(struct super_block *, struct statfs *, int); +struct super_block *hpfs_read_super(struct super_block *, void *, int); diff --git a/fs/hpfs/inode.c b/fs/hpfs/inode.c new file mode 100644 index 000000000..99bfa1004 --- /dev/null +++ b/fs/hpfs/inode.c @@ -0,0 +1,381 @@ +/* + * linux/fs/hpfs/inode.c + * + * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999 + * + * inode VFS functions + */ + +#include "hpfs_fn.h" + +static const struct file_operations hpfs_file_ops = +{ + NULL, /* lseek - default */ + hpfs_file_read, /* read */ + hpfs_file_write, /* write */ + NULL, /* readdir - bad */ + NULL, /* poll - default */ + NULL, /* ioctl - default */ + generic_file_mmap/*hpfs_mmap*/, /* mmap */ + hpfs_open, /* open */ + NULL, /* flush */ + hpfs_file_release, /* release */ + hpfs_file_fsync, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL, /* revalidate */ + NULL, /* lock */ +}; + +static const struct inode_operations hpfs_file_iops = +{ + (nonconst *) & hpfs_file_ops, /* default file operations */ + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + generic_readpage, /* readpage */ + NULL, /* writepage */ + (int (*)(struct inode *, int)) + &hpfs_bmap, /* bmap */ + &hpfs_truncate, /* truncate */ + NULL, /* permission */ + NULL, /* smap */ + NULL, /* updatepage */ + NULL, /* revalidate */ +}; + +static const struct file_operations hpfs_dir_ops = +{ + NULL, /* lseek - default */ + hpfs_dir_read, /* read */ + NULL, /* write - bad */ + hpfs_readdir, /* readdir */ + NULL, /* poll - default */ + NULL, /* ioctl - default */ + NULL, /* mmap */ + hpfs_open, /* open */ + NULL, /* flush */ + hpfs_dir_release, /* no special release code */ + hpfs_file_fsync, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL, /* revalidate */ + NULL, /* lock */ +}; + +static const struct inode_operations hpfs_dir_iops = +{ + (nonconst *) & hpfs_dir_ops, /* default directory file ops */ + hpfs_create, /* create */ + hpfs_lookup, /* lookup */ + NULL, /* link */ + hpfs_unlink, /* unlink */ + hpfs_symlink, /* symlink */ + hpfs_mkdir, /* mkdir */ + hpfs_rmdir, /* rmdir */ + hpfs_mknod, /* mknod */ + hpfs_rename, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + NULL, /* readpage */ + NULL, /* writepage */ + NULL, /* bmap */ + NULL, /* truncate */ + NULL, /* permission */ + NULL, /* smap */ + NULL, /* updatepage */ + NULL, /* revalidate */ +}; + +const struct inode_operations hpfs_symlink_iops = +{ + NULL, /* default file operations */ + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + hpfs_readlink, /* readlink */ + hpfs_follow_link, /* follow_link */ + NULL, /* readpage */ + NULL, /* writepage */ + NULL, /* bmap */ + NULL, /* truncate */ + NULL, /* permission */ + NULL, /* smap */ + NULL, /* updatepage */ + NULL, /* revalidate */ +}; + + +void hpfs_read_inode(struct inode *i) +{ + struct buffer_head *bh; + struct fnode *fnode; + struct super_block *sb = i->i_sb; + unsigned char *ea; + int ea_size; + i->i_op = 0; + /*i->i_hpfs_sem = MUTEX;*/ + init_MUTEX(&i->i_hpfs_sem); + i->i_uid = sb->s_hpfs_uid; + i->i_gid = sb->s_hpfs_gid; + i->i_mode = sb->s_hpfs_mode; + i->i_hpfs_conv = sb->s_hpfs_conv; + i->i_blksize = 512; + i->i_size = -1; + i->i_blocks = -1; + + i->i_hpfs_dno = 0; + i->i_hpfs_n_secs = 0; + i->i_hpfs_file_sec = 0; + i->i_hpfs_disk_sec = 0; + i->i_hpfs_dpos = 0; + i->i_hpfs_dsubdno = 0; + i->i_hpfs_ea_mode = 0; + i->i_hpfs_ea_uid = 0; + i->i_hpfs_ea_gid = 0; + i->i_hpfs_ea_size = 0; + i->i_version = ++event; + + i->i_hpfs_rddir_off = NULL; + i->i_hpfs_dirty = 0; + + i->i_atime = 0; + i->i_mtime = 0; + i->i_ctime = 0; + + if (!i->i_sb->s_hpfs_rd_inode) + hpfs_error(i->i_sb, "read_inode: s_hpfs_rd_inode == 0"); + if (i->i_sb->s_hpfs_rd_inode == 2) { + i->i_mode |= S_IFREG; + i->i_mode &= ~0111; + i->i_op = (struct inode_operations *) &hpfs_file_iops; + i->i_nlink = 1; + return; + } + if (!(fnode = hpfs_map_fnode(sb, i->i_ino, &bh))) { + /*i->i_mode |= S_IFREG; + i->i_mode &= ~0111; + i->i_op = (struct inode_operations *) &hpfs_file_iops; + i->i_nlink = 0;*/ + make_bad_inode(i); + return; + } + if (i->i_sb->s_hpfs_eas) { + if ((ea = hpfs_get_ea(i->i_sb, fnode, "UID", &ea_size))) { + if (ea_size == 2) { + i->i_uid = ea[0] + (ea[1] << 8); + i->i_hpfs_ea_uid = 1; + } + kfree(ea); + } + if ((ea = hpfs_get_ea(i->i_sb, fnode, "GID", &ea_size))) { + if (ea_size == 2) { + i->i_gid = ea[0] + (ea[1] << 8); + i->i_hpfs_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 = (struct inode_operations *) &hpfs_symlink_iops; + 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 = sb->s_hpfs_mode; + if (ea_size == 2) { + mode = ea[0] + (ea[1] << 8); + i->i_hpfs_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 = ea[0] + (ea[1] << 8) + (ea[2] << 16) + (ea[3] << 24); + 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, rdev); + return; + } + } + } + if (fnode->dirflag) { + unsigned n_dnodes, n_subdirs; + i->i_mode |= S_IFDIR; + i->i_op = (struct inode_operations *) &hpfs_dir_iops; + i->i_hpfs_parent_dir = fnode->up; + i->i_hpfs_dno = fnode->u.external[0].disk_secno; + if (sb->s_hpfs_chk >= 2) { + struct buffer_head *bh0; + if (hpfs_map_fnode(sb, i->i_hpfs_parent_dir, &bh0)) brelse(bh0); + } + n_dnodes = 0; n_subdirs = 0; + hpfs_count_dnodes(i->i_sb, i->i_hpfs_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 (!i->i_hpfs_ea_mode) i->i_mode &= ~0111; + i->i_op = (struct inode_operations *) &hpfs_file_iops; + i->i_nlink = 1; + i->i_size = fnode->file_size; + i->i_blocks = ((i->i_size + 511) >> 9) + 1; + } + brelse(bh); +} + +void hpfs_write_inode_ea(struct inode *i, struct fnode *fnode) +{ + 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 (i->i_sb->s_hpfs_eas >= 2) { + unsigned char ea[4]; + if ((i->i_uid != i->i_sb->s_hpfs_uid) || i->i_hpfs_ea_uid) { + ea[0] = i->i_uid & 0xff; + ea[1] = i->i_uid >> 8; + hpfs_set_ea(i, fnode, "UID", ea, 2); + i->i_hpfs_ea_uid = 1; + } + if ((i->i_gid != i->i_sb->s_hpfs_gid) || i->i_hpfs_ea_gid) { + ea[0] = i->i_gid & 0xff; + ea[1] = i->i_gid >> 8; + hpfs_set_ea(i, fnode, "GID", ea, 2); + i->i_hpfs_ea_gid = 1; + } + if (!S_ISLNK(i->i_mode)) + if ((i->i_mode != ((i->i_sb->s_hpfs_mode & ~(S_ISDIR(i->i_mode) ? 0 : 0111)) + | (S_ISDIR(i->i_mode) ? S_IFDIR : S_IFREG)) + && i->i_mode != ((i->i_sb->s_hpfs_mode & ~(S_ISDIR(i->i_mode) ? 0222 : 0333)) + | (S_ISDIR(i->i_mode) ? S_IFDIR : S_IFREG))) || i->i_hpfs_ea_mode) { + ea[0] = i->i_mode & 0xff; + ea[1] = i->i_mode >> 8; + hpfs_set_ea(i, fnode, "MODE", ea, 2); + i->i_hpfs_ea_mode = 1; + } + if (S_ISBLK(i->i_mode) || S_ISCHR(i->i_mode)) { + int d = kdev_t_to_nr(i->i_rdev); + ea[0] = d & 0xff; + ea[1] = (d >> 8) & 0xff; + ea[2] = (d >> 16) & 0xff; + ea[3] = d >> 24; + hpfs_set_ea(i, fnode, "DEV", ea, 4); + } + } +} + +void hpfs_write_inode(struct inode *i) +{ + struct inode *parent; + if (!i->i_nlink) return; + if (i->i_ino == i->i_sb->s_hpfs_root) return; + if (i->i_hpfs_rddir_off && !i->i_count) { + if (*i->i_hpfs_rddir_off) printk("HPFS: write_inode: some position still there\n"); + kfree(i->i_hpfs_rddir_off); + i->i_hpfs_rddir_off = NULL; + } + i->i_hpfs_dirty = 0; + hpfs_lock_iget(i->i_sb, 1); + parent = iget(i->i_sb, i->i_hpfs_parent_dir); + hpfs_unlock_iget(i->i_sb); + hpfs_lock_inode(parent); + hpfs_write_inode_nolock(i); + hpfs_unlock_inode(parent); + iput(parent); +} + +void hpfs_write_inode_nolock(struct inode *i) +{ + struct buffer_head *bh; + struct fnode *fnode; + struct quad_buffer_head qbh; + struct hpfs_dirent *de; + if (i->i_ino == i->i_sb->s_hpfs_root) return; + if (!(fnode = hpfs_map_fnode(i->i_sb, i->i_ino, &bh))) return; + if (i->i_ino != i->i_sb->s_hpfs_root) { + 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 = de->file_size = i->i_size; + } else if (S_ISDIR(i->i_mode)) { + fnode->file_size = de->file_size = 0; + } + hpfs_write_inode_ea(i, fnode); + if (de) { + de->write_date = gmt_to_local(i->i_sb, i->i_mtime); + de->read_date = gmt_to_local(i->i_sb, i->i_atime); + de->creation_date = gmt_to_local(i->i_sb, i->i_ctime); + de->read_only = !(i->i_mode & 0222); + de->ea_size = i->i_hpfs_ea_size; + hpfs_mark_4buffers_dirty(&qbh); + hpfs_brelse4(&qbh); + } + if (S_ISDIR(i->i_mode)) { + if ((de = map_dirent(i, i->i_hpfs_dno, "\001\001", 2, NULL, &qbh))) { + de->write_date = gmt_to_local(i->i_sb, i->i_mtime); + de->read_date = gmt_to_local(i->i_sb, i->i_atime); + de->creation_date = gmt_to_local(i->i_sb, i->i_ctime); + de->read_only = !(i->i_mode & 0222); + de->ea_size = /*i->i_hpfs_ea_size*/0; + de->file_size = 0; + hpfs_mark_4buffers_dirty(&qbh); + hpfs_brelse4(&qbh); + } else hpfs_error(i->i_sb, "directory %08x doesn't have '.' entry", i->i_ino); + } + mark_buffer_dirty(bh, 1); + brelse(bh); +} + +int hpfs_notify_change(struct dentry *dentry, struct iattr *attr) +{ + struct inode *inode = dentry->d_inode; + int error; + if (inode->i_sb->s_hpfs_root == inode->i_ino) return -EINVAL; + if ((error = inode_change_ok(inode, attr))) return error; + inode_setattr(inode, attr); + hpfs_write_inode(inode); + return 0; +} + +void hpfs_write_if_changed(struct inode *inode) +{ + if (inode->i_hpfs_dirty) { + hpfs_write_inode(inode); + } +} + +void hpfs_delete_inode(struct inode *inode) +{ + hpfs_remove_fnode(inode->i_sb, inode->i_ino); +} diff --git a/fs/hpfs/map.c b/fs/hpfs/map.c new file mode 100644 index 000000000..a0e0feada --- /dev/null +++ b/fs/hpfs/map.c @@ -0,0 +1,268 @@ +/* + * 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, s->s_hpfs_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 (s->s_hpfs_chk) if (bmp_block * 16384 > s->s_hpfs_fs_size) { + hpfs_error(s, "hpfs_map_bitmap called with bad parameter: %08x at %s", bmp_block, id); + return NULL; + } + sec = s->s_hpfs_bmp_dir[bmp_block]; + if (!sec || sec > s->s_hpfs_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 (!(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 = (s->s_hpfs_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 (s->s_hpfs_chk) if (hpfs_chk_sectors(s, ino, 1, "fnode")) { + return NULL; + } + if ((fnode = hpfs_map_sector(s, ino, bhp, FNODE_RD_AHEAD))) { + if (s->s_hpfs_chk) { + struct extended_attribute *ea; + struct extended_attribute *ea_end; + if (fnode->magic != FNODE_MAGIC) { + hpfs_error(s, "bad magic on fnode %08x", 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 %08x", 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 %08x", ino); + goto bail; + } + } + if (fnode->ea_size_s && ((signed int)fnode->ea_offs < 0xc4 || + (signed int)fnode->ea_offs + fnode->ea_size_s > 0x200)) { + hpfs_error(s, "bad EA info in fnode %08x: ea_offs == %04x ea_size_s == %04x", + 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 %08x", 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 (s->s_hpfs_chk) if (hpfs_chk_sectors(s, ano, 1, "anode")) return NULL; + if ((anode = hpfs_map_sector(s, ano, bhp, ANODE_RD_AHEAD))) + if (s->s_hpfs_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 (s->s_hpfs_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 (s->s_hpfs_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)) { + 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) { + hpfs_error(s, "namelen does not match dirent size in dnode %08x, dirent %03x, last %03x", secno, p, pp); + goto bail; + } + if (s->s_hpfs_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; +} diff --git a/fs/hpfs/mmap.c b/fs/hpfs/mmap.c new file mode 100644 index 000000000..3fd544664 --- /dev/null +++ b/fs/hpfs/mmap.c @@ -0,0 +1,128 @@ +/* + * linux/fs/hpfs/mmap.c + * + * taken from fat filesystem + * + * Written by Jacques Gelinas (jacques@solucorp.qc.ca) + * Inspired by fs/nfs/mmap.c (Jon Tombs 15 Aug 1993) + * + * Modified for HPFS by Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz) + * + * mmap handling for hpfs filesystem + * (generic_file_mmap may be used only on filesystems that keep zeros + * in last file sector beyond end) + */ + +/* + * generic_file_mmap doesn't erase the space beyond file end in last sector. :-( + * Hpfs doesn't keep zeros in last sector. This causes problems with kernel + * mkdep.c and probably other programs. Additionally this could be a security + * hole - some interesting data, like pieces of /etc/shadow could be found + * beyond file end. + * + * So, I can't use generic mmap. mmap from fat filesystem looks good, so I used + * it. + * + * BTW. fat uses generic mmap on normal disks. Doesn't it also have above bugs? + * I don't think Msdos erases space in last sector. + * + * If you fix generic_file_mmap, you can remove this file and use it. + */ + +#include "hpfs_fn.h" + +/* + * Fill in the supplied page for mmap + */ + +static unsigned long hpfs_file_mmap_nopage( + struct vm_area_struct * area, + unsigned long address, + int error_code) +{ + /*struct inode * inode = area->vm_inode;*/ + struct inode * inode = area->vm_file->f_dentry->d_inode; + unsigned long page; + unsigned int clear; + loff_t pos; + long gap; /* distance from eof to pos */ + + page = __get_free_page(GFP_KERNEL); + if (!page) + return page; + address &= PAGE_MASK; + pos = address - area->vm_start + area->vm_offset; + + clear = 0; + gap = inode->i_size - pos; + if (gap <= 0){ + /* mmaping beyond end of file */ + clear = PAGE_SIZE; + }else{ + int cur_read; + int need_read; + /*struct file *filp = area->vm_file;*/ + struct file filp; + if (gap < PAGE_SIZE){ + clear = PAGE_SIZE - gap; + } + filp.f_reada = 0; + filp.f_pos = pos; + filp.f_dentry=area->vm_file->f_dentry; + need_read = PAGE_SIZE - clear; + { + mm_segment_t cur_fs = get_fs(); + set_fs (KERNEL_DS); + cur_read = generic_file_read (&filp,(char*)page + ,need_read,&pos); + set_fs (cur_fs); + } + if (cur_read != need_read){ + hpfs_error(inode->i_sb, "Error while reading an mmap file %08x", inode->i_ino); + } + } + if (clear > 0){ + memset ((char*)page+PAGE_SIZE-clear,0,clear); + } + return page; +} + +struct vm_operations_struct hpfs_file_mmap = { + NULL, /* open */ + NULL, /* close */ + NULL, /* unmap */ + NULL, /* protect */ + NULL, /* sync */ + NULL, /* advise */ + hpfs_file_mmap_nopage, /* nopage */ + NULL, /* wppage */ + NULL, /* swapout */ + NULL, /* swapin */ +}; + +/* + * This is used for a general mmap of an msdos file + * Returns 0 if ok, or a negative error code if not. + */ +int hpfs_mmap(struct file * file, struct vm_area_struct * vma) +{ + struct inode *inode = file->f_dentry->d_inode; + /*printk("start mmap\n");*/ + if (vma->vm_flags & VM_SHARED) /* only PAGE_COW or read-only supported now */ + return -EINVAL; + if (vma->vm_offset & (inode->i_sb->s_blocksize - 1)) + return -EINVAL; + if (!inode->i_sb || !S_ISREG(inode->i_mode)) + return -EACCES; + /*if (!IS_RDONLY(inode)) { + inode->i_atime = CURRENT_TIME; + mark_inode_dirty(inode); + }*/ + + vma->vm_file = file; + /*inode->i_count++;*/ + file->f_count++; + vma->vm_ops = &hpfs_file_mmap; + /*printk("end mmap\n");*/ + return 0; +} diff --git a/fs/hpfs/name.c b/fs/hpfs/name.c new file mode 100644 index 000000000..f9fa94c7e --- /dev/null +++ b/fs/hpfs/name.c @@ -0,0 +1,144 @@ +/* + * linux/fs/hpfs/name.c + * + * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999 + * + * operations with filenames + */ + +#include <linux/string.h> +#include "hpfs_fn.h" + +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", ""}; + +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) +{ + int i; + if (inode->i_hpfs_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; + } + inode->i_hpfs_conv = CONV_BINARY; + return; + text: + inode->i_hpfs_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 (s->s_hpfs_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(s->s_hpfs_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(s->s_hpfs_cp_table,n1[i]); + unsigned char c2 = upcase(s->s_hpfs_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)--; +} diff --git a/fs/hpfs/namei.c b/fs/hpfs/namei.c new file mode 100644 index 000000000..fe9b66e3d --- /dev/null +++ b/fs/hpfs/namei.c @@ -0,0 +1,549 @@ +/* + * linux/fs/hpfs/namei.c + * + * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999 + * + * adding & removing files & directories + */ + +#include <linux/string.h> +#include "hpfs_fn.h" + +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; + if (!(fnode = hpfs_alloc_fnode(dir->i_sb, dir->i_hpfs_dno, &fno, &bh))) goto bail; + if (!(dnode = hpfs_alloc_dnode(dir->i_sb, fno, &dno, &qbh0, 1))) 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, CURRENT_TIME); + hpfs_lock_inode(dir); + r = hpfs_add_dirent(dir, (char *)name, len, &dee, 0); + if (r == 1) goto bail2; + if (r == -1) { + brelse(bh); + hpfs_brelse4(&qbh0); + hpfs_free_sectors(dir->i_sb, fno, 1); + hpfs_free_dnode(dir->i_sb, dno); + hpfs_unlock_inode(dir); + return -EEXIST; + } + 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, CURRENT_TIME); + if (!(mode & 0222)) de->read_only = 1; + de->first = de->directory = 1; + /*de->hidden = de->system = 0;*/ + de->fnode = fno; + mark_buffer_dirty(bh, 1); + brelse(bh); + hpfs_mark_4buffers_dirty(&qbh0); + hpfs_brelse4(&qbh0); + dir->i_nlink++; + hpfs_lock_iget(dir->i_sb, 1); + if ((result = iget(dir->i_sb, fno))) { + result->i_hpfs_parent_dir = dir->i_ino; + result->i_ctime = result->i_mtime = result->i_atime = local_to_gmt(dir->i_sb, dee.creation_date); + result->i_hpfs_ea_size = 0; + if (dee.read_only) result->i_mode &= ~0222; + 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); + } + hpfs_unlock_iget(dir->i_sb); + hpfs_unlock_inode(dir); + return 0; + bail2: + hpfs_brelse4(&qbh0); + hpfs_free_dnode(dir->i_sb, dno); + hpfs_unlock_inode(dir); + bail1: + brelse(bh); + hpfs_free_sectors(dir->i_sb, fno, 1); + bail: + return -ENOSPC; +} + +int hpfs_create(struct inode *dir, struct dentry *dentry, int mode) +{ + 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; + if (!(fnode = hpfs_alloc_fnode(dir->i_sb, dir->i_hpfs_dno, &fno, &bh))) 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, CURRENT_TIME); + hpfs_lock_inode(dir); + r = hpfs_add_dirent(dir, (char *)name, len, &dee, 0); + if (r == 1) goto bail1; + if (r == -1) { + brelse(bh); + hpfs_free_sectors(dir->i_sb, fno, 1); + hpfs_unlock_inode(dir); + return -EEXIST; + } + fnode->len = len; + memcpy(fnode->name, name, len > 15 ? 15 : len); + fnode->up = dir->i_ino; + mark_buffer_dirty(bh, 1); + brelse(bh); + hpfs_lock_iget(dir->i_sb, 2); + if ((result = iget(dir->i_sb, fno))) { + hpfs_decide_conv(result, (char *)name, len); + result->i_hpfs_parent_dir = dir->i_ino; + result->i_ctime = result->i_mtime = result->i_atime = local_to_gmt(dir->i_sb, dee.creation_date); + result->i_hpfs_ea_size = 0; + if (dee.read_only) result->i_mode &= ~0222; + if (result->i_blocks == -1) result->i_blocks = 1; + if (result->i_size == -1) result->i_size = 0; + 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); + } + hpfs_unlock_iget(dir->i_sb); + hpfs_unlock_inode(dir); + return 0; + bail1: + brelse(bh); + hpfs_free_sectors(dir->i_sb, fno, 1); + hpfs_unlock_inode(dir); + bail: + return -ENOSPC; +} + +int hpfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int 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 (dir->i_sb->s_hpfs_eas < 2) return -EPERM; + if (!(fnode = hpfs_alloc_fnode(dir->i_sb, dir->i_hpfs_dno, &fno, &bh))) 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, CURRENT_TIME); + hpfs_lock_inode(dir); + r = hpfs_add_dirent(dir, (char *)name, len, &dee, 0); + if (r == 1) goto bail1; + if (r == -1) { + brelse(bh); + hpfs_free_sectors(dir->i_sb, fno, 1); + hpfs_unlock_inode(dir); + return -EEXIST; + } + fnode->len = len; + memcpy(fnode->name, name, len > 15 ? 15 : len); + fnode->up = dir->i_ino; + mark_buffer_dirty(bh, 1); + hpfs_lock_iget(dir->i_sb, 2); + if ((result = iget(dir->i_sb, fno))) { + result->i_hpfs_parent_dir = dir->i_ino; + result->i_ctime = result->i_mtime = result->i_atime = local_to_gmt(dir->i_sb, dee.creation_date); + result->i_hpfs_ea_size = 0; + /*if (result->i_blocks == -1) result->i_blocks = 1; + if (result->i_size == -1) result->i_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); + hpfs_write_inode_nolock(result); + d_instantiate(dentry, result); + } + hpfs_unlock_iget(dir->i_sb); + hpfs_unlock_inode(dir); + brelse(bh); + return 0; + bail1: + brelse(bh); + hpfs_free_sectors(dir->i_sb, fno, 1); + hpfs_unlock_inode(dir); + bail: + return -ENOSPC; +} + +extern const struct inode_operations hpfs_symlink_iops; + +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; + if (dir->i_sb->s_hpfs_eas < 2) return -EPERM; + if (!(fnode = hpfs_alloc_fnode(dir->i_sb, dir->i_hpfs_dno, &fno, &bh))) 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, CURRENT_TIME); + hpfs_lock_inode(dir); + r = hpfs_add_dirent(dir, (char *)name, len, &dee, 0); + if (r == 1) goto bail1; + if (r == -1) { + brelse(bh); + hpfs_free_sectors(dir->i_sb, fno, 1); + hpfs_unlock_inode(dir); + return -EEXIST; + } + fnode->len = len; + memcpy(fnode->name, name, len > 15 ? 15 : len); + fnode->up = dir->i_ino; + mark_buffer_dirty(bh, 1); + brelse(bh); + hpfs_lock_iget(dir->i_sb, 2); + if ((result = iget(dir->i_sb, fno))) { + result->i_hpfs_parent_dir = dir->i_ino; + result->i_ctime = result->i_mtime = result->i_atime = local_to_gmt(dir->i_sb, dee.creation_date); + result->i_hpfs_ea_size = 0; + /*if (result->i_blocks == -1) result->i_blocks = 1; + if (result->i_size == -1) result->i_size = 0;*/ + result->i_mode = S_IFLNK | 0777; + result->i_uid = current->fsuid; + result->i_gid = current->fsgid; + result->i_blocks = 1; + result->i_size = strlen(symlink); + result->i_op = (struct inode_operations *) &hpfs_symlink_iops; + if ((fnode = hpfs_map_fnode(dir->i_sb, fno, &bh))) { + hpfs_set_ea(result, fnode, "SYMLINK", (char *)symlink, strlen(symlink)); + mark_buffer_dirty(bh, 1); + brelse(bh); + } + hpfs_write_inode_nolock(result); + d_instantiate(dentry, result); + } + hpfs_unlock_iget(dir->i_sb); + hpfs_unlock_inode(dir); + return 0; + bail1: + brelse(bh); + hpfs_free_sectors(dir->i_sb, fno, 1); + hpfs_unlock_inode(dir); + bail: + return -ENOSPC; +} + +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; + hpfs_adjust_length((char *)name, &len); + again: + hpfs_lock_2inodes(dir, inode); + if (!(de = map_dirent(dir, dir->i_hpfs_dno, (char *)name, len, &dno, &qbh))) { + hpfs_unlock_2inodes(dir, inode); + return -ENOENT; + } + if (de->first) { + hpfs_brelse4(&qbh); + hpfs_unlock_2inodes(dir, inode); + return -EPERM; + } + if (de->directory) { + hpfs_brelse4(&qbh); + hpfs_unlock_2inodes(dir, inode); + return -EISDIR; + } + fno = de->fnode; + if ((r = hpfs_remove_dirent(dir, dno, de, &qbh, 1)) == 1) hpfs_error(dir->i_sb, "there was error when removing dirent"); + if (r != 2) { + inode->i_nlink--; + hpfs_unlock_2inodes(dir, inode); + d_delete(dentry); + } else { /* no space for deleting, try to truncate file */ + struct iattr newattrs; + hpfs_unlock_2inodes(dir, inode); + if (rep || dentry->d_count > 1 || permission(inode, MAY_WRITE) || get_write_access(inode)) goto ret; + /*printk("HPFS: truncating file before delete.\n");*/ + down(&inode->i_sem); /* do_truncate should be called here, but it's */ + newattrs.ia_size = 0; /* not exported */ + newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME; + if (notify_change(dentry, &newattrs)) { + up(&inode->i_sem); + put_write_access(inode); + goto ret; + } + vmtruncate(inode, 0); + if (inode->i_op && inode->i_op->truncate) inode->i_op->truncate(inode); + up(&inode->i_sem); + put_write_access(inode); + rep = 1; + goto again; + } + ret: + return r == 2 ? -ENOSPC : r == 1 ? -EFSERROR : 0; +} + +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 r; + hpfs_adjust_length((char *)name, &len); + hpfs_lock_2inodes(dir, inode); + if (!(de = map_dirent(dir, dir->i_hpfs_dno, (char *)name, len, &dno, &qbh))) { + hpfs_unlock_2inodes(dir, inode); + return -ENOENT; + } + if (de->first) { + hpfs_brelse4(&qbh); + hpfs_unlock_2inodes(dir, inode); + return -EPERM; + } + if (!de->directory) { + hpfs_brelse4(&qbh); + hpfs_unlock_2inodes(dir, inode); + return -ENOTDIR; + } + if (!list_empty(&dentry->d_hash)) { + hpfs_brelse4(&qbh); + hpfs_unlock_2inodes(dir, inode); + return -EBUSY; + } + hpfs_count_dnodes(dir->i_sb, inode->i_hpfs_dno, NULL, NULL, &n_items); + if (n_items) { + hpfs_brelse4(&qbh); + hpfs_unlock_2inodes(dir, inode); + return -ENOTEMPTY; + } + fno = de->fnode; + if ((r = hpfs_remove_dirent(dir, dno, de, &qbh, 1)) == 1) + hpfs_error(dir->i_sb, "there was error when removing dirent"); + if (r != 2) { + dir->i_nlink--; + inode->i_nlink = 0; + hpfs_unlock_2inodes(dir, inode); + d_delete(dentry); + } else hpfs_unlock_2inodes(dir, inode); + return r == 2 ? -ENOSPC : r == 1 ? -EFSERROR : 0; +} + +int hpfs_readlink(struct dentry *dentry, char *buf, int len) +{ + struct inode *i = dentry->d_inode; + struct fnode *fnode; + struct buffer_head *bh; + char *symlink; + int slen; + if (!S_ISLNK(i->i_mode)) { + return -EINVAL; + } + if (!(fnode = hpfs_map_fnode(i->i_sb, i->i_ino, &bh))) { + return -EIO; + } + if (!(symlink = hpfs_get_ea(i->i_sb, fnode, "SYMLINK", &slen))) { + brelse(bh); + return -EFSERROR; + } + brelse(bh); + if (slen > len) slen = len; + memcpy_tofs(buf, symlink, slen); + kfree(symlink); + return slen; +} + +struct dentry *hpfs_follow_link(struct dentry *dinode, struct dentry *ddir, + unsigned int follow) +{ + struct inode *inode = dinode->d_inode; + char *link; + unsigned len; + struct buffer_head *bh; + struct fnode *fnode; + if (!(fnode = hpfs_map_fnode(inode->i_sb, inode->i_ino, &bh))) { + dput(dinode); + return ERR_PTR(-EIO); + } + if (!(link = hpfs_get_ea(inode->i_sb, fnode, "SYMLINK", &len))) { + brelse(bh); + dput(dinode); + return ERR_PTR(-EIO); + } + brelse(bh); + UPDATE_ATIME(inode); + ddir = lookup_dentry(link, ddir, follow); + kfree(link); + return ddir; +} + +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); + + hpfs_lock_3inodes(old_dir, new_dir, i); + + /* 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, old_dir->i_hpfs_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, new_dir->i_hpfs_dno, (char *)new_name, new_len, NULL, &qbh1))) { + new_inode->i_nlink = 0; + 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, old_dir->i_hpfs_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: + i->i_hpfs_parent_dir = new_dir->i_ino; + if (S_ISDIR(i->i_mode)) { + new_dir->i_nlink++; + old_dir->i_nlink--; + } + 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, 1); + brelse(bh); + } + i->i_hpfs_conv = i->i_sb->s_hpfs_conv; + hpfs_decide_conv(i, (char *)new_name, new_len); + end1: + hpfs_unlock_3inodes(old_dir, new_dir, i); + return err; +} diff --git a/fs/hpfs/super.c b/fs/hpfs/super.c new file mode 100644 index 000000000..a50f2a49b --- /dev/null +++ b/fs/hpfs/super.c @@ -0,0 +1,598 @@ +/* + * linux/fs/hpfs/super.c + * + * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999 + * + * mouning, unmounting, error handling + */ + +#include <linux/string.h> +#include "hpfs_fn.h" +#include <linux/module.h> + +/* Mark the filesystem dirty, so that chkdsk checks it when os/2 booted */ + +static void mark_dirty(struct super_block *s) +{ + if (s->s_hpfs_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, 1); + 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 = s->s_hpfs_chkdsk > 1 - s->s_hpfs_was_error; + sb->old_wrote = s->s_hpfs_chkdsk >= 2 && !s->s_hpfs_was_error; + mark_buffer_dirty(bh, 1); + brelse(bh); + } +} + +/* Filesystem error... */ + +#define ERR_BUF_SIZE 1024 + +void hpfs_error(struct super_block *s, char *m,...) +{ + char *buf; + va_list l; + va_start(l, m); + if (!(buf = kmalloc(ERR_BUF_SIZE, GFP_KERNEL))) + printk("HPFS: No memory for error message '%s'\n",m); + else if (vsprintf(buf, m, l) >= ERR_BUF_SIZE) + printk("HPFS: Grrrr... Kernel memory corrupted ... going on, but it'll crash very soon :-(\n"); + printk("HPFS: filesystem error: "); + if (buf) printk("%s", buf); + else printk("%s\n",m); + if (!s->s_hpfs_was_error) { + if (s->s_hpfs_err == 2) { + printk("; crashing the system because you wanted it\n"); + mark_dirty(s); + panic("HPFS panic"); + } else if (s->s_hpfs_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"); + if (buf) kfree(buf); + s->s_hpfs_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; +} + +void hpfs_put_super(struct super_block *s) +{ + if (s->s_hpfs_cp_table) kfree(s->s_hpfs_cp_table); + if (s->s_hpfs_bmp_dir) kfree(s->s_hpfs_bmp_dir); + unmark_dirty(s); + s->s_dev = 0; + MOD_DEC_USE_COUNT; +} + +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 = (s->s_hpfs_fs_size + 0x3fff) >> 14; + count = 0; + for (n = 0; n < n_bands; n++) + count += hpfs_count_one_bitmap(s, s->s_hpfs_bmp_dir[n]); + return count; +} + +int hpfs_statfs(struct super_block *s, struct statfs *buf, int bufsiz) +{ + struct statfs tmp; + /*if (s->s_hpfs_n_free == -1) {*/ + s->s_hpfs_n_free = count_bitmaps(s); + s->s_hpfs_n_free_dnodes = hpfs_count_one_bitmap(s, s->s_hpfs_dmap); + /*}*/ + tmp.f_type = s->s_magic; + tmp.f_bsize = 512; + tmp.f_blocks = s->s_hpfs_fs_size; + tmp.f_bfree = s->s_hpfs_n_free; + tmp.f_bavail = s->s_hpfs_n_free; + tmp.f_files = s->s_hpfs_dirband_size / 4; + tmp.f_ffree = s->s_hpfs_n_free_dnodes; + tmp.f_namelen = 254; + return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0; +} + +/* Super operations */ + +static const struct super_operations hpfs_sops = +{ + hpfs_read_inode, /* read_inode */ + NULL, /* write_inode */ + NULL, /* put_inode */ + hpfs_delete_inode, /* delete inode */ + hpfs_notify_change, /* notify_change */ + hpfs_put_super, /* put_super */ + NULL, /* write_super */ + hpfs_statfs, /* statfs */ + hpfs_remount_fs, /* remount_fs */ + NULL, /* clear inode */ + NULL, /* umount_begin */ +}; + +/* + * A tiny parser for option strings, stolen from dosfs. + * + * Stolen again from read-only hpfs. + */ + +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, *rhs; + + if (!opts) + return 1; + + /*printk("Parsing opts: '%s'\n",opts);*/ + + for (p = strtok(opts, ","); p != 0; p = strtok(0, ",")) { + if ((rhs = strchr(p, '=')) != 0) + *rhs++ = '\0'; + if (!strcmp(p, "help")) return 2; + if (!strcmp(p, "uid")) { + if (!rhs || !*rhs) + return 0; + *uid = simple_strtoul(rhs, &rhs, 0); + if (*rhs) + return 0; + } + else if (!strcmp(p, "gid")) { + if (!rhs || !*rhs) + return 0; + *gid = simple_strtoul(rhs, &rhs, 0); + if (*rhs) + return 0; + } + else if (!strcmp(p, "umask")) { + if (!rhs || !*rhs) + return 0; + *umask = simple_strtoul(rhs, &rhs, 8); + if (*rhs) + return 0; + } + else if (!strcmp(p, "timeshift")) { + int m = 1; + if (!rhs || !*rhs) + return 0; + if (*rhs == '-') m = -1; + if (*rhs == '+' || *rhs == '-') rhs++; + *timeshift = simple_strtoul(rhs, &rhs, 0) * m; + if (*rhs) + return 0; + } + else if (!strcmp(p, "case")) { + if (!strcmp(rhs, "lower")) + *lowercase = 1; + else if (!strcmp(rhs, "asis")) + *lowercase = 0; + else + return 0; + } + else if (!strcmp(p, "conv")) { + if (!strcmp(rhs, "binary")) + *conv = CONV_BINARY; + else if (!strcmp(rhs, "text")) + *conv = CONV_TEXT; + else if (!strcmp(rhs, "auto")) + *conv = CONV_AUTO; + else + return 0; + } + else if (!strcmp(p, "check")) { + if (!strcmp(rhs, "none")) + *chk = 0; + else if (!strcmp(rhs, "normal")) + *chk = 1; + else if (!strcmp(rhs, "strict")) + *chk = 2; + else + return 0; + } + else if (!strcmp(p, "errors")) { + if (!strcmp(rhs, "continue")) + *errs = 0; + else if (!strcmp(rhs, "remount-ro")) + *errs = 1; + else if (!strcmp(rhs, "panic")) + *errs = 2; + else + return 0; + } + else if (!strcmp(p, "eas")) { + if (!strcmp(rhs, "no")) + *eas = 0; + else if (!strcmp(rhs, "ro")) + *eas = 1; + else if (!strcmp(rhs, "rw")) + *eas = 2; + else + return 0; + } + else if (!strcmp(p, "chkdsk")) { + if (!strcmp(rhs, "no")) + *chkdsk = 0; + else if (!strcmp(rhs, "errors")) + *chkdsk = 1; + else if (!strcmp(rhs, "always")) + *chkdsk = 2; + else + return 0; + } + else + return 0; + } + return 1; +} + +static inline void hpfs_help() +{ + 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"); +} + +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; + + *flags |= MS_NOATIME; + + uid = s->s_hpfs_uid; gid = s->s_hpfs_gid; + umask = 0777 & ~s->s_hpfs_mode; + lowercase = s->s_hpfs_lowercase; conv = s->s_hpfs_conv; + eas = s->s_hpfs_eas; chk = s->s_hpfs_chk; chkdsk = s->s_hpfs_chkdsk; + errs = s->s_hpfs_err; timeshift = s->s_hpfs_timeshift; + + if (!(o = parse_opts(data, &uid, &gid, &umask, &lowercase, &conv, + &eas, &chk, &errs, &chkdsk, ×hift))) { + printk("HPFS: bad mount options.\n"); + return 1; + } + if (o == 2) { + hpfs_help(); + return 1; + } + if (timeshift != s->s_hpfs_timeshift) { + printk("HPFS: timeshift can't be changed using remount.\n"); + return 1; + } + + unmark_dirty(s); + + s->s_hpfs_uid = uid; s->s_hpfs_gid = gid; + s->s_hpfs_mode = 0777 & ~umask; + s->s_hpfs_lowercase = lowercase; s->s_hpfs_conv = conv; + s->s_hpfs_eas = eas; s->s_hpfs_chk = chk; s->s_hpfs_chkdsk = chkdsk; + s->s_hpfs_err = errs; s->s_hpfs_timeshift = timeshift; + + if (!(*flags & MS_RDONLY)) mark_dirty(s); + + return 0; +} + +struct super_block *hpfs_read_super(struct super_block *s, void *options, + int silent) +{ + kdev_t dev; + struct buffer_head *bh0, *bh1, *bh2; + struct hpfs_boot_block *bootblock; + struct hpfs_super_block *superblock; + struct hpfs_spare_block *spareblock; + + 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; + + MOD_INC_USE_COUNT; + + s->s_hpfs_bmp_dir = NULL; + s->s_hpfs_cp_table = NULL; + + s->s_hpfs_creation_de_lock = s->s_hpfs_rd_inode = 0; + init_waitqueue_head(&s->s_hpfs_creation_de); + init_waitqueue_head(&s->s_hpfs_iget_q); + + uid = current->uid; + gid = current->gid; + umask = current->fs->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, ×hift))) { + printk("HPFS: bad mount options.\n"); + goto bail0; + } + if (o==2) { + hpfs_help(); + goto bail0; + } + + /*s->s_hpfs_mounting = 1;*/ + lock_super(s); + dev = s->s_dev; + set_blocksize(dev, 512); + s->s_hpfs_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_blocksize = 512; + s->s_blocksize_bits = 9; + s->s_op = (struct super_operations *) &hpfs_sops; + + s->s_hpfs_root = superblock->root; + s->s_hpfs_fs_size = superblock->n_sectors; + s->s_hpfs_bitmaps = superblock->bitmaps; + s->s_hpfs_dirband_start = superblock->dir_band_start; + s->s_hpfs_dirband_size = superblock->n_dir_band; + s->s_hpfs_dmap = superblock->dir_band_bitmap; + s->s_hpfs_uid = uid; + s->s_hpfs_gid = gid; + s->s_hpfs_mode = 0777 & ~umask; + s->s_hpfs_n_free = -1; + s->s_hpfs_n_free_dnodes = -1; + s->s_hpfs_lowercase = lowercase; + s->s_hpfs_conv = conv; + s->s_hpfs_eas = eas; + s->s_hpfs_chk = chk; + s->s_hpfs_chkdsk = chkdsk; + s->s_hpfs_err = errs; + s->s_hpfs_timeshift = timeshift; + s->s_hpfs_was_error = 0; + s->s_hpfs_cp_table = NULL; + s->s_hpfs_c_bitmap = -1; + + /* Load bitmap directory */ + if (!(s->s_hpfs_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, 1); + } + + 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 = s->s_hpfs_dirband_size; + s->s_hpfs_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; + } + s->s_hpfs_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 (!(s->s_hpfs_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); + + hpfs_lock_iget(s, 1); + s->s_root = d_alloc_root(iget(s, s->s_hpfs_root)); + hpfs_unlock_iget(s); + unlock_super(s); + if (!s->s_root || !s->s_root->d_inode) { + printk("HPFS: iget failed. Why???\n"); + 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, s->s_hpfs_root); + if (root_dno) + de = map_dirent(s->s_root->d_inode, root_dno, "\001\001", 2, NULL, &qbh); + if (!root_dno || !de) hpfs_error(s, "unable to find root dir"); + else { + s->s_root->d_inode->i_atime = local_to_gmt(s, de->read_date); + s->s_root->d_inode->i_mtime = local_to_gmt(s, de->write_date); + s->s_root->d_inode->i_ctime = local_to_gmt(s, de->creation_date); + s->s_root->d_inode->i_hpfs_ea_size = de->ea_size; + s->s_root->d_inode->i_hpfs_parent_dir = s->s_root->d_inode->i_ino; + if (s->s_root->d_inode->i_size == -1) s->s_root->d_inode->i_size = 2048; + if (s->s_root->d_inode->i_blocks == -1) s->s_root->d_inode->i_blocks = 5; + } + if (de) hpfs_brelse4(&qbh); + + return s; + +bail4: brelse(bh2); +bail3: brelse(bh1); +bail2: brelse(bh0); +bail1: unlock_super(s); +bail0: s->s_dev = 0; + if (s->s_hpfs_bmp_dir) kfree(s->s_hpfs_bmp_dir); + if (s->s_hpfs_cp_table) kfree(s->s_hpfs_cp_table); + MOD_DEC_USE_COUNT; + return NULL; +} + +struct file_system_type hpfs_fs_type = { + "hpfs", FS_REQUIRES_DEV, hpfs_read_super, NULL +}; + +int init_hpfs_fs(void) +{ + return register_filesystem(&hpfs_fs_type); +} + +#ifdef MODULE + +/*int register_symtab_from(struct symbol_table *, long *);*/ + +int init_module(void) +{ + /*int status; + if (!(status = init_hpfs_fs())) register_symtab(NULL); + return status;*/ + return init_hpfs_fs(); +} + +void cleanup_module(void) +{ + unregister_filesystem(&hpfs_fs_type); +} + +#endif diff --git a/fs/proc/sysvipc.c b/fs/proc/sysvipc.c new file mode 100644 index 000000000..eab3e3186 --- /dev/null +++ b/fs/proc/sysvipc.c @@ -0,0 +1,138 @@ +/* + * linux/fs/proc/sysvipc.c + * + * Copyright (c) 1999 Dragos Acostachioaie + * + * This code is derived from linux/fs/proc/generic.c, + * which is Copyright (C) 1991, 1992 Linus Torvalds. + * + * /proc/sysvipc directory handling functions + */ +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/proc_fs.h> +#include <linux/stat.h> +#include <linux/mm.h> + +#include <asm/uaccess.h> + +#ifndef MIN +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +/* 4K page size but our output routines use some slack for overruns */ +#define PROC_BLOCK_SIZE (3*1024) + +static ssize_t +proc_sysvipc_read(struct file * file, char * buf, size_t nbytes, loff_t *ppos) +{ + struct inode * inode = file->f_dentry->d_inode; + char *page; + ssize_t retval=0; + int eof=0; + ssize_t n, count; + char *start; + struct proc_dir_entry * dp; + + dp = (struct proc_dir_entry *) inode->u.generic_ip; + if (!(page = (char*) __get_free_page(GFP_KERNEL))) + return -ENOMEM; + + while ((nbytes > 0) && !eof) + { + count = MIN(PROC_BLOCK_SIZE, nbytes); + + start = NULL; + if (dp->get_info) { + /* + * Handle backwards compatibility with the old net + * routines. + * + * XXX What gives with the file->f_flags & O_ACCMODE + * test? Seems stupid to me.... + */ + n = dp->get_info(page, &start, *ppos, count, + (file->f_flags & O_ACCMODE) == O_RDWR); + if (n < count) + eof = 1; + } else if (dp->read_proc) { + n = dp->read_proc(page, &start, *ppos, + count, &eof, dp->data); + } else + break; + + if (!start) { + /* + * For proc files that are less than 4k + */ + start = page + *ppos; + n -= *ppos; + if (n <= 0) + break; + if (n > count) + n = count; + } + if (n == 0) + break; /* End of file */ + if (n < 0) { + if (retval == 0) + retval = n; + break; + } + + /* This is a hack to allow mangling of file pos independent + * of actual bytes read. Simply place the data at page, + * return the bytes, and set `start' to the desired offset + * as an unsigned int. - Paul.Russell@rustcorp.com.au + */ + n -= copy_to_user(buf, start < page ? page : start, n); + if (n == 0) { + if (retval == 0) + retval = -EFAULT; + break; + } + + *ppos += start < page ? (long)start : n; /* Move down the file */ + nbytes -= n; + buf += n; + retval += n; + } + free_page((unsigned long) page); + return retval; +} + +static struct file_operations proc_sysvipc_operations = { + NULL, /* lseek */ + proc_sysvipc_read, /* read */ + NULL, /* write */ + NULL, /* readdir */ + NULL, /* poll */ + NULL, /* ioctl */ + NULL, /* mmap */ + NULL, /* no special open code */ + NULL, /* no special release code */ + NULL /* can't fsync */ +}; + +/* + * proc directories can do almost nothing.. + */ +struct inode_operations proc_sysvipc_inode_operations = { + &proc_sysvipc_operations, /* default net file-ops */ + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + NULL, /* readpage */ + NULL, /* writepage */ + NULL, /* bmap */ + NULL, /* truncate */ + NULL /* permission */ +}; |