diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /fs/hpfs | |
download | kernel_samsung_smdk4412-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.tar.gz kernel_samsung_smdk4412-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.tar.bz2 kernel_samsung_smdk4412-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.zip |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'fs/hpfs')
-rw-r--r-- | fs/hpfs/Makefile | 8 | ||||
-rw-r--r-- | fs/hpfs/alloc.c | 456 | ||||
-rw-r--r-- | fs/hpfs/anode.c | 491 | ||||
-rw-r--r-- | fs/hpfs/buffer.c | 175 | ||||
-rw-r--r-- | fs/hpfs/dentry.c | 60 | ||||
-rw-r--r-- | fs/hpfs/dir.c | 320 | ||||
-rw-r--r-- | fs/hpfs/dnode.c | 1080 | ||||
-rw-r--r-- | fs/hpfs/ea.c | 363 | ||||
-rw-r--r-- | fs/hpfs/file.c | 140 | ||||
-rw-r--r-- | fs/hpfs/hpfs.h | 493 | ||||
-rw-r--r-- | fs/hpfs/hpfs_fn.h | 338 | ||||
-rw-r--r-- | fs/hpfs/inode.c | 291 | ||||
-rw-r--r-- | fs/hpfs/map.c | 275 | ||||
-rw-r--r-- | fs/hpfs/name.c | 144 | ||||
-rw-r--r-- | fs/hpfs/namei.c | 673 | ||||
-rw-r--r-- | fs/hpfs/super.c | 701 |
16 files changed, 6008 insertions, 0 deletions
diff --git a/fs/hpfs/Makefile b/fs/hpfs/Makefile new file mode 100644 index 00000000000..57b786fb982 --- /dev/null +++ b/fs/hpfs/Makefile @@ -0,0 +1,8 @@ +# +# Makefile for the Linux hpfs filesystem routines. +# + +obj-$(CONFIG_HPFS_FS) += hpfs.o + +hpfs-objs := alloc.o anode.o buffer.o dentry.o dir.o dnode.o ea.o file.o \ + inode.o map.o name.o namei.o super.o diff --git a/fs/hpfs/alloc.c b/fs/hpfs/alloc.c new file mode 100644 index 00000000000..5503e2c2891 --- /dev/null +++ b/fs/hpfs/alloc.c @@ -0,0 +1,456 @@ +/* + * linux/fs/hpfs/alloc.c + * + * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999 + * + * HPFS bitmap operations + */ + +#include "hpfs_fn.h" + +static int hpfs_alloc_if_possible_nolock(struct super_block *s, secno sec); + +/* + * Check if a sector is allocated in bitmap + * This is really slow. Turned on only if chk==2 + */ + +static int chk_if_allocated(struct super_block *s, secno sec, char *msg) +{ + struct quad_buffer_head qbh; + unsigned *bmp; + if (!(bmp = hpfs_map_bitmap(s, sec >> 14, &qbh, "chk"))) goto fail; + if ((bmp[(sec & 0x3fff) >> 5] >> (sec & 0x1f)) & 1) { + hpfs_error(s, "sector '%s' - %08x not allocated in bitmap", msg, sec); + goto fail1; + } + hpfs_brelse4(&qbh); + if (sec >= hpfs_sb(s)->sb_dirband_start && sec < hpfs_sb(s)->sb_dirband_start + hpfs_sb(s)->sb_dirband_size) { + unsigned ssec = (sec - hpfs_sb(s)->sb_dirband_start) / 4; + if (!(bmp = hpfs_map_dnode_bitmap(s, &qbh))) goto fail; + if ((bmp[ssec >> 5] >> (ssec & 0x1f)) & 1) { + hpfs_error(s, "sector '%s' - %08x not allocated in directory bitmap", msg, sec); + goto fail1; + } + hpfs_brelse4(&qbh); + } + return 0; + fail1: + hpfs_brelse4(&qbh); + fail: + return 1; +} + +/* + * Check if sector(s) have proper number and additionally check if they're + * allocated in bitmap. + */ + +int hpfs_chk_sectors(struct super_block *s, secno start, int len, char *msg) +{ + if (start + len < start || start < 0x12 || + start + len > hpfs_sb(s)->sb_fs_size) { + hpfs_error(s, "sector(s) '%s' badly placed at %08x", msg, start); + return 1; + } + if (hpfs_sb(s)->sb_chk>=2) { + int i; + for (i = 0; i < len; i++) + if (chk_if_allocated(s, start + i, msg)) return 1; + } + return 0; +} + +static secno alloc_in_bmp(struct super_block *s, secno near, unsigned n, unsigned forward) +{ + struct quad_buffer_head qbh; + unsigned *bmp; + unsigned bs = near & ~0x3fff; + unsigned nr = (near & 0x3fff) & ~(n - 1); + /*unsigned mnr;*/ + unsigned i, q; + int a, b; + secno ret = 0; + if (n != 1 && n != 4) { + hpfs_error(s, "Bad allocation size: %d", n); + return 0; + } + lock_super(s); + if (bs != ~0x3fff) { + if (!(bmp = hpfs_map_bitmap(s, near >> 14, &qbh, "aib"))) goto uls; + } else { + if (!(bmp = hpfs_map_dnode_bitmap(s, &qbh))) goto uls; + } + if (!tstbits(bmp, nr, n + forward)) { + ret = bs + nr; + goto rt; + } + /*if (!tstbits(bmp, nr + n, n + forward)) { + ret = bs + nr + n; + goto rt; + }*/ + q = nr + n; b = 0; + while ((a = tstbits(bmp, q, n + forward)) != 0) { + q += a; + if (n != 1) q = ((q-1)&~(n-1))+n; + if (!b) { + if (q>>5 != nr>>5) { + b = 1; + q = nr & 0x1f; + } + } else if (q > nr) break; + } + if (!a) { + ret = bs + q; + goto rt; + } + nr >>= 5; + /*for (i = nr + 1; i != nr; i++, i &= 0x1ff) {*/ + i = nr; + do { + if (!bmp[i]) goto cont; + if (n + forward >= 0x3f && bmp[i] != -1) goto cont; + q = i<<5; + if (i > 0) { + unsigned k = bmp[i-1]; + while (k & 0x80000000) { + q--; k <<= 1; + } + } + if (n != 1) q = ((q-1)&~(n-1))+n; + while ((a = tstbits(bmp, q, n + forward)) != 0) { + q += a; + if (n != 1) q = ((q-1)&~(n-1))+n; + if (q>>5 > i) break; + } + if (!a) { + ret = bs + q; + goto rt; + } + cont: + i++, i &= 0x1ff; + } while (i != nr); + rt: + if (ret) { + if (hpfs_sb(s)->sb_chk && ((ret >> 14) != (bs >> 14) || (bmp[(ret & 0x3fff) >> 5] | ~(((1 << n) - 1) << (ret & 0x1f))) != 0xffffffff)) { + hpfs_error(s, "Allocation doesn't work! Wanted %d, allocated at %08x", n, ret); + ret = 0; + goto b; + } + bmp[(ret & 0x3fff) >> 5] &= ~(((1 << n) - 1) << (ret & 0x1f)); + hpfs_mark_4buffers_dirty(&qbh); + } + b: + hpfs_brelse4(&qbh); + uls: + unlock_super(s); + return ret; +} + +/* + * Allocation strategy: 1) search place near the sector specified + * 2) search bitmap where free sectors last found + * 3) search all bitmaps + * 4) search all bitmaps ignoring number of pre-allocated + * sectors + */ + +secno hpfs_alloc_sector(struct super_block *s, secno near, unsigned n, int forward, int lock) +{ + secno sec; + int i; + unsigned n_bmps; + struct hpfs_sb_info *sbi = hpfs_sb(s); + int f_p = 0; + int near_bmp; + if (forward < 0) { + forward = -forward; + f_p = 1; + } + if (lock) hpfs_lock_creation(s); + n_bmps = (sbi->sb_fs_size + 0x4000 - 1) >> 14; + if (near && near < sbi->sb_fs_size) { + if ((sec = alloc_in_bmp(s, near, n, f_p ? forward : forward/4))) goto ret; + near_bmp = near >> 14; + } else near_bmp = n_bmps / 2; + /* + if (b != -1) { + if ((sec = alloc_in_bmp(s, b<<14, n, f_p ? forward : forward/2))) { + b &= 0x0fffffff; + goto ret; + } + if (b > 0x10000000) if ((sec = alloc_in_bmp(s, (b&0xfffffff)<<14, n, f_p ? forward : 0))) goto ret; + */ + if (!f_p) if (forward > sbi->sb_max_fwd_alloc) forward = sbi->sb_max_fwd_alloc; + less_fwd: + for (i = 0; i < n_bmps; i++) { + if (near_bmp+i < n_bmps && ((sec = alloc_in_bmp(s, (near_bmp+i) << 14, n, forward)))) { + sbi->sb_c_bitmap = near_bmp+i; + goto ret; + } + if (!forward) { + if (near_bmp-i-1 >= 0 && ((sec = alloc_in_bmp(s, (near_bmp-i-1) << 14, n, forward)))) { + sbi->sb_c_bitmap = near_bmp-i-1; + goto ret; + } + } else { + if (near_bmp+i >= n_bmps && ((sec = alloc_in_bmp(s, (near_bmp+i-n_bmps) << 14, n, forward)))) { + sbi->sb_c_bitmap = near_bmp+i-n_bmps; + goto ret; + } + } + if (i == 1 && sbi->sb_c_bitmap != -1 && ((sec = alloc_in_bmp(s, (sbi->sb_c_bitmap) << 14, n, forward)))) { + goto ret; + } + } + if (!f_p) { + if (forward) { + sbi->sb_max_fwd_alloc = forward * 3 / 4; + forward /= 2; + goto less_fwd; + } + } + sec = 0; + ret: + if (sec && f_p) { + for (i = 0; i < forward; i++) { + if (!hpfs_alloc_if_possible_nolock(s, sec + i + 1)) { + hpfs_error(s, "Prealloc doesn't work! Wanted %d, allocated at %08x, can't allocate %d", forward, sec, i); + sec = 0; + break; + } + } + } + if (lock) hpfs_unlock_creation(s); + return sec; +} + +static secno alloc_in_dirband(struct super_block *s, secno near, int lock) +{ + unsigned nr = near; + secno sec; + struct hpfs_sb_info *sbi = hpfs_sb(s); + if (nr < sbi->sb_dirband_start) + nr = sbi->sb_dirband_start; + if (nr >= sbi->sb_dirband_start + sbi->sb_dirband_size) + nr = sbi->sb_dirband_start + sbi->sb_dirband_size - 4; + nr -= sbi->sb_dirband_start; + nr >>= 2; + if (lock) hpfs_lock_creation(s); + sec = alloc_in_bmp(s, (~0x3fff) | nr, 1, 0); + if (lock) hpfs_unlock_creation(s); + if (!sec) return 0; + return ((sec & 0x3fff) << 2) + sbi->sb_dirband_start; +} + +/* Alloc sector if it's free */ + +static int hpfs_alloc_if_possible_nolock(struct super_block *s, secno sec) +{ + struct quad_buffer_head qbh; + unsigned *bmp; + lock_super(s); + if (!(bmp = hpfs_map_bitmap(s, sec >> 14, &qbh, "aip"))) goto end; + if (bmp[(sec & 0x3fff) >> 5] & (1 << (sec & 0x1f))) { + bmp[(sec & 0x3fff) >> 5] &= ~(1 << (sec & 0x1f)); + hpfs_mark_4buffers_dirty(&qbh); + hpfs_brelse4(&qbh); + unlock_super(s); + return 1; + } + hpfs_brelse4(&qbh); + end: + unlock_super(s); + return 0; +} + +int hpfs_alloc_if_possible(struct super_block *s, secno sec) +{ + int r; + hpfs_lock_creation(s); + r = hpfs_alloc_if_possible_nolock(s, sec); + hpfs_unlock_creation(s); + return r; +} + +/* Free sectors in bitmaps */ + +void hpfs_free_sectors(struct super_block *s, secno sec, unsigned n) +{ + struct quad_buffer_head qbh; + unsigned *bmp; + struct hpfs_sb_info *sbi = hpfs_sb(s); + /*printk("2 - ");*/ + if (!n) return; + if (sec < 0x12) { + hpfs_error(s, "Trying to free reserved sector %08x", sec); + return; + } + lock_super(s); + sbi->sb_max_fwd_alloc += n > 0xffff ? 0xffff : n; + if (sbi->sb_max_fwd_alloc > 0xffffff) sbi->sb_max_fwd_alloc = 0xffffff; + new_map: + if (!(bmp = hpfs_map_bitmap(s, sec >> 14, &qbh, "free"))) { + unlock_super(s); + return; + } + new_tst: + if ((bmp[(sec & 0x3fff) >> 5] >> (sec & 0x1f) & 1)) { + hpfs_error(s, "sector %08x not allocated", sec); + hpfs_brelse4(&qbh); + unlock_super(s); + return; + } + bmp[(sec & 0x3fff) >> 5] |= 1 << (sec & 0x1f); + if (!--n) { + hpfs_mark_4buffers_dirty(&qbh); + hpfs_brelse4(&qbh); + unlock_super(s); + return; + } + if (!(++sec & 0x3fff)) { + hpfs_mark_4buffers_dirty(&qbh); + hpfs_brelse4(&qbh); + goto new_map; + } + goto new_tst; +} + +/* + * Check if there are at least n free dnodes on the filesystem. + * Called before adding to dnode. If we run out of space while + * splitting dnodes, it would corrupt dnode tree. + */ + +int hpfs_check_free_dnodes(struct super_block *s, int n) +{ + int n_bmps = (hpfs_sb(s)->sb_fs_size + 0x4000 - 1) >> 14; + int b = hpfs_sb(s)->sb_c_bitmap & 0x0fffffff; + int i, j; + unsigned *bmp; + struct quad_buffer_head qbh; + if ((bmp = hpfs_map_dnode_bitmap(s, &qbh))) { + for (j = 0; j < 512; j++) { + unsigned k; + if (!bmp[j]) continue; + for (k = bmp[j]; k; k >>= 1) if (k & 1) if (!--n) { + hpfs_brelse4(&qbh); + return 0; + } + } + } + hpfs_brelse4(&qbh); + i = 0; + if (hpfs_sb(s)->sb_c_bitmap != -1) { + bmp = hpfs_map_bitmap(s, b, &qbh, "chkdn1"); + goto chk_bmp; + } + chk_next: + if (i == b) i++; + if (i >= n_bmps) return 1; + bmp = hpfs_map_bitmap(s, i, &qbh, "chkdn2"); + chk_bmp: + if (bmp) { + for (j = 0; j < 512; j++) { + unsigned k; + if (!bmp[j]) continue; + for (k = 0xf; k; k <<= 4) + if ((bmp[j] & k) == k) { + if (!--n) { + hpfs_brelse4(&qbh); + return 0; + } + } + } + hpfs_brelse4(&qbh); + } + i++; + goto chk_next; +} + +void hpfs_free_dnode(struct super_block *s, dnode_secno dno) +{ + if (hpfs_sb(s)->sb_chk) if (dno & 3) { + hpfs_error(s, "hpfs_free_dnode: dnode %08x not aligned", dno); + return; + } + if (dno < hpfs_sb(s)->sb_dirband_start || + dno >= hpfs_sb(s)->sb_dirband_start + hpfs_sb(s)->sb_dirband_size) { + hpfs_free_sectors(s, dno, 4); + } else { + struct quad_buffer_head qbh; + unsigned *bmp; + unsigned ssec = (dno - hpfs_sb(s)->sb_dirband_start) / 4; + lock_super(s); + if (!(bmp = hpfs_map_dnode_bitmap(s, &qbh))) { + unlock_super(s); + return; + } + bmp[ssec >> 5] |= 1 << (ssec & 0x1f); + hpfs_mark_4buffers_dirty(&qbh); + hpfs_brelse4(&qbh); + unlock_super(s); + } +} + +struct dnode *hpfs_alloc_dnode(struct super_block *s, secno near, + dnode_secno *dno, struct quad_buffer_head *qbh, + int lock) +{ + struct dnode *d; + if (hpfs_count_one_bitmap(s, hpfs_sb(s)->sb_dmap) > FREE_DNODES_ADD) { + if (!(*dno = alloc_in_dirband(s, near, lock))) + if (!(*dno = hpfs_alloc_sector(s, near, 4, 0, lock))) return NULL; + } else { + if (!(*dno = hpfs_alloc_sector(s, near, 4, 0, lock))) + if (!(*dno = alloc_in_dirband(s, near, lock))) return NULL; + } + if (!(d = hpfs_get_4sectors(s, *dno, qbh))) { + hpfs_free_dnode(s, *dno); + return NULL; + } + memset(d, 0, 2048); + d->magic = DNODE_MAGIC; + d->first_free = 52; + d->dirent[0] = 32; + d->dirent[2] = 8; + d->dirent[30] = 1; + d->dirent[31] = 255; + d->self = *dno; + return d; +} + +struct fnode *hpfs_alloc_fnode(struct super_block *s, secno near, fnode_secno *fno, + struct buffer_head **bh) +{ + struct fnode *f; + if (!(*fno = hpfs_alloc_sector(s, near, 1, FNODE_ALLOC_FWD, 1))) return NULL; + if (!(f = hpfs_get_sector(s, *fno, bh))) { + hpfs_free_sectors(s, *fno, 1); + return NULL; + } + memset(f, 0, 512); + f->magic = FNODE_MAGIC; + f->ea_offs = 0xc4; + f->btree.n_free_nodes = 8; + f->btree.first_free = 8; + return f; +} + +struct anode *hpfs_alloc_anode(struct super_block *s, secno near, anode_secno *ano, + struct buffer_head **bh) +{ + struct anode *a; + if (!(*ano = hpfs_alloc_sector(s, near, 1, ANODE_ALLOC_FWD, 1))) return NULL; + if (!(a = hpfs_get_sector(s, *ano, bh))) { + hpfs_free_sectors(s, *ano, 1); + return NULL; + } + memset(a, 0, 512); + a->magic = ANODE_MAGIC; + a->self = *ano; + a->btree.n_free_nodes = 40; + a->btree.n_used_nodes = 0; + a->btree.first_free = 8; + return a; +} diff --git a/fs/hpfs/anode.c b/fs/hpfs/anode.c new file mode 100644 index 00000000000..1aa88c4e096 --- /dev/null +++ b/fs/hpfs/anode.c @@ -0,0 +1,491 @@ +/* + * linux/fs/hpfs/anode.c + * + * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999 + * + * handling HPFS anode tree that contains file allocation info + */ + +#include "hpfs_fn.h" + +/* Find a sector in allocation tree */ + +secno hpfs_bplus_lookup(struct super_block *s, struct inode *inode, + struct bplus_header *btree, unsigned sec, + struct buffer_head *bh) +{ + anode_secno a = -1; + struct anode *anode; + int i; + int c1, c2 = 0; + go_down: + if (hpfs_sb(s)->sb_chk) if (hpfs_stop_cycles(s, a, &c1, &c2, "hpfs_bplus_lookup")) return -1; + if (btree->internal) { + for (i = 0; i < btree->n_used_nodes; i++) + if (btree->u.internal[i].file_secno > sec) { + a = btree->u.internal[i].down; + brelse(bh); + if (!(anode = hpfs_map_anode(s, a, &bh))) return -1; + btree = &anode->btree; + goto go_down; + } + hpfs_error(s, "sector %08x not found in internal anode %08x", sec, a); + brelse(bh); + return -1; + } + for (i = 0; i < btree->n_used_nodes; i++) + if (btree->u.external[i].file_secno <= sec && + btree->u.external[i].file_secno + btree->u.external[i].length > sec) { + a = btree->u.external[i].disk_secno + sec - btree->u.external[i].file_secno; + if (hpfs_sb(s)->sb_chk) if (hpfs_chk_sectors(s, a, 1, "data")) { + brelse(bh); + return -1; + } + if (inode) { + struct hpfs_inode_info *hpfs_inode = hpfs_i(inode); + hpfs_inode->i_file_sec = btree->u.external[i].file_secno; + hpfs_inode->i_disk_sec = btree->u.external[i].disk_secno; + hpfs_inode->i_n_secs = btree->u.external[i].length; + } + brelse(bh); + return a; + } + hpfs_error(s, "sector %08x not found in external anode %08x", sec, a); + brelse(bh); + return -1; +} + +/* Add a sector to tree */ + +secno hpfs_add_sector_to_btree(struct super_block *s, secno node, int fnod, unsigned fsecno) +{ + struct bplus_header *btree; + struct anode *anode = NULL, *ranode = NULL; + struct fnode *fnode; + anode_secno a, na = -1, ra, up = -1; + secno se; + struct buffer_head *bh, *bh1, *bh2; + int n; + unsigned fs; + int c1, c2 = 0; + if (fnod) { + if (!(fnode = hpfs_map_fnode(s, node, &bh))) return -1; + btree = &fnode->btree; + } else { + if (!(anode = hpfs_map_anode(s, node, &bh))) return -1; + btree = &anode->btree; + } + a = node; + go_down: + if ((n = btree->n_used_nodes - 1) < -!!fnod) { + hpfs_error(s, "anode %08x has no entries", a); + brelse(bh); + return -1; + } + if (btree->internal) { + a = btree->u.internal[n].down; + btree->u.internal[n].file_secno = -1; + mark_buffer_dirty(bh); + brelse(bh); + if (hpfs_sb(s)->sb_chk) + if (hpfs_stop_cycles(s, a, &c1, &c2, "hpfs_add_sector_to_btree #1")) return -1; + if (!(anode = hpfs_map_anode(s, a, &bh))) return -1; + btree = &anode->btree; + goto go_down; + } + if (n >= 0) { + if (btree->u.external[n].file_secno + btree->u.external[n].length != fsecno) { + hpfs_error(s, "allocated size %08x, trying to add sector %08x, %cnode %08x", + btree->u.external[n].file_secno + btree->u.external[n].length, fsecno, + fnod?'f':'a', node); + brelse(bh); + return -1; + } + if (hpfs_alloc_if_possible(s, se = btree->u.external[n].disk_secno + btree->u.external[n].length)) { + btree->u.external[n].length++; + mark_buffer_dirty(bh); + brelse(bh); + return se; + } + } else { + if (fsecno) { + hpfs_error(s, "empty file %08x, trying to add sector %08x", node, fsecno); + brelse(bh); + return -1; + } + se = !fnod ? node : (node + 16384) & ~16383; + } + if (!(se = hpfs_alloc_sector(s, se, 1, fsecno*ALLOC_M>ALLOC_FWD_MAX ? ALLOC_FWD_MAX : fsecno*ALLOC_M<ALLOC_FWD_MIN ? ALLOC_FWD_MIN : fsecno*ALLOC_M, 1))) { + brelse(bh); + return -1; + } + fs = n < 0 ? 0 : btree->u.external[n].file_secno + btree->u.external[n].length; + if (!btree->n_free_nodes) { + up = a != node ? anode->up : -1; + if (!(anode = hpfs_alloc_anode(s, a, &na, &bh1))) { + brelse(bh); + hpfs_free_sectors(s, se, 1); + return -1; + } + if (a == node && fnod) { + anode->up = node; + anode->btree.fnode_parent = 1; + anode->btree.n_used_nodes = btree->n_used_nodes; + anode->btree.first_free = btree->first_free; + anode->btree.n_free_nodes = 40 - anode->btree.n_used_nodes; + memcpy(&anode->u, &btree->u, btree->n_used_nodes * 12); + btree->internal = 1; + btree->n_free_nodes = 11; + btree->n_used_nodes = 1; + btree->first_free = (char *)&(btree->u.internal[1]) - (char *)btree; + btree->u.internal[0].file_secno = -1; + btree->u.internal[0].down = na; + mark_buffer_dirty(bh); + } else if (!(ranode = hpfs_alloc_anode(s, /*a*/0, &ra, &bh2))) { + brelse(bh); + brelse(bh1); + hpfs_free_sectors(s, se, 1); + hpfs_free_sectors(s, na, 1); + return -1; + } + brelse(bh); + bh = bh1; + btree = &anode->btree; + } + btree->n_free_nodes--; n = btree->n_used_nodes++; + btree->first_free += 12; + btree->u.external[n].disk_secno = se; + btree->u.external[n].file_secno = fs; + btree->u.external[n].length = 1; + mark_buffer_dirty(bh); + brelse(bh); + if ((a == node && fnod) || na == -1) return se; + c2 = 0; + while (up != -1) { + struct anode *new_anode; + if (hpfs_sb(s)->sb_chk) + if (hpfs_stop_cycles(s, up, &c1, &c2, "hpfs_add_sector_to_btree #2")) return -1; + if (up != node || !fnod) { + if (!(anode = hpfs_map_anode(s, up, &bh))) return -1; + btree = &anode->btree; + } else { + if (!(fnode = hpfs_map_fnode(s, up, &bh))) return -1; + btree = &fnode->btree; + } + if (btree->n_free_nodes) { + btree->n_free_nodes--; n = btree->n_used_nodes++; + btree->first_free += 8; + btree->u.internal[n].file_secno = -1; + btree->u.internal[n].down = na; + btree->u.internal[n-1].file_secno = fs; + mark_buffer_dirty(bh); + brelse(bh); + brelse(bh2); + hpfs_free_sectors(s, ra, 1); + if ((anode = hpfs_map_anode(s, na, &bh))) { + anode->up = up; + anode->btree.fnode_parent = up == node && fnod; + mark_buffer_dirty(bh); + brelse(bh); + } + return se; + } + up = up != node ? anode->up : -1; + btree->u.internal[btree->n_used_nodes - 1].file_secno = /*fs*/-1; + mark_buffer_dirty(bh); + brelse(bh); + a = na; + if ((new_anode = hpfs_alloc_anode(s, a, &na, &bh))) { + anode = new_anode; + /*anode->up = up != -1 ? up : ra;*/ + anode->btree.internal = 1; + anode->btree.n_used_nodes = 1; + anode->btree.n_free_nodes = 59; + anode->btree.first_free = 16; + anode->btree.u.internal[0].down = a; + anode->btree.u.internal[0].file_secno = -1; + mark_buffer_dirty(bh); + brelse(bh); + if ((anode = hpfs_map_anode(s, a, &bh))) { + anode->up = na; + mark_buffer_dirty(bh); + brelse(bh); + } + } else na = a; + } + if ((anode = hpfs_map_anode(s, na, &bh))) { + anode->up = node; + if (fnod) anode->btree.fnode_parent = 1; + mark_buffer_dirty(bh); + brelse(bh); + } + if (!fnod) { + if (!(anode = hpfs_map_anode(s, node, &bh))) { + brelse(bh2); + return -1; + } + btree = &anode->btree; + } else { + if (!(fnode = hpfs_map_fnode(s, node, &bh))) { + brelse(bh2); + return -1; + } + btree = &fnode->btree; + } + ranode->up = node; + memcpy(&ranode->btree, btree, btree->first_free); + if (fnod) ranode->btree.fnode_parent = 1; + ranode->btree.n_free_nodes = (ranode->btree.internal ? 60 : 40) - ranode->btree.n_used_nodes; + if (ranode->btree.internal) for (n = 0; n < ranode->btree.n_used_nodes; n++) { + struct anode *unode; + if ((unode = hpfs_map_anode(s, ranode->u.internal[n].down, &bh1))) { + unode->up = ra; + unode->btree.fnode_parent = 0; + mark_buffer_dirty(bh1); + brelse(bh1); + } + } + btree->internal = 1; + btree->n_free_nodes = fnod ? 10 : 58; + btree->n_used_nodes = 2; + btree->first_free = (char *)&btree->u.internal[2] - (char *)btree; + btree->u.internal[0].file_secno = fs; + btree->u.internal[0].down = ra; + btree->u.internal[1].file_secno = -1; + btree->u.internal[1].down = na; + mark_buffer_dirty(bh); + brelse(bh); + mark_buffer_dirty(bh2); + brelse(bh2); + return se; +} + +/* + * Remove allocation tree. Recursion would look much nicer but + * I want to avoid it because it can cause stack overflow. + */ + +void hpfs_remove_btree(struct super_block *s, struct bplus_header *btree) +{ + struct bplus_header *btree1 = btree; + struct anode *anode = NULL; + anode_secno ano = 0, oano; + struct buffer_head *bh; + int level = 0; + int pos = 0; + int i; + int c1, c2 = 0; + int d1, d2; + go_down: + d2 = 0; + while (btree1->internal) { + ano = btree1->u.internal[pos].down; + if (level) brelse(bh); + if (hpfs_sb(s)->sb_chk) + if (hpfs_stop_cycles(s, ano, &d1, &d2, "hpfs_remove_btree #1")) + return; + if (!(anode = hpfs_map_anode(s, ano, &bh))) return; + btree1 = &anode->btree; + level++; + pos = 0; + } + for (i = 0; i < btree1->n_used_nodes; i++) + hpfs_free_sectors(s, btree1->u.external[i].disk_secno, btree1->u.external[i].length); + go_up: + if (!level) return; + brelse(bh); + if (hpfs_sb(s)->sb_chk) + if (hpfs_stop_cycles(s, ano, &c1, &c2, "hpfs_remove_btree #2")) return; + hpfs_free_sectors(s, ano, 1); + oano = ano; + ano = anode->up; + if (--level) { + if (!(anode = hpfs_map_anode(s, ano, &bh))) return; + btree1 = &anode->btree; + } else btree1 = btree; + for (i = 0; i < btree1->n_used_nodes; i++) { + if (btree1->u.internal[i].down == oano) { + if ((pos = i + 1) < btree1->n_used_nodes) + goto go_down; + else + goto go_up; + } + } + hpfs_error(s, + "reference to anode %08x not found in anode %08x " + "(probably bad up pointer)", + oano, level ? ano : -1); + if (level) + brelse(bh); +} + +/* Just a wrapper around hpfs_bplus_lookup .. used for reading eas */ + +static secno anode_lookup(struct super_block *s, anode_secno a, unsigned sec) +{ + struct anode *anode; + struct buffer_head *bh; + if (!(anode = hpfs_map_anode(s, a, &bh))) return -1; + return hpfs_bplus_lookup(s, NULL, &anode->btree, sec, bh); +} + +int hpfs_ea_read(struct super_block *s, secno a, int ano, unsigned pos, + unsigned len, char *buf) +{ + struct buffer_head *bh; + char *data; + secno sec; + unsigned l; + while (len) { + if (ano) { + if ((sec = anode_lookup(s, a, pos >> 9)) == -1) + return -1; + } else sec = a + (pos >> 9); + if (hpfs_sb(s)->sb_chk) if (hpfs_chk_sectors(s, sec, 1, "ea #1")) return -1; + if (!(data = hpfs_map_sector(s, sec, &bh, (len - 1) >> 9))) + return -1; + l = 0x200 - (pos & 0x1ff); if (l > len) l = len; + memcpy(buf, data + (pos & 0x1ff), l); + brelse(bh); + buf += l; pos += l; len -= l; + } + return 0; +} + +int hpfs_ea_write(struct super_block *s, secno a, int ano, unsigned pos, + unsigned len, char *buf) +{ + struct buffer_head *bh; + char *data; + secno sec; + unsigned l; + while (len) { + if (ano) { + if ((sec = anode_lookup(s, a, pos >> 9)) == -1) + return -1; + } else sec = a + (pos >> 9); + if (hpfs_sb(s)->sb_chk) if (hpfs_chk_sectors(s, sec, 1, "ea #2")) return -1; + if (!(data = hpfs_map_sector(s, sec, &bh, (len - 1) >> 9))) + return -1; + l = 0x200 - (pos & 0x1ff); if (l > len) l = len; + memcpy(data + (pos & 0x1ff), buf, l); + mark_buffer_dirty(bh); + brelse(bh); + buf += l; pos += l; len -= l; + } + return 0; +} + +void hpfs_ea_remove(struct super_block *s, secno a, int ano, unsigned len) +{ + struct anode *anode; + struct buffer_head *bh; + if (ano) { + if (!(anode = hpfs_map_anode(s, a, &bh))) return; + hpfs_remove_btree(s, &anode->btree); + brelse(bh); + hpfs_free_sectors(s, a, 1); + } else hpfs_free_sectors(s, a, (len + 511) >> 9); +} + +/* Truncate allocation tree. Doesn't join anodes - I hope it doesn't matter */ + +void hpfs_truncate_btree(struct super_block *s, secno f, int fno, unsigned secs) +{ + struct fnode *fnode; + struct anode *anode; + struct buffer_head *bh; + struct bplus_header *btree; + anode_secno node = f; + int i, j, nodes; + int c1, c2 = 0; + if (fno) { + if (!(fnode = hpfs_map_fnode(s, f, &bh))) return; + btree = &fnode->btree; + } else { + if (!(anode = hpfs_map_anode(s, f, &bh))) return; + btree = &anode->btree; + } + if (!secs) { + hpfs_remove_btree(s, btree); + if (fno) { + btree->n_free_nodes = 8; + btree->n_used_nodes = 0; + btree->first_free = 8; + btree->internal = 0; + mark_buffer_dirty(bh); + } else hpfs_free_sectors(s, f, 1); + brelse(bh); + return; + } + while (btree->internal) { + nodes = btree->n_used_nodes + btree->n_free_nodes; + for (i = 0; i < btree->n_used_nodes; i++) + if (btree->u.internal[i].file_secno >= secs) goto f; + brelse(bh); + hpfs_error(s, "internal btree %08x doesn't end with -1", node); + return; + f: + for (j = i + 1; j < btree->n_used_nodes; j++) + hpfs_ea_remove(s, btree->u.internal[j].down, 1, 0); + btree->n_used_nodes = i + 1; + btree->n_free_nodes = nodes - btree->n_used_nodes; + btree->first_free = 8 + 8 * btree->n_used_nodes; + mark_buffer_dirty(bh); + if (btree->u.internal[i].file_secno == secs) { + brelse(bh); + return; + } + node = btree->u.internal[i].down; + brelse(bh); + if (hpfs_sb(s)->sb_chk) + if (hpfs_stop_cycles(s, node, &c1, &c2, "hpfs_truncate_btree")) + return; + if (!(anode = hpfs_map_anode(s, node, &bh))) return; + btree = &anode->btree; + } + nodes = btree->n_used_nodes + btree->n_free_nodes; + for (i = 0; i < btree->n_used_nodes; i++) + if (btree->u.external[i].file_secno + btree->u.external[i].length >= secs) goto ff; + brelse(bh); + return; + ff: + if (secs <= btree->u.external[i].file_secno) { + hpfs_error(s, "there is an allocation error in file %08x, sector %08x", f, secs); + if (i) i--; + } + else if (btree->u.external[i].file_secno + btree->u.external[i].length > secs) { + hpfs_free_sectors(s, btree->u.external[i].disk_secno + secs - + btree->u.external[i].file_secno, btree->u.external[i].length + - secs + btree->u.external[i].file_secno); /* I hope gcc optimizes this :-) */ + btree->u.external[i].length = secs - btree->u.external[i].file_secno; + } + for (j = i + 1; j < btree->n_used_nodes; j++) + hpfs_free_sectors(s, btree->u.external[j].disk_secno, btree->u.external[j].length); + btree->n_used_nodes = i + 1; + btree->n_free_nodes = nodes - btree->n_used_nodes; + btree->first_free = 8 + 12 * btree->n_used_nodes; + mark_buffer_dirty(bh); + brelse(bh); +} + +/* Remove file or directory and it's eas - note that directory must + be empty when this is called. */ + +void hpfs_remove_fnode(struct super_block *s, fnode_secno fno) +{ + struct buffer_head *bh; + struct fnode *fnode; + struct extended_attribute *ea; + struct extended_attribute *ea_end; + if (!(fnode = hpfs_map_fnode(s, fno, &bh))) return; + if (!fnode->dirflag) hpfs_remove_btree(s, &fnode->btree); + else hpfs_remove_dtree(s, fnode->u.external[0].disk_secno); + ea_end = fnode_end_ea(fnode); + for (ea = fnode_ea(fnode); ea < ea_end; ea = next_ea(ea)) + if (ea->indirect) + hpfs_ea_remove(s, ea_sec(ea), ea->anode, ea_len(ea)); + hpfs_ea_ext_remove(s, fnode->ea_secno, fnode->ea_anode, fnode->ea_size_l); + brelse(bh); + hpfs_free_sectors(s, fno, 1); +} diff --git a/fs/hpfs/buffer.c b/fs/hpfs/buffer.c new file mode 100644 index 00000000000..2807aa833e6 --- /dev/null +++ b/fs/hpfs/buffer.c @@ -0,0 +1,175 @@ +/* + * linux/fs/hpfs/buffer.c + * + * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999 + * + * general buffer i/o + */ + +#include "hpfs_fn.h" + +void hpfs_lock_creation(struct super_block *s) +{ +#ifdef DEBUG_LOCKS + printk("lock creation\n"); +#endif + down(&hpfs_sb(s)->hpfs_creation_de); +} + +void hpfs_unlock_creation(struct super_block *s) +{ +#ifdef DEBUG_LOCKS + printk("unlock creation\n"); +#endif + up(&hpfs_sb(s)->hpfs_creation_de); +} + +/* Map a sector into a buffer and return pointers to it and to the buffer. */ + +void *hpfs_map_sector(struct super_block *s, unsigned secno, struct buffer_head **bhp, + int ahead) +{ + struct buffer_head *bh; + + cond_resched(); + + *bhp = bh = sb_bread(s, secno); + if (bh != NULL) + return bh->b_data; + else { + printk("HPFS: hpfs_map_sector: read error\n"); + return NULL; + } +} + +/* Like hpfs_map_sector but don't read anything */ + +void *hpfs_get_sector(struct super_block *s, unsigned secno, struct buffer_head **bhp) +{ + struct buffer_head *bh; + /*return hpfs_map_sector(s, secno, bhp, 0);*/ + + cond_resched(); + + if ((*bhp = bh = sb_getblk(s, secno)) != NULL) { + if (!buffer_uptodate(bh)) wait_on_buffer(bh); + set_buffer_uptodate(bh); + return bh->b_data; + } else { + printk("HPFS: hpfs_get_sector: getblk failed\n"); + return NULL; + } +} + +/* Map 4 sectors into a 4buffer and return pointers to it and to the buffer. */ + +void *hpfs_map_4sectors(struct super_block *s, unsigned secno, struct quad_buffer_head *qbh, + int ahead) +{ + struct buffer_head *bh; + char *data; + + cond_resched(); + + if (secno & 3) { + printk("HPFS: hpfs_map_4sectors: unaligned read\n"); + return NULL; + } + + qbh->data = data = (char *)kmalloc(2048, GFP_NOFS); + if (!data) { + printk("HPFS: hpfs_map_4sectors: out of memory\n"); + goto bail; + } + + qbh->bh[0] = bh = sb_bread(s, secno); + if (!bh) + goto bail0; + memcpy(data, bh->b_data, 512); + + qbh->bh[1] = bh = sb_bread(s, secno + 1); + if (!bh) + goto bail1; + memcpy(data + 512, bh->b_data, 512); + + qbh->bh[2] = bh = sb_bread(s, secno + 2); + if (!bh) + goto bail2; + memcpy(data + 2 * 512, bh->b_data, 512); + + qbh->bh[3] = bh = sb_bread(s, secno + 3); + if (!bh) + goto bail3; + memcpy(data + 3 * 512, bh->b_data, 512); + + return data; + + bail3: + brelse(qbh->bh[2]); + bail2: + brelse(qbh->bh[1]); + bail1: + brelse(qbh->bh[0]); + bail0: + kfree(data); + printk("HPFS: hpfs_map_4sectors: read error\n"); + bail: + return NULL; +} + +/* Don't read sectors */ + +void *hpfs_get_4sectors(struct super_block *s, unsigned secno, + struct quad_buffer_head *qbh) +{ + cond_resched(); + + if (secno & 3) { + printk("HPFS: hpfs_get_4sectors: unaligned read\n"); + return NULL; + } + + /*return hpfs_map_4sectors(s, secno, qbh, 0);*/ + if (!(qbh->data = kmalloc(2048, GFP_NOFS))) { + printk("HPFS: hpfs_get_4sectors: out of memory\n"); + return NULL; + } + if (!(hpfs_get_sector(s, secno, &qbh->bh[0]))) goto bail0; + if (!(hpfs_get_sector(s, secno + 1, &qbh->bh[1]))) goto bail1; + if (!(hpfs_get_sector(s, secno + 2, &qbh->bh[2]))) goto bail2; + if (!(hpfs_get_sector(s, secno + 3, &qbh->bh[3]))) goto bail3; + memcpy(qbh->data, qbh->bh[0]->b_data, 512); + memcpy(qbh->data + 512, qbh->bh[1]->b_data, 512); + memcpy(qbh->data + 2*512, qbh->bh[2]->b_data, 512); + memcpy(qbh->data + 3*512, qbh->bh[3]->b_data, 512); + return qbh->data; + + bail3: brelse(qbh->bh[2]); + bail2: brelse(qbh->bh[1]); + bail1: brelse(qbh->bh[0]); + bail0: + return NULL; +} + + +void hpfs_brelse4(struct quad_buffer_head *qbh) +{ + brelse(qbh->bh[3]); + brelse(qbh->bh[2]); + brelse(qbh->bh[1]); + brelse(qbh->bh[0]); + kfree(qbh->data); +} + +void hpfs_mark_4buffers_dirty(struct quad_buffer_head *qbh) +{ + PRINTK(("hpfs_mark_4buffers_dirty\n")); + memcpy(qbh->bh[0]->b_data, qbh->data, 512); + memcpy(qbh->bh[1]->b_data, qbh->data + 512, 512); + memcpy(qbh->bh[2]->b_data, qbh->data + 2 * 512, 512); + memcpy(qbh->bh[3]->b_data, qbh->data + 3 * 512, 512); + mark_buffer_dirty(qbh->bh[0]); + mark_buffer_dirty(qbh->bh[1]); + mark_buffer_dirty(qbh->bh[2]); + mark_buffer_dirty(qbh->bh[3]); +} diff --git a/fs/hpfs/dentry.c b/fs/hpfs/dentry.c new file mode 100644 index 00000000000..08319126b2a --- /dev/null +++ b/fs/hpfs/dentry.c @@ -0,0 +1,60 @@ +/* + * linux/fs/hpfs/dentry.c + * + * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999 + * + * dcache operations + */ + +#include "hpfs_fn.h" + +/* + * Note: the dentry argument is the parent dentry. + */ + +static int hpfs_hash_dentry(struct dentry *dentry, struct qstr *qstr) +{ + unsigned long hash; + int i; + unsigned l = qstr->len; + + if (l == 1) if (qstr->name[0]=='.') goto x; + if (l == 2) if (qstr->name[0]=='.' || qstr->name[1]=='.') goto x; + hpfs_adjust_length((char *)qstr->name, &l); + /*if (hpfs_chk_name((char *)qstr->name,&l))*/ + /*return -ENAMETOOLONG;*/ + /*return -ENOENT;*/ + x: + + hash = init_name_hash(); + for (i = 0; i < l; i++) + hash = partial_name_hash(hpfs_upcase(hpfs_sb(dentry->d_sb)->sb_cp_table,qstr->name[i]), hash); + qstr->hash = end_name_hash(hash); + + return 0; +} + +static int hpfs_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b) +{ + unsigned al=a->len; + unsigned bl=b->len; + hpfs_adjust_length((char *)a->name, &al); + /*hpfs_adjust_length((char *)b->name, &bl);*/ + /* 'a' is the qstr of an already existing dentry, so the name + * must be valid. 'b' must be validated first. + */ + + if (hpfs_chk_name((char *)b->name, &bl)) return 1; + if (hpfs_compare_names(dentry->d_sb, (char *)a->name, al, (char *)b->name, bl, 0)) return 1; + return 0; +} + +static struct dentry_operations hpfs_dentry_operations = { + .d_hash = hpfs_hash_dentry, + .d_compare = hpfs_compare_dentry, +}; + +void hpfs_set_dentry_operations(struct dentry *dentry) +{ + dentry->d_op = &hpfs_dentry_operations; +} diff --git a/fs/hpfs/dir.c b/fs/hpfs/dir.c new file mode 100644 index 00000000000..0217c3a0444 --- /dev/null +++ b/fs/hpfs/dir.c @@ -0,0 +1,320 @@ +/* + * linux/fs/hpfs/dir.c + * + * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999 + * + * directory VFS functions + */ + +#include "hpfs_fn.h" + +static int hpfs_dir_release(struct inode *inode, struct file *filp) +{ + lock_kernel(); + hpfs_del_pos(inode, &filp->f_pos); + /*hpfs_write_if_changed(inode);*/ + unlock_kernel(); + return 0; +} + +/* This is slow, but it's not used often */ + +static loff_t hpfs_dir_lseek(struct file *filp, loff_t off, int whence) +{ + loff_t new_off = off + (whence == 1 ? filp->f_pos : 0); + loff_t pos; + struct quad_buffer_head qbh; + struct inode *i = filp->f_dentry->d_inode; + struct hpfs_inode_info *hpfs_inode = hpfs_i(i); + struct super_block *s = i->i_sb; + + lock_kernel(); + + /*printk("dir lseek\n");*/ + if (new_off == 0 || new_off == 1 || new_off == 11 || new_off == 12 || new_off == 13) goto ok; + down(&i->i_sem); + pos = ((loff_t) hpfs_de_as_down_as_possible(s, hpfs_inode->i_dno) << 4) + 1; + while (pos != new_off) { + if (map_pos_dirent(i, &pos, &qbh)) hpfs_brelse4(&qbh); + else goto fail; + if (pos == 12) goto fail; + } + up(&i->i_sem); +ok: + unlock_kernel(); + return filp->f_pos = new_off; +fail: + up(&i->i_sem); + /*printk("illegal lseek: %016llx\n", new_off);*/ + unlock_kernel(); + return -ESPIPE; +} + +static int hpfs_readdir(struct file *filp, void *dirent, filldir_t filldir) +{ + struct inode *inode = filp->f_dentry->d_inode; + struct hpfs_inode_info *hpfs_inode = hpfs_i(inode); + struct quad_buffer_head qbh; + struct hpfs_dirent *de; + int lc; + long old_pos; + char *tempname; + int c1, c2 = 0; + int ret = 0; + + lock_kernel(); + + if (hpfs_sb(inode->i_sb)->sb_chk) { + if (hpfs_chk_sectors(inode->i_sb, inode->i_ino, 1, "dir_fnode")) { + ret = -EFSERROR; + goto out; + } + if (hpfs_chk_sectors(inode->i_sb, hpfs_inode->i_dno, 4, "dir_dnode")) { + ret = -EFSERROR; + goto out; + } + } + if (hpfs_sb(inode->i_sb)->sb_chk >= 2) { + struct buffer_head *bh; + struct fnode *fno; + int e = 0; + if (!(fno = hpfs_map_fnode(inode->i_sb, inode->i_ino, &bh))) { + ret = -EIOERROR; + goto out; + } + if (!fno->dirflag) { + e = 1; + hpfs_error(inode->i_sb, "not a directory, fnode %08x",inode->i_ino); + } + if (hpfs_inode->i_dno != fno->u.external[0].disk_secno) { + e = 1; + hpfs_error(inode->i_sb, "corrupted inode: i_dno == %08x, fnode -> dnode == %08x", hpfs_inode->i_dno, fno->u.external[0].disk_secno); + } + brelse(bh); + if (e) { + ret = -EFSERROR; + goto out; + } + } + lc = hpfs_sb(inode->i_sb)->sb_lowercase; + if (filp->f_pos == 12) { /* diff -r requires this (note, that diff -r */ + filp->f_pos = 13; /* also fails on msdos filesystem in 2.0) */ + goto out; + } + if (filp->f_pos == 13) { + ret = -ENOENT; + goto out; + } + + while (1) { + again: + /* This won't work when cycle is longer than number of dirents + accepted by filldir, but what can I do? + maybe killall -9 ls helps */ + if (hpfs_sb(inode->i_sb)->sb_chk) + if (hpfs_stop_cycles(inode->i_sb, filp->f_pos, &c1, &c2, "hpfs_readdir")) { + ret = -EFSERROR; + goto out; + } + if (filp->f_pos == 12) + goto out; + if (filp->f_pos == 3 || filp->f_pos == 4 || filp->f_pos == 5) { + printk("HPFS: warning: pos==%d\n",(int)filp->f_pos); + goto out; + } + if (filp->f_pos == 0) { + if (filldir(dirent, ".", 1, filp->f_pos, inode->i_ino, DT_DIR) < 0) + goto out; + filp->f_pos = 11; + } + if (filp->f_pos == 11) { + if (filldir(dirent, "..", 2, filp->f_pos, hpfs_inode->i_parent_dir, DT_DIR) < 0) + goto out; + filp->f_pos = 1; + } + if (filp->f_pos == 1) { + filp->f_pos = ((loff_t) hpfs_de_as_down_as_possible(inode->i_sb, hpfs_inode->i_dno) << 4) + 1; + hpfs_add_pos(inode, &filp->f_pos); + filp->f_version = inode->i_version; + } + old_pos = filp->f_pos; + if (!(de = map_pos_dirent(inode, &filp->f_pos, &qbh))) { + ret = -EIOERROR; + goto out; + } + if (de->first || de->last) { + if (hpfs_sb(inode->i_sb)->sb_chk) { + if (de->first && !de->last && (de->namelen != 2 || de ->name[0] != 1 || de->name[1] != 1)) hpfs_error(inode->i_sb, "hpfs_readdir: bad ^A^A entry; pos = %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, DT_UNKNOWN) < 0) { + filp->f_pos = old_pos; + if (tempname != (char *)de->name) kfree(tempname); + hpfs_brelse4(&qbh); + goto out; + } + if (tempname != (char *)de->name) kfree(tempname); + hpfs_brelse4(&qbh); + } +out: + unlock_kernel(); + return ret; +} + +/* + * lookup. Search the specified directory for the specified name, set + * *result to the corresponding inode. + * + * lookup uses the inode number to tell read_inode whether it is reading + * the inode of a directory or a file -- file ino's are odd, directory + * ino's are even. read_inode avoids i/o for file inodes; everything + * needed is up here in the directory. (And file fnodes are out in + * the boondocks.) + * + * - M.P.: this is over, sometimes we've got to read file's fnode for eas + * inode numbers are just fnode sector numbers; iget lock is used + * to tell read_inode to read fnode or not. + */ + +struct dentry *hpfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) +{ + const char *name = dentry->d_name.name; + unsigned len = dentry->d_name.len; + struct quad_buffer_head qbh; + struct hpfs_dirent *de; + ino_t ino; + int err; + struct inode *result = NULL; + struct hpfs_inode_info *hpfs_result; + + lock_kernel(); + if ((err = hpfs_chk_name((char *)name, &len))) { + if (err == -ENAMETOOLONG) { + unlock_kernel(); + return ERR_PTR(-ENAMETOOLONG); + } + goto end_add; + } + + /* + * '.' and '..' will never be passed here. + */ + + de = map_dirent(dir, hpfs_i(dir)->i_dno, (char *) name, len, NULL, &qbh); + + /* + * This is not really a bailout, just means file not found. + */ + + if (!de) goto end; + + /* + * Get inode number, what we're after. + */ + + ino = de->fnode; + + /* + * Go find or make an inode. + */ + + result = iget_locked(dir->i_sb, ino); + if (!result) { + hpfs_error(dir->i_sb, "hpfs_lookup: can't get inode"); + goto bail1; + } + if (result->i_state & I_NEW) { + hpfs_init_inode(result); + if (de->directory) + hpfs_read_inode(result); + else if (de->ea_size && hpfs_sb(dir->i_sb)->sb_eas) + hpfs_read_inode(result); + else { + result->i_mode |= S_IFREG; + result->i_mode &= ~0111; + result->i_op = &hpfs_file_iops; + result->i_fop = &hpfs_file_ops; + result->i_nlink = 1; + } + unlock_new_inode(result); + } + hpfs_result = hpfs_i(result); + if (!de->directory) hpfs_result->i_parent_dir = dir->i_ino; + + hpfs_decide_conv(result, (char *)name, len); + + if (de->has_acl || de->has_xtd_perm) if (!(dir->i_sb->s_flags & MS_RDONLY)) { + hpfs_error(result->i_sb, "ACLs or XPERM found. This is probably HPFS386. This driver doesn't support it now. Send me some info on these structures"); + goto bail1; + } + + /* + * Fill in the info from the directory if this is a newly created + * inode. + */ + + if (!result->i_ctime.tv_sec) { + if (!(result->i_ctime.tv_sec = local_to_gmt(dir->i_sb, de->creation_date))) + result->i_ctime.tv_sec = 1; + result->i_ctime.tv_nsec = 0; + result->i_mtime.tv_sec = local_to_gmt(dir->i_sb, de->write_date); + result->i_mtime.tv_nsec = 0; + result->i_atime.tv_sec = local_to_gmt(dir->i_sb, de->read_date); + result->i_atime.tv_nsec = 0; + hpfs_result->i_ea_size = de->ea_size; + if (!hpfs_result->i_ea_mode && de->read_only) + result->i_mode &= ~0222; + if (!de->directory) { + if (result->i_size == -1) { + result->i_size = de->file_size; + result->i_data.a_ops = &hpfs_aops; + hpfs_i(result)->mmu_private = result->i_size; + /* + * i_blocks should count the fnode and any anodes. + * We count 1 for the fnode and don't bother about + * anodes -- the disk heads are on the directory band + * and we want them to stay there. + */ + result->i_blocks = 1 + ((result->i_size + 511) >> 9); + } + } + } + + hpfs_brelse4(&qbh); + + /* + * Made it. + */ + + end: + end_add: + hpfs_set_dentry_operations(dentry); + unlock_kernel(); + d_add(dentry, result); + return NULL; + + /* + * Didn't. + */ + bail1: + + hpfs_brelse4(&qbh); + + /*bail:*/ + + unlock_kernel(); + return ERR_PTR(-ENOENT); +} + +struct file_operations hpfs_dir_ops = +{ + .llseek = hpfs_dir_lseek, + .read = generic_read_dir, + .readdir = hpfs_readdir, + .release = hpfs_dir_release, + .fsync = hpfs_file_fsync, +}; diff --git a/fs/hpfs/dnode.c b/fs/hpfs/dnode.c new file mode 100644 index 00000000000..1d21307730a --- /dev/null +++ b/fs/hpfs/dnode.c @@ -0,0 +1,1080 @@ +/* + * 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) +{ + struct hpfs_inode_info *hpfs_inode = hpfs_i(inode); + int i = 0; + loff_t **ppos; + + if (hpfs_inode->i_rddir_off) + for (; hpfs_inode->i_rddir_off[i]; i++) + if (hpfs_inode->i_rddir_off[i] == pos) return; + if (!(i&0x0f)) { + if (!(ppos = kmalloc((i+0x11) * sizeof(loff_t*), GFP_NOFS))) { + printk("HPFS: out of memory for position list\n"); + return; + } + if (hpfs_inode->i_rddir_off) { + memcpy(ppos, hpfs_inode->i_rddir_off, i * sizeof(loff_t)); + kfree(hpfs_inode->i_rddir_off); + } + hpfs_inode->i_rddir_off = ppos; + } + hpfs_inode->i_rddir_off[i] = pos; + hpfs_inode->i_rddir_off[i + 1] = NULL; +} + +void hpfs_del_pos(struct inode *inode, loff_t *pos) +{ + struct hpfs_inode_info *hpfs_inode = hpfs_i(inode); + loff_t **i, **j; + + if (!hpfs_inode->i_rddir_off) goto not_f; + for (i = hpfs_inode->i_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 == hpfs_inode->i_rddir_off) { + kfree(hpfs_inode->i_rddir_off); + hpfs_inode->i_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) +{ + struct hpfs_inode_info *hpfs_inode = hpfs_i(inode); + loff_t **i; + + if (!hpfs_inode->i_rddir_off) return; + for (i = hpfs_inode->i_rddir_off; *i; i++) (*f)(*i, p1, p2); + return; +} + +static 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); +}*/ + +static 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; + } +} + +static 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 (hpfs_sb(s)->sb_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 its subtree */ + +static 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 */ + +static 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_NOFS))) { + 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 (hpfs_sb(i->i_sb)->sb_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_NOFS))) { + /* 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); + brelse(bh); + d->up = ad->up = hpfs_i(i)->i_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 hpfs_inode_info *hpfs_inode = hpfs_i(i); + struct dnode *d; + struct hpfs_dirent *de, *de_end; + struct quad_buffer_head qbh; + dnode_secno dno; + int c; + int c1, c2 = 0; + dno = hpfs_inode->i_dno; + down: + if (hpfs_sb(i->i_sb)->sb_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++; + 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 (hpfs_sb(i->i_sb)->sb_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 (hpfs_sb(i->i_sb)->sb_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_NOFS))) { + 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 hpfs_inode_info *hpfs_inode = hpfs_i(i); + 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 (hpfs_sb(i->i_sb)->sb_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 (hpfs_sb(i->i_sb)->sb_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); + brelse(bh); + } + hpfs_inode->i_dno = down; + for_all_poss(i, hpfs_pos_subst, ((loff_t)dno << 4) | 1, (loff_t) 12); + 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_NOFS))) { + 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 (hpfs_sb(i->i_sb)->sb_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 (hpfs_sb(i->i_sb)->sb_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_NOFS))) { + 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++; + 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 (hpfs_sb(s)->sb_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 (hpfs_sb(s)->sb_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 (hpfs_sb(s)->sb_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 (hpfs_sb(s)->sb_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 (hpfs_sb(s)->sb_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 = 12; + 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 (hpfs_sb(inode->i_sb)->sb_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_NOFS))) { + 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 (hpfs_sb(s)->sb_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 (hpfs_sb(s)->sb_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 00000000000..66339dc030e --- /dev/null +++ b/fs/hpfs/ea.c @@ -0,0 +1,363 @@ +/* + * linux/fs/hpfs/ea.c + * + * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999 + * + * handling extended attributes + */ + +#include "hpfs_fn.h" + +/* Remove external extended attributes. ano specifies whether a is a + direct sector where eas starts or an anode */ + +void hpfs_ea_ext_remove(struct super_block *s, secno a, int ano, unsigned len) +{ + unsigned pos = 0; + while (pos < len) { + char ex[4 + 255 + 1 + 8]; + struct extended_attribute *ea = (struct extended_attribute *)ex; + if (pos + 4 > len) { + hpfs_error(s, "EAs don't end correctly, %s %08x, len %08x", + ano ? "anode" : "sectors", a, len); + return; + } + if (hpfs_ea_read(s, a, ano, pos, 4, ex)) return; + if (ea->indirect) { + if (ea->valuelen != 8) { + hpfs_error(s, "ea->indirect set while ea->valuelen!=8, %s %08x, pos %08x", + ano ? "anode" : "sectors", a, pos); + return; + } + if (hpfs_ea_read(s, a, ano, pos + 4, ea->namelen + 9, ex+4)) + return; + hpfs_ea_remove(s, ea_sec(ea), ea->anode, ea_len(ea)); + } + pos += ea->namelen + ea->valuelen + 5; + } + if (!ano) hpfs_free_sectors(s, a, (len+511) >> 9); + else { + struct buffer_head *bh; + struct anode *anode; + if ((anode = hpfs_map_anode(s, a, &bh))) { + hpfs_remove_btree(s, &anode->btree); + brelse(bh); + hpfs_free_sectors(s, a, 1); + } + } +} + +static char *get_indirect_ea(struct super_block *s, int ano, secno a, int size) +{ + char *ret; + if (!(ret = kmalloc(size + 1, GFP_NOFS))) { + printk("HPFS: out of memory for EA\n"); + return NULL; + } + if (hpfs_ea_read(s, a, ano, 0, size, ret)) { + kfree(ret); + return NULL; + } + ret[size] = 0; + return ret; +} + +static void set_indirect_ea(struct super_block *s, int ano, secno a, char *data, + int size) +{ + hpfs_ea_write(s, a, ano, 0, size, data); +} + +/* Read an extended attribute named 'key' into the provided buffer */ + +int hpfs_read_ea(struct super_block *s, struct fnode *fnode, char *key, + char *buf, int size) +{ + unsigned pos; + int ano, len; + secno a; + struct extended_attribute *ea; + struct extended_attribute *ea_end = fnode_end_ea(fnode); + for (ea = fnode_ea(fnode); ea < ea_end; ea = next_ea(ea)) + if (!strcmp(ea->name, key)) { + if (ea->indirect) + goto indirect; + if (ea->valuelen >= size) + return -EINVAL; + memcpy(buf, ea_data(ea), ea->valuelen); + buf[ea->valuelen] = 0; + return 0; + } + a = fnode->ea_secno; + len = fnode->ea_size_l; + ano = fnode->ea_anode; + pos = 0; + while (pos < len) { + char ex[4 + 255 + 1 + 8]; + ea = (struct extended_attribute *)ex; + if (pos + 4 > len) { + hpfs_error(s, "EAs don't end correctly, %s %08x, len %08x", + ano ? "anode" : "sectors", a, len); + return -EIO; + } + if (hpfs_ea_read(s, a, ano, pos, 4, ex)) return -EIO; + if (hpfs_ea_read(s, a, ano, pos + 4, ea->namelen + 1 + (ea->indirect ? 8 : 0), ex + 4)) + return -EIO; + if (!strcmp(ea->name, key)) { + if (ea->indirect) + goto indirect; + if (ea->valuelen >= size) + return -EINVAL; + if (hpfs_ea_read(s, a, ano, pos + 4 + ea->namelen + 1, ea->valuelen, buf)) + return -EIO; + buf[ea->valuelen] = 0; + return 0; + } + pos += ea->namelen + ea->valuelen + 5; + } + return -ENOENT; +indirect: + if (ea_len(ea) >= size) + return -EINVAL; + if (hpfs_ea_read(s, ea_sec(ea), ea->anode, 0, ea_len(ea), buf)) + return -EIO; + buf[ea_len(ea)] = 0; + return 0; +} + +/* Read an extended attribute named 'key' */ +char *hpfs_get_ea(struct super_block *s, struct fnode *fnode, char *key, int *size) +{ + char *ret; + unsigned pos; + int ano, len; + secno a; + struct extended_attribute *ea; + struct extended_attribute *ea_end = fnode_end_ea(fnode); + for (ea = fnode_ea(fnode); ea < ea_end; ea = next_ea(ea)) + if (!strcmp(ea->name, key)) { + if (ea->indirect) + return get_indirect_ea(s, ea->anode, ea_sec(ea), *size = ea_len(ea)); + if (!(ret = kmalloc((*size = ea->valuelen) + 1, GFP_NOFS))) { + printk("HPFS: out of memory for EA\n"); + return NULL; + } + memcpy(ret, ea_data(ea), ea->valuelen); + ret[ea->valuelen] = 0; + return ret; + } + a = fnode->ea_secno; + len = fnode->ea_size_l; + ano = fnode->ea_anode; + pos = 0; + while (pos < len) { + char ex[4 + 255 + 1 + 8]; + ea = (struct extended_attribute *)ex; + if (pos + 4 > len) { + hpfs_error(s, "EAs don't end correctly, %s %08x, len %08x", + ano ? "anode" : "sectors", a, len); + return NULL; + } + if (hpfs_ea_read(s, a, ano, pos, 4, ex)) return NULL; + if (hpfs_ea_read(s, a, ano, pos + 4, ea->namelen + 1 + (ea->indirect ? 8 : 0), ex + 4)) + return NULL; + if (!strcmp(ea->name, key)) { + if (ea->indirect) + return get_indirect_ea(s, ea->anode, ea_sec(ea), *size = ea_len(ea)); + if (!(ret = kmalloc((*size = ea->valuelen) + 1, GFP_NOFS))) { + printk("HPFS: out of memory for EA\n"); + return NULL; + } + if (hpfs_ea_read(s, a, ano, pos + 4 + ea->namelen + 1, ea->valuelen, ret)) { + kfree(ret); + return NULL; + } + ret[ea->valuelen] = 0; + return ret; + } + pos += ea->namelen + ea->valuelen + 5; + } + return NULL; +} + +/* + * Update or create extended attribute 'key' with value 'data'. Note that + * when this ea exists, it MUST have the same size as size of data. + * This driver can't change sizes of eas ('cause I just don't need it). + */ + +void hpfs_set_ea(struct inode *inode, struct fnode *fnode, char *key, char *data, int size) +{ + fnode_secno fno = inode->i_ino; + struct super_block *s = inode->i_sb; + unsigned pos; + int ano, len; + secno a; + unsigned char h[4]; + struct extended_attribute *ea; + struct extended_attribute *ea_end = fnode_end_ea(fnode); + for (ea = fnode_ea(fnode); ea < ea_end; ea = next_ea(ea)) + if (!strcmp(ea->name, key)) { + if (ea->indirect) { + if (ea_len(ea) == size) + set_indirect_ea(s, ea->anode, ea_sec(ea), data, size); + } else if (ea->valuelen == size) { + memcpy(ea_data(ea), data, size); + } + return; + } + a = fnode->ea_secno; + len = fnode->ea_size_l; + ano = fnode->ea_anode; + pos = 0; + while (pos < len) { + char ex[4 + 255 + 1 + 8]; + ea = (struct extended_attribute *)ex; + if (pos + 4 > len) { + hpfs_error(s, "EAs don't end correctly, %s %08x, len %08x", + ano ? "anode" : "sectors", a, len); + return; + } + if (hpfs_ea_read(s, a, ano, pos, 4, ex)) return; + if (hpfs_ea_read(s, a, ano, pos + 4, ea->namelen + 1 + (ea->indirect ? 8 : 0), ex + 4)) + return; + if (!strcmp(ea->name, key)) { + if (ea->indirect) { + if (ea_len(ea) == size) + set_indirect_ea(s, ea->anode, ea_sec(ea), data, size); + } + else { + if (ea->valuelen == size) + hpfs_ea_write(s, a, ano, pos + 4 + ea->namelen + 1, size, data); + } + return; + } + pos += ea->namelen + ea->valuelen + 5; + } + if (!fnode->ea_offs) { + /*if (fnode->ea_size_s) { + hpfs_error(s, "fnode %08x: ea_size_s == %03x, ea_offs == 0", + inode->i_ino, fnode->ea_size_s); + return; + }*/ + fnode->ea_offs = 0xc4; + } + if (fnode->ea_offs < 0xc4 || fnode->ea_offs + fnode->acl_size_s + fnode->ea_size_s > 0x200) { + hpfs_error(s, "fnode %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->acl_size_s + fnode->ea_size_s + strlen(key) + size + 5 <= 0x200) { + ea = fnode_end_ea(fnode); + *(char *)ea = 0; + ea->namelen = strlen(key); + ea->valuelen = size; + strcpy(ea->name, key); + memcpy(ea_data(ea), data, size); + fnode->ea_size_s += strlen(key) + size + 5; + goto ret; + } + /* Most the code here is 99.9993422% unused. I hope there are no bugs. + But what .. HPFS.IFS has also bugs in ea management. */ + if (fnode->ea_size_s && !fnode->ea_size_l) { + secno n; + struct buffer_head *bh; + char *data; + if (!(n = hpfs_alloc_sector(s, fno, 1, 0, 1))) return; + if (!(data = hpfs_get_sector(s, n, &bh))) { + hpfs_free_sectors(s, n, 1); + return; + } + memcpy(data, fnode_ea(fnode), fnode->ea_size_s); + fnode->ea_size_l = fnode->ea_size_s; + fnode->ea_size_s = 0; + fnode->ea_secno = n; + fnode->ea_anode = 0; + mark_buffer_dirty(bh); + brelse(bh); + } + pos = fnode->ea_size_l + 5 + strlen(key) + size; + len = (fnode->ea_size_l + 511) >> 9; + if (pos >= 30000) goto bail; + while (((pos + 511) >> 9) > len) { + if (!len) { + if (!(fnode->ea_secno = hpfs_alloc_sector(s, fno, 1, 0, 1))) + goto bail; + fnode->ea_anode = 0; + len++; + } else if (!fnode->ea_anode) { + if (hpfs_alloc_if_possible(s, fnode->ea_secno + len)) { + len++; + } else { + /* Aargh... don't know how to create ea anodes :-( */ + /*struct buffer_head *bh; + struct anode *anode; + anode_secno a_s; + if (!(anode = hpfs_alloc_anode(s, fno, &a_s, &bh))) + goto bail; + anode->up = fno; + anode->btree.fnode_parent = 1; + anode->btree.n_free_nodes--; + anode->btree.n_used_nodes++; + anode->btree.first_free += 12; + anode->u.external[0].disk_secno = fnode->ea_secno; + anode->u.external[0].file_secno = 0; + anode->u.external[0].length = len; + mark_buffer_dirty(bh); + brelse(bh); + fnode->ea_anode = 1; + fnode->ea_secno = a_s;*/ + secno new_sec; + int i; + if (!(new_sec = hpfs_alloc_sector(s, fno, 1, 1 - ((pos + 511) >> 9), 1))) + goto bail; + for (i = 0; i < len; i++) { + struct buffer_head *bh1, *bh2; + void *b1, *b2; + if (!(b1 = hpfs_map_sector(s, fnode->ea_secno + i, &bh1, len - i - 1))) { + hpfs_free_sectors(s, new_sec, (pos + 511) >> 9); + goto bail; + } + if (!(b2 = hpfs_get_sector(s, new_sec + i, &bh2))) { + brelse(bh1); + hpfs_free_sectors(s, new_sec, (pos + 511) >> 9); + goto bail; + } + memcpy(b2, b1, 512); + brelse(bh1); + mark_buffer_dirty(bh2); + brelse(bh2); + } + hpfs_free_sectors(s, fnode->ea_secno, len); + fnode->ea_secno = new_sec; + len = (pos + 511) >> 9; + } + } + if (fnode->ea_anode) { + if (hpfs_add_sector_to_btree(s, fnode->ea_secno, + 0, len) != -1) { + len++; + } else { + goto bail; + } + } + } + h[0] = 0; + h[1] = strlen(key); + h[2] = size & 0xff; + h[3] = size >> 8; + if (hpfs_ea_write(s, fnode->ea_secno, fnode->ea_anode, fnode->ea_size_l, 4, h)) goto bail; + if (hpfs_ea_write(s, fnode->ea_secno, fnode->ea_anode, fnode->ea_size_l + 4, h[1] + 1, key)) goto bail; + if (hpfs_ea_write(s, fnode->ea_secno, fnode->ea_anode, fnode->ea_size_l + 5 + h[1], size, data)) goto bail; + fnode->ea_size_l = pos; + ret: + hpfs_i(inode)->i_ea_size += 5 + strlen(key) + size; + return; + bail: + if (fnode->ea_secno) + if (fnode->ea_anode) hpfs_truncate_btree(s, fnode->ea_secno, 1, (fnode->ea_size_l + 511) >> 9); + else hpfs_free_sectors(s, fnode->ea_secno + ((fnode->ea_size_l + 511) >> 9), len - ((fnode->ea_size_l + 511) >> 9)); + else fnode->ea_secno = fnode->ea_size_l = 0; +} + diff --git a/fs/hpfs/file.c b/fs/hpfs/file.c new file mode 100644 index 00000000000..ab144dabd87 --- /dev/null +++ b/fs/hpfs/file.c @@ -0,0 +1,140 @@ +/* + * linux/fs/hpfs/file.c + * + * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999 + * + * file VFS functions + */ + +#include "hpfs_fn.h" + +#define BLOCKS(size) (((size) + 511) >> 9) + +static int hpfs_file_release(struct inode *inode, struct file *file) +{ + lock_kernel(); + hpfs_write_if_changed(inode); + unlock_kernel(); + return 0; +} + +int hpfs_file_fsync(struct file *file, struct dentry *dentry, int datasync) +{ + /*return file_fsync(file, dentry);*/ + return 0; /* Don't fsync :-) */ +} + +/* + * generic_file_read often calls bmap with non-existing sector, + * so we must ignore such errors. + */ + +static secno hpfs_bmap(struct inode *inode, unsigned file_secno) +{ + struct hpfs_inode_info *hpfs_inode = hpfs_i(inode); + unsigned n, disk_secno; + struct fnode *fnode; + struct buffer_head *bh; + if (BLOCKS(hpfs_i(inode)->mmu_private) <= file_secno) return 0; + n = file_secno - hpfs_inode->i_file_sec; + if (n < hpfs_inode->i_n_secs) return hpfs_inode->i_disk_sec + n; + if (!(fnode = hpfs_map_fnode(inode->i_sb, inode->i_ino, &bh))) return 0; + disk_secno = hpfs_bplus_lookup(inode->i_sb, inode, &fnode->btree, file_secno, bh); + if (disk_secno == -1) return 0; + if (hpfs_chk_sectors(inode->i_sb, disk_secno, 1, "bmap")) return 0; + return disk_secno; +} + +static void hpfs_truncate(struct inode *i) +{ + if (IS_IMMUTABLE(i)) return /*-EPERM*/; + lock_kernel(); + hpfs_i(i)->i_n_secs = 0; + i->i_blocks = 1 + ((i->i_size + 511) >> 9); + hpfs_i(i)->mmu_private = i->i_size; + hpfs_truncate_btree(i->i_sb, i->i_ino, 1, ((i->i_size + 511) >> 9)); + hpfs_write_inode(i); + hpfs_i(i)->i_n_secs = 0; + unlock_kernel(); +} + +static int hpfs_get_block(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create) +{ + secno s; + s = hpfs_bmap(inode, iblock); + if (s) { + map_bh(bh_result, inode->i_sb, s); + return 0; + } + if (!create) return 0; + if (iblock<<9 != hpfs_i(inode)->mmu_private) { + BUG(); + return -EIO; + } + if ((s = hpfs_add_sector_to_btree(inode->i_sb, inode->i_ino, 1, inode->i_blocks - 1)) == -1) { + hpfs_truncate_btree(inode->i_sb, inode->i_ino, 1, inode->i_blocks - 1); + return -ENOSPC; + } + inode->i_blocks++; + hpfs_i(inode)->mmu_private += 512; + set_buffer_new(bh_result); + map_bh(bh_result, inode->i_sb, s); + return 0; +} + +static int hpfs_writepage(struct page *page, struct writeback_control *wbc) +{ + return block_write_full_page(page,hpfs_get_block, wbc); +} +static int hpfs_readpage(struct file *file, struct page *page) +{ + return block_read_full_page(page,hpfs_get_block); +} +static int hpfs_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to) +{ + return cont_prepare_write(page,from,to,hpfs_get_block, + &hpfs_i(page->mapping->host)->mmu_private); +} +static sector_t _hpfs_bmap(struct address_space *mapping, sector_t block) +{ + return generic_block_bmap(mapping,block,hpfs_get_block); +} +struct address_space_operations hpfs_aops = { + .readpage = hpfs_readpage, + .writepage = hpfs_writepage, + .sync_page = block_sync_page, + .prepare_write = hpfs_prepare_write, + .commit_write = generic_commit_write, + .bmap = _hpfs_bmap +}; + +static ssize_t hpfs_file_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + ssize_t retval; + + retval = generic_file_write(file, buf, count, ppos); + if (retval > 0) { + struct inode *inode = file->f_dentry->d_inode; + inode->i_mtime = CURRENT_TIME_SEC; + hpfs_i(inode)->i_dirty = 1; + } + return retval; +} + +struct file_operations hpfs_file_ops = +{ + .llseek = generic_file_llseek, + .read = generic_file_read, + .write = hpfs_file_write, + .mmap = generic_file_mmap, + .release = hpfs_file_release, + .fsync = hpfs_file_fsync, + .sendfile = generic_file_sendfile, +}; + +struct inode_operations hpfs_file_iops = +{ + .truncate = hpfs_truncate, + .setattr = hpfs_notify_change, +}; diff --git a/fs/hpfs/hpfs.h b/fs/hpfs/hpfs.h new file mode 100644 index 00000000000..0e84c73cd9c --- /dev/null +++ b/fs/hpfs/hpfs.h @@ -0,0 +1,493 @@ +/* + * linux/fs/hpfs/hpfs.h + * + * HPFS structures by Chris Smith, 1993 + * + * a little bit modified by Mikulas Patocka, 1998-1999 + */ + +/* The paper + + Duncan, Roy + Design goals and implementation of the new High Performance File System + Microsoft Systems Journal Sept 1989 v4 n5 p1(13) + + describes what HPFS looked like when it was new, and it is the source + of most of the information given here. The rest is conjecture. + + For definitive information on the Duncan paper, see it, not this file. + For definitive information on HPFS, ask somebody else -- this is guesswork. + There are certain to be many mistakes. */ + +/* Notation */ + +typedef unsigned secno; /* sector number, partition relative */ + +typedef secno dnode_secno; /* sector number of a dnode */ +typedef secno fnode_secno; /* sector number of an fnode */ +typedef secno anode_secno; /* sector number of an anode */ + +typedef u32 time32_t; /* 32-bit time_t type */ + +/* sector 0 */ + +/* The boot block is very like a FAT boot block, except that the + 29h signature byte is 28h instead, and the ID string is "HPFS". */ + +#define BB_MAGIC 0xaa55 + +struct hpfs_boot_block +{ + unsigned char jmp[3]; + unsigned char oem_id[8]; + unsigned char bytes_per_sector[2]; /* 512 */ + unsigned char sectors_per_cluster; + unsigned char n_reserved_sectors[2]; + unsigned char n_fats; + unsigned char n_rootdir_entries[2]; + unsigned char n_sectors_s[2]; + unsigned char media_byte; + unsigned short sectors_per_fat; + unsigned short sectors_per_track; + unsigned short heads_per_cyl; + unsigned int n_hidden_sectors; + unsigned int n_sectors_l; /* size of partition */ + unsigned char drive_number; + unsigned char mbz; + unsigned char sig_28h; /* 28h */ + unsigned char vol_serno[4]; + unsigned char vol_label[11]; + unsigned char sig_hpfs[8]; /* "HPFS " */ + unsigned char pad[448]; + unsigned short magic; /* aa55 */ +}; + + +/* sector 16 */ + +/* The super block has the pointer to the root directory. */ + +#define SB_MAGIC 0xf995e849 + +struct hpfs_super_block +{ + unsigned magic; /* f995 e849 */ + unsigned magic1; /* fa53 e9c5, more magic? */ + /*unsigned huh202;*/ /* ?? 202 = N. of B. in 1.00390625 S.*/ + char version; /* version of a filesystem usually 2 */ + char funcversion; /* functional version - oldest version + of filesystem that can understand + this disk */ + unsigned short int zero; /* 0 */ + fnode_secno root; /* fnode of root directory */ + secno n_sectors; /* size of filesystem */ + unsigned n_badblocks; /* number of bad blocks */ + secno bitmaps; /* pointers to free space bit maps */ + unsigned zero1; /* 0 */ + secno badblocks; /* bad block list */ + unsigned zero3; /* 0 */ + time32_t last_chkdsk; /* date last checked, 0 if never */ + /*unsigned zero4;*/ /* 0 */ + time32_t last_optimize; /* date last optimized, 0 if never */ + secno n_dir_band; /* number of sectors in dir band */ + secno dir_band_start; /* first sector in dir band */ + secno dir_band_end; /* last sector in dir band */ + secno dir_band_bitmap; /* free space map, 1 dnode per bit */ + char volume_name[32]; /* not used */ + secno user_id_table; /* 8 preallocated sectors - user id */ + unsigned zero6[103]; /* 0 */ +}; + + +/* sector 17 */ + +/* The spare block has pointers to spare sectors. */ + +#define SP_MAGIC 0xf9911849 + +struct hpfs_spare_block +{ + unsigned magic; /* f991 1849 */ + unsigned magic1; /* fa52 29c5, more magic? */ + + unsigned dirty: 1; /* 0 clean, 1 "improperly stopped" */ + /*unsigned flag1234: 4;*/ /* unknown flags */ + unsigned sparedir_used: 1; /* spare dirblks used */ + unsigned hotfixes_used: 1; /* hotfixes used */ + unsigned bad_sector: 1; /* bad sector, corrupted disk (???) */ + unsigned bad_bitmap: 1; /* bad bitmap */ + unsigned fast: 1; /* partition was fast formatted */ + unsigned old_wrote: 1; /* old version wrote to partion */ + unsigned old_wrote_1: 1; /* old version wrote to partion (?) */ + unsigned install_dasd_limits: 1; /* HPFS386 flags */ + unsigned resynch_dasd_limits: 1; + unsigned dasd_limits_operational: 1; + unsigned multimedia_active: 1; + unsigned dce_acls_active: 1; + unsigned dasd_limits_dirty: 1; + unsigned flag67: 2; + unsigned char mm_contlgulty; + unsigned char unused; + + secno hotfix_map; /* info about remapped bad sectors */ + unsigned n_spares_used; /* number of hotfixes */ + unsigned n_spares; /* number of spares in hotfix map */ + unsigned n_dnode_spares_free; /* spare dnodes unused */ + unsigned n_dnode_spares; /* length of spare_dnodes[] list, + follows in this block*/ + secno code_page_dir; /* code page directory block */ + unsigned n_code_pages; /* number of code pages */ + /*unsigned large_numbers[2];*/ /* ?? */ + unsigned super_crc; /* on HPFS386 and LAN Server this is + checksum of superblock, on normal + OS/2 unused */ + unsigned spare_crc; /* on HPFS386 checksum of spareblock */ + unsigned zero1[15]; /* unused */ + dnode_secno spare_dnodes[100]; /* emergency free dnode list */ + unsigned zero2[1]; /* room for more? */ +}; + +/* The bad block list is 4 sectors long. The first word must be zero, + the remaining words give n_badblocks bad block numbers. + I bet you can see it coming... */ + +#define BAD_MAGIC 0 + +/* The hotfix map is 4 sectors long. It looks like + + secno from[n_spares]; + secno to[n_spares]; + + The to[] list is initialized to point to n_spares preallocated empty + sectors. The from[] list contains the sector numbers of bad blocks + which have been remapped to corresponding sectors in the to[] list. + n_spares_used gives the length of the from[] list. */ + + +/* Sectors 18 and 19 are preallocated and unused. + Maybe they're spares for 16 and 17, but simple substitution fails. */ + + +/* The code page info pointed to by the spare block consists of an index + block and blocks containing uppercasing tables. I don't know what + these are for (CHKDSK, maybe?) -- OS/2 does not seem to use them + itself. Linux doesn't use them either. */ + +/* block pointed to by spareblock->code_page_dir */ + +#define CP_DIR_MAGIC 0x494521f7 + +struct code_page_directory +{ + unsigned magic; /* 4945 21f7 */ + unsigned n_code_pages; /* number of pointers following */ + unsigned zero1[2]; + struct { + unsigned short ix; /* index */ + unsigned short code_page_number; /* code page number */ + unsigned bounds; /* matches corresponding word + in data block */ + secno code_page_data; /* sector number of a code_page_data + containing c.p. array */ + unsigned short index; /* index in c.p. array in that sector*/ + unsigned short unknown; /* some unknown value; usually 0; + 2 in Japanese version */ + } array[31]; /* unknown length */ +}; + +/* blocks pointed to by code_page_directory */ + +#define CP_DATA_MAGIC 0x894521f7 + +struct code_page_data +{ + unsigned magic; /* 8945 21f7 */ + unsigned n_used; /* # elements used in c_p_data[] */ + unsigned bounds[3]; /* looks a bit like + (beg1,end1), (beg2,end2) + one byte each */ + unsigned short offs[3]; /* offsets from start of sector + to start of c_p_data[ix] */ + struct { + unsigned short ix; /* index */ + unsigned short code_page_number; /* code page number */ + unsigned short unknown; /* the same as in cp directory */ + unsigned char map[128]; /* upcase table for chars 80..ff */ + unsigned short zero2; + } code_page[3]; + unsigned char incognita[78]; +}; + + +/* Free space bitmaps are 4 sectors long, which is 16384 bits. + 16384 sectors is 8 meg, and each 8 meg band has a 4-sector bitmap. + Bit order in the maps is little-endian. 0 means taken, 1 means free. + + Bit map sectors are marked allocated in the bit maps, and so are sectors + off the end of the partition. + + Band 0 is sectors 0-3fff, its map is in sectors 18-1b. + Band 1 is 4000-7fff, its map is in 7ffc-7fff. + Band 2 is 8000-ffff, its map is in 8000-8003. + The remaining bands have maps in their first (even) or last (odd) 4 sectors + -- if the last, partial, band is odd its map is in its last 4 sectors. + + The bitmap locations are given in a table pointed to by the super block. + No doubt they aren't constrained to be at 18, 7ffc, 8000, ...; that is + just where they usually are. + + The "directory band" is a bunch of sectors preallocated for dnodes. + It has a 4-sector free space bitmap of its own. Each bit in the map + corresponds to one 4-sector dnode, bit 0 of the map corresponding to + the first 4 sectors of the directory band. The entire band is marked + allocated in the main bitmap. The super block gives the locations + of the directory band and its bitmap. ("band" doesn't mean it is + 8 meg long; it isn't.) */ + + +/* dnode: directory. 4 sectors long */ + +/* A directory is a tree of dnodes. The fnode for a directory + contains one pointer, to the root dnode of the tree. The fnode + never moves, the dnodes do the B-tree thing, splitting and merging + as files are added and removed. */ + +#define DNODE_MAGIC 0x77e40aae + +struct dnode { + unsigned magic; /* 77e4 0aae */ + unsigned first_free; /* offset from start of dnode to + first free dir entry */ + unsigned root_dnode:1; /* Is it root dnode? */ + unsigned increment_me:31; /* some kind of activity counter? + Neither HPFS.IFS nor CHKDSK cares + if you change this word */ + secno up; /* (root dnode) directory's fnode + (nonroot) parent dnode */ + dnode_secno self; /* pointer to this dnode */ + unsigned char dirent[2028]; /* one or more dirents */ +}; + +struct hpfs_dirent { + unsigned short length; /* offset to next dirent */ + unsigned first: 1; /* set on phony ^A^A (".") entry */ + unsigned has_acl: 1; + unsigned down: 1; /* down pointer present (after name) */ + unsigned last: 1; /* set on phony \377 entry */ + unsigned has_ea: 1; /* entry has EA */ + unsigned has_xtd_perm: 1; /* has extended perm list (???) */ + unsigned has_explicit_acl: 1; + unsigned has_needea: 1; /* ?? some EA has NEEDEA set + I have no idea why this is + interesting in a dir entry */ + unsigned read_only: 1; /* dos attrib */ + unsigned hidden: 1; /* dos attrib */ + unsigned system: 1; /* dos attrib */ + unsigned flag11: 1; /* would be volume label dos attrib */ + unsigned directory: 1; /* dos attrib */ + unsigned archive: 1; /* dos attrib */ + unsigned not_8x3: 1; /* name is not 8.3 */ + unsigned flag15: 1; + fnode_secno fnode; /* fnode giving allocation info */ + time32_t write_date; /* mtime */ + unsigned file_size; /* file length, bytes */ + time32_t read_date; /* atime */ + time32_t creation_date; /* ctime */ + unsigned ea_size; /* total EA length, bytes */ + unsigned char no_of_acls : 3; /* number of ACL's */ + unsigned char reserver : 5; + unsigned char ix; /* code page index (of filename), see + struct code_page_data */ + unsigned char namelen, name[1]; /* file name */ + /* dnode_secno down; btree down pointer, if present, + follows name on next word boundary, or maybe it + precedes next dirent, which is on a word boundary. */ +}; + + +/* B+ tree: allocation info in fnodes and anodes */ + +/* dnodes point to fnodes which are responsible for listing the sectors + assigned to the file. This is done with trees of (length,address) + pairs. (Actually triples, of (length, file-address, disk-address) + which can represent holes. Find out if HPFS does that.) + At any rate, fnodes contain a small tree; if subtrees are needed + they occupy essentially a full block in anodes. A leaf-level tree node + has 3-word entries giving sector runs, a non-leaf node has 2-word + entries giving subtree pointers. A flag in the header says which. */ + +struct bplus_leaf_node +{ + unsigned file_secno; /* first file sector in extent */ + unsigned length; /* length, sectors */ + secno disk_secno; /* first corresponding disk sector */ +}; + +struct bplus_internal_node +{ + unsigned file_secno; /* subtree maps sectors < this */ + anode_secno down; /* pointer to subtree */ +}; + +struct bplus_header +{ + unsigned hbff: 1; /* high bit of first free entry offset */ + unsigned flag1: 1; + unsigned flag2: 1; + unsigned flag3: 1; + unsigned flag4: 1; + unsigned fnode_parent: 1; /* ? we're pointed to by an fnode, + the data btree or some ea or the + main ea bootage pointer ea_secno */ + /* also can get set in fnodes, which + may be a chkdsk glitch or may mean + this bit is irrelevant in fnodes, + or this interpretation is all wet */ + unsigned binary_search: 1; /* suggest binary search (unused) */ + unsigned internal: 1; /* 1 -> (internal) tree of anodes + 0 -> (leaf) list of extents */ + unsigned char fill[3]; + unsigned char n_free_nodes; /* free nodes in following array */ + unsigned char n_used_nodes; /* used nodes in following array */ + unsigned short first_free; /* offset from start of header to + first free node in array */ + union { + struct bplus_internal_node internal[0]; /* (internal) 2-word entries giving + subtree pointers */ + struct bplus_leaf_node external[0]; /* (external) 3-word entries giving + sector runs */ + } u; +}; + +/* fnode: root of allocation b+ tree, and EA's */ + +/* Every file and every directory has one fnode, pointed to by the directory + entry and pointing to the file's sectors or directory's root dnode. EA's + are also stored here, and there are said to be ACL's somewhere here too. */ + +#define FNODE_MAGIC 0xf7e40aae + +struct fnode +{ + unsigned magic; /* f7e4 0aae */ + unsigned zero1[2]; /* read history */ + unsigned char len, name[15]; /* true length, truncated name */ + fnode_secno up; /* pointer to file's directory fnode */ + /*unsigned zero2[3];*/ + secno acl_size_l; + secno acl_secno; + unsigned short acl_size_s; + char acl_anode; + char zero2; /* history bit count */ + unsigned ea_size_l; /* length of disk-resident ea's */ + secno ea_secno; /* first sector of disk-resident ea's*/ + unsigned short ea_size_s; /* length of fnode-resident ea's */ + + unsigned flag0: 1; + unsigned ea_anode: 1; /* 1 -> ea_secno is an anode */ + unsigned flag2: 1; + unsigned flag3: 1; + unsigned flag4: 1; + unsigned flag5: 1; + unsigned flag6: 1; + unsigned flag7: 1; + unsigned dirflag: 1; /* 1 -> directory. first & only extent + points to dnode. */ + unsigned flag9: 1; + unsigned flag10: 1; + unsigned flag11: 1; + unsigned flag12: 1; + unsigned flag13: 1; + unsigned flag14: 1; + unsigned flag15: 1; + + struct bplus_header btree; /* b+ tree, 8 extents or 12 subtrees */ + union { + struct bplus_leaf_node external[8]; + struct bplus_internal_node internal[12]; + } u; + + unsigned file_size; /* file length, bytes */ + unsigned n_needea; /* number of EA's with NEEDEA set */ + char user_id[16]; /* unused */ + unsigned short ea_offs; /* offset from start of fnode + to first fnode-resident ea */ + char dasd_limit_treshhold; + char dasd_limit_delta; + unsigned dasd_limit; + unsigned dasd_usage; + /*unsigned zero5[2];*/ + unsigned char ea[316]; /* zero or more EA's, packed together + with no alignment padding. + (Do not use this name, get here + via fnode + ea_offs. I think.) */ +}; + + +/* anode: 99.44% pure allocation tree */ + +#define ANODE_MAGIC 0x37e40aae + +struct anode +{ + unsigned magic; /* 37e4 0aae */ + anode_secno self; /* pointer to this anode */ + secno up; /* parent anode or fnode */ + + struct bplus_header btree; /* b+tree, 40 extents or 60 subtrees */ + union { + struct bplus_leaf_node external[40]; + struct bplus_internal_node internal[60]; + } u; + + unsigned fill[3]; /* unused */ +}; + + +/* extended attributes. + + A file's EA info is stored as a list of (name,value) pairs. It is + usually in the fnode, but (if it's large) it is moved to a single + sector run outside the fnode, or to multiple runs with an anode tree + that points to them. + + The value of a single EA is stored along with the name, or (if large) + it is moved to a single sector run, or multiple runs pointed to by an + anode tree, pointed to by the value field of the (name,value) pair. + + Flags in the EA tell whether the value is immediate, in a single sector + run, or in multiple runs. Flags in the fnode tell whether the EA list + is immediate, in a single run, or in multiple runs. */ + +struct extended_attribute +{ + unsigned indirect: 1; /* 1 -> value gives sector number + where real value starts */ + unsigned anode: 1; /* 1 -> sector is an anode + that points to fragmented value */ + unsigned flag2: 1; + unsigned flag3: 1; + unsigned flag4: 1; + unsigned flag5: 1; + unsigned flag6: 1; + unsigned needea: 1; /* required ea */ + unsigned char namelen; /* length of name, bytes */ + unsigned short valuelen; /* length of value, bytes */ + unsigned char name[0]; + /* + unsigned char name[namelen]; ascii attrib name + unsigned char nul; terminating '\0', not counted + unsigned char value[valuelen]; value, arbitrary + if this.indirect, valuelen is 8 and the value is + unsigned length; real length of value, bytes + secno secno; sector address where it starts + if this.anode, the above sector number is the root of an anode tree + which points to the value. + */ +}; + +/* + Local Variables: + comment-column: 40 + End: +*/ diff --git a/fs/hpfs/hpfs_fn.h b/fs/hpfs/hpfs_fn.h new file mode 100644 index 00000000000..6628c3b352c --- /dev/null +++ b/fs/hpfs/hpfs_fn.h @@ -0,0 +1,338 @@ +/* + * linux/fs/hpfs/hpfs_fn.h + * + * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999 + * + * function headers + */ + +//#define DBG +//#define DEBUG_LOCKS + +#include <linux/pagemap.h> +#include <linux/buffer_head.h> +#include <linux/hpfs_fs.h> +#include <linux/slab.h> +#include <linux/smp_lock.h> + +#include "hpfs.h" + +#define EIOERROR EIO +#define EFSERROR EPERM +#define EMEMERROR ENOMEM + +#define ANODE_ALLOC_FWD 512 +#define FNODE_ALLOC_FWD 0 +#define ALLOC_FWD_MIN 16 +#define ALLOC_FWD_MAX 128 +#define ALLOC_M 1 +#define FNODE_RD_AHEAD 16 +#define ANODE_RD_AHEAD 16 +#define DNODE_RD_AHEAD 4 + +#define FREE_DNODES_ADD 58 +#define FREE_DNODES_DEL 29 + +#define CHKCOND(x,y) if (!(x)) printk y + +#ifdef DBG +#define PRINTK(x) printk x +#else +#undef PRINTK +#define PRINTK(x) +#endif + +struct hpfs_inode_info { + loff_t mmu_private; + ino_t i_parent_dir; /* (directories) gives fnode of parent dir */ + unsigned i_dno; /* (directories) root dnode */ + unsigned i_dpos; /* (directories) temp for readdir */ + unsigned i_dsubdno; /* (directories) temp for readdir */ + unsigned i_file_sec; /* (files) minimalist cache of alloc info */ + unsigned i_disk_sec; /* (files) minimalist cache of alloc info */ + unsigned i_n_secs; /* (files) minimalist cache of alloc info */ + unsigned i_ea_size; /* size of extended attributes */ + unsigned i_conv : 2; /* (files) crlf->newline hackery */ + unsigned i_ea_mode : 1; /* file's permission is stored in ea */ + unsigned i_ea_uid : 1; /* file's uid is stored in ea */ + unsigned i_ea_gid : 1; /* file's gid is stored in ea */ + unsigned i_dirty : 1; + struct semaphore i_sem; + struct semaphore i_parent; + loff_t **i_rddir_off; + struct inode vfs_inode; +}; + +struct hpfs_sb_info { + ino_t sb_root; /* inode number of root dir */ + unsigned sb_fs_size; /* file system size, sectors */ + unsigned sb_bitmaps; /* sector number of bitmap list */ + unsigned sb_dirband_start; /* directory band start sector */ + unsigned sb_dirband_size; /* directory band size, dnodes */ + unsigned sb_dmap; /* sector number of dnode bit map */ + unsigned sb_n_free; /* free blocks for statfs, or -1 */ + unsigned sb_n_free_dnodes; /* free dnodes for statfs, or -1 */ + uid_t sb_uid; /* uid from mount options */ + gid_t sb_gid; /* gid from mount options */ + umode_t sb_mode; /* mode from mount options */ + unsigned sb_conv : 2; /* crlf->newline hackery */ + unsigned sb_eas : 2; /* eas: 0-ignore, 1-ro, 2-rw */ + unsigned sb_err : 2; /* on errs: 0-cont, 1-ro, 2-panic */ + unsigned sb_chk : 2; /* checks: 0-no, 1-normal, 2-strict */ + unsigned sb_lowercase : 1; /* downcase filenames hackery */ + unsigned sb_was_error : 1; /* there was an error, set dirty flag */ + unsigned sb_chkdsk : 2; /* chkdsk: 0-no, 1-on errs, 2-allways */ + unsigned char *sb_cp_table; /* code page tables: */ + /* 128 bytes uppercasing table & */ + /* 128 bytes lowercasing table */ + unsigned *sb_bmp_dir; /* main bitmap directory */ + unsigned sb_c_bitmap; /* current bitmap */ + unsigned sb_max_fwd_alloc; /* max forwad allocation */ + struct semaphore hpfs_creation_de; /* when creating dirents, nobody else + can alloc blocks */ + /*unsigned sb_mounting : 1;*/ + int sb_timeshift; +}; + +/* + * conv= options + */ + +#define CONV_BINARY 0 /* no conversion */ +#define CONV_TEXT 1 /* crlf->newline */ +#define CONV_AUTO 2 /* decide based on file contents */ + +/* Four 512-byte buffers and the 2k block obtained by concatenating them */ + +struct quad_buffer_head { + struct buffer_head *bh[4]; + void *data; +}; + +/* The b-tree down pointer from a dir entry */ + +static inline dnode_secno de_down_pointer (struct hpfs_dirent *de) +{ + CHKCOND(de->down,("HPFS: de_down_pointer: !de->down\n")); + return *(dnode_secno *) ((void *) de + de->length - 4); +} + +/* The first dir entry in a dnode */ + +static inline struct hpfs_dirent *dnode_first_de (struct dnode *dnode) +{ + return (void *) dnode->dirent; +} + +/* The end+1 of the dir entries */ + +static inline struct hpfs_dirent *dnode_end_de (struct dnode *dnode) +{ + CHKCOND(dnode->first_free>=0x14 && dnode->first_free<=0xa00,("HPFS: dnode_end_de: dnode->first_free = %d\n",(int)dnode->first_free)); + return (void *) dnode + dnode->first_free; +} + +/* The dir entry after dir entry de */ + +static inline struct hpfs_dirent *de_next_de (struct hpfs_dirent *de) +{ + CHKCOND(de->length>=0x20 && de->length<0x800,("HPFS: de_next_de: de->length = %d\n",(int)de->length)); + return (void *) de + de->length; +} + +static inline struct extended_attribute *fnode_ea(struct fnode *fnode) +{ + return (struct extended_attribute *)((char *)fnode + fnode->ea_offs + fnode->acl_size_s); +} + +static inline struct extended_attribute *fnode_end_ea(struct fnode *fnode) +{ + return (struct extended_attribute *)((char *)fnode + fnode->ea_offs + fnode->acl_size_s + fnode->ea_size_s); +} + +static inline struct extended_attribute *next_ea(struct extended_attribute *ea) +{ + return (struct extended_attribute *)((char *)ea + 5 + ea->namelen + ea->valuelen); +} + +static inline secno ea_sec(struct extended_attribute *ea) +{ + return *(secno *)((char *)ea + 9 + ea->namelen); +} + +static inline secno ea_len(struct extended_attribute *ea) +{ + return *(secno *)((char *)ea + 5 + ea->namelen); +} + +static inline char *ea_data(struct extended_attribute *ea) +{ + return (char *)((char *)ea + 5 + ea->namelen); +} + +static inline unsigned de_size(int namelen, secno down_ptr) +{ + return ((0x1f + namelen + 3) & ~3) + (down_ptr ? 4 : 0); +} + +static inline void copy_de(struct hpfs_dirent *dst, struct hpfs_dirent *src) +{ + int a; + int n; + if (!dst || !src) return; + a = dst->down; + n = dst->not_8x3; + memcpy((char *)dst + 2, (char *)src + 2, 28); + dst->down = a; + dst->not_8x3 = n; +} + +static inline unsigned tstbits(unsigned *bmp, unsigned b, unsigned n) +{ + int i; + if ((b >= 0x4000) || (b + n - 1 >= 0x4000)) return n; + if (!((bmp[(b & 0x3fff) >> 5] >> (b & 0x1f)) & 1)) return 1; + for (i = 1; i < n; i++) + if (/*b+i < 0x4000 &&*/ !((bmp[((b+i) & 0x3fff) >> 5] >> ((b+i) & 0x1f)) & 1)) + return i + 1; + return 0; +} + +/* alloc.c */ + +int hpfs_chk_sectors(struct super_block *, secno, int, char *); +secno hpfs_alloc_sector(struct super_block *, secno, unsigned, int, int); +int hpfs_alloc_if_possible(struct super_block *, secno); +void hpfs_free_sectors(struct super_block *, secno, unsigned); +int hpfs_check_free_dnodes(struct super_block *, int); +void hpfs_free_dnode(struct super_block *, secno); +struct dnode *hpfs_alloc_dnode(struct super_block *, secno, dnode_secno *, struct quad_buffer_head *, int); +struct fnode *hpfs_alloc_fnode(struct super_block *, secno, fnode_secno *, struct buffer_head **); +struct anode *hpfs_alloc_anode(struct super_block *, secno, anode_secno *, struct buffer_head **); + +/* anode.c */ + +secno hpfs_bplus_lookup(struct super_block *, struct inode *, struct bplus_header *, unsigned, struct buffer_head *); +secno hpfs_add_sector_to_btree(struct super_block *, secno, int, unsigned); +void hpfs_remove_btree(struct super_block *, struct bplus_header *); +int hpfs_ea_read(struct super_block *, secno, int, unsigned, unsigned, char *); +int hpfs_ea_write(struct super_block *, secno, int, unsigned, unsigned, char *); +void hpfs_ea_remove(struct super_block *, secno, int, unsigned); +void hpfs_truncate_btree(struct super_block *, secno, int, unsigned); +void hpfs_remove_fnode(struct super_block *, fnode_secno fno); + +/* buffer.c */ + +void hpfs_lock_creation(struct super_block *); +void hpfs_unlock_creation(struct super_block *); +void *hpfs_map_sector(struct super_block *, unsigned, struct buffer_head **, int); +void *hpfs_get_sector(struct super_block *, unsigned, struct buffer_head **); +void *hpfs_map_4sectors(struct super_block *, unsigned, struct quad_buffer_head *, int); +void *hpfs_get_4sectors(struct super_block *, unsigned, struct quad_buffer_head *); +void hpfs_brelse4(struct quad_buffer_head *); +void hpfs_mark_4buffers_dirty(struct quad_buffer_head *); + +/* dentry.c */ + +void hpfs_set_dentry_operations(struct dentry *); + +/* dir.c */ + +struct dentry *hpfs_lookup(struct inode *, struct dentry *, struct nameidata *); +extern struct file_operations hpfs_dir_ops; + +/* dnode.c */ + +void hpfs_add_pos(struct inode *, loff_t *); +void hpfs_del_pos(struct inode *, loff_t *); +struct hpfs_dirent *hpfs_add_de(struct super_block *, struct dnode *, unsigned char *, unsigned, secno); +int hpfs_add_dirent(struct inode *, unsigned char *, unsigned, struct hpfs_dirent *, int); +int hpfs_remove_dirent(struct inode *, dnode_secno, struct hpfs_dirent *, struct quad_buffer_head *, int); +void hpfs_count_dnodes(struct super_block *, dnode_secno, int *, int *, int *); +dnode_secno hpfs_de_as_down_as_possible(struct super_block *, dnode_secno dno); +struct hpfs_dirent *map_pos_dirent(struct inode *, loff_t *, struct quad_buffer_head *); +struct hpfs_dirent *map_dirent(struct inode *, dnode_secno, char *, unsigned, dnode_secno *, struct quad_buffer_head *); +void hpfs_remove_dtree(struct super_block *, dnode_secno); +struct hpfs_dirent *map_fnode_dirent(struct super_block *, fnode_secno, struct fnode *, struct quad_buffer_head *); + +/* ea.c */ + +void hpfs_ea_ext_remove(struct super_block *, secno, int, unsigned); +int hpfs_read_ea(struct super_block *, struct fnode *, char *, char *, int); +char *hpfs_get_ea(struct super_block *, struct fnode *, char *, int *); +void hpfs_set_ea(struct inode *, struct fnode *, char *, char *, int); + +/* file.c */ + +int hpfs_file_fsync(struct file *, struct dentry *, int); +extern struct file_operations hpfs_file_ops; +extern struct inode_operations hpfs_file_iops; +extern struct address_space_operations hpfs_aops; + +/* inode.c */ + +void hpfs_init_inode(struct inode *); +void hpfs_read_inode(struct inode *); +void hpfs_write_inode(struct inode *); +void hpfs_write_inode_nolock(struct inode *); +int hpfs_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); + +/* name.c */ + +unsigned char hpfs_upcase(unsigned char *, unsigned char); +int hpfs_chk_name(unsigned char *, unsigned *); +char *hpfs_translate_name(struct super_block *, unsigned char *, unsigned, int, int); +int hpfs_compare_names(struct super_block *, unsigned char *, unsigned, unsigned char *, unsigned, int); +int hpfs_is_name_long(unsigned char *, unsigned); +void hpfs_adjust_length(unsigned char *, unsigned *); +void hpfs_decide_conv(struct inode *, unsigned char *, unsigned); + +/* namei.c */ + +extern struct inode_operations hpfs_dir_iops; +extern struct address_space_operations hpfs_symlink_aops; + +static inline struct hpfs_inode_info *hpfs_i(struct inode *inode) +{ + return list_entry(inode, struct hpfs_inode_info, vfs_inode); +} + +static inline struct hpfs_sb_info *hpfs_sb(struct super_block *sb) +{ + return sb->s_fs_info; +} + +/* super.c */ + +void hpfs_error(struct super_block *, char *, ...); +int hpfs_stop_cycles(struct super_block *, int, int *, int *, char *); +unsigned hpfs_count_one_bitmap(struct super_block *, secno); + +/* + * local time (HPFS) to GMT (Unix) + */ + +static inline time_t local_to_gmt(struct super_block *s, time32_t t) +{ + extern struct timezone sys_tz; + return t + sys_tz.tz_minuteswest * 60 + hpfs_sb(s)->sb_timeshift; +} + +static inline time32_t gmt_to_local(struct super_block *s, time_t t) +{ + extern struct timezone sys_tz; + return t - sys_tz.tz_minuteswest * 60 - hpfs_sb(s)->sb_timeshift; +} diff --git a/fs/hpfs/inode.c b/fs/hpfs/inode.c new file mode 100644 index 00000000000..38b1741fa53 --- /dev/null +++ b/fs/hpfs/inode.c @@ -0,0 +1,291 @@ +/* + * linux/fs/hpfs/inode.c + * + * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999 + * + * inode VFS functions + */ + +#include "hpfs_fn.h" + +void hpfs_init_inode(struct inode *i) +{ + struct super_block *sb = i->i_sb; + struct hpfs_inode_info *hpfs_inode = hpfs_i(i); + + i->i_uid = hpfs_sb(sb)->sb_uid; + i->i_gid = hpfs_sb(sb)->sb_gid; + i->i_mode = hpfs_sb(sb)->sb_mode; + hpfs_inode->i_conv = hpfs_sb(sb)->sb_conv; + i->i_blksize = 512; + i->i_size = -1; + i->i_blocks = -1; + + hpfs_inode->i_dno = 0; + hpfs_inode->i_n_secs = 0; + hpfs_inode->i_file_sec = 0; + hpfs_inode->i_disk_sec = 0; + hpfs_inode->i_dpos = 0; + hpfs_inode->i_dsubdno = 0; + hpfs_inode->i_ea_mode = 0; + hpfs_inode->i_ea_uid = 0; + hpfs_inode->i_ea_gid = 0; + hpfs_inode->i_ea_size = 0; + + hpfs_inode->i_rddir_off = NULL; + hpfs_inode->i_dirty = 0; + + i->i_ctime.tv_sec = i->i_ctime.tv_nsec = 0; + i->i_mtime.tv_sec = i->i_mtime.tv_nsec = 0; + i->i_atime.tv_sec = i->i_atime.tv_nsec = 0; +} + +void hpfs_read_inode(struct inode *i) +{ + struct buffer_head *bh; + struct fnode *fnode; + struct super_block *sb = i->i_sb; + struct hpfs_inode_info *hpfs_inode = hpfs_i(i); + unsigned char *ea; + int ea_size; + + if (!(fnode = hpfs_map_fnode(sb, i->i_ino, &bh))) { + /*i->i_mode |= S_IFREG; + i->i_mode &= ~0111; + i->i_op = &hpfs_file_iops; + i->i_fop = &hpfs_file_ops; + i->i_nlink = 0;*/ + make_bad_inode(i); + return; + } + if (hpfs_sb(i->i_sb)->sb_eas) { + if ((ea = hpfs_get_ea(i->i_sb, fnode, "UID", &ea_size))) { + if (ea_size == 2) { + i->i_uid = le16_to_cpu(*(u16*)ea); + hpfs_inode->i_ea_uid = 1; + } + kfree(ea); + } + if ((ea = hpfs_get_ea(i->i_sb, fnode, "GID", &ea_size))) { + if (ea_size == 2) { + i->i_gid = le16_to_cpu(*(u16*)ea); + hpfs_inode->i_ea_gid = 1; + } + kfree(ea); + } + if ((ea = hpfs_get_ea(i->i_sb, fnode, "SYMLINK", &ea_size))) { + kfree(ea); + i->i_mode = S_IFLNK | 0777; + i->i_op = &page_symlink_inode_operations; + i->i_data.a_ops = &hpfs_symlink_aops; + i->i_nlink = 1; + i->i_size = ea_size; + i->i_blocks = 1; + brelse(bh); + return; + } + if ((ea = hpfs_get_ea(i->i_sb, fnode, "MODE", &ea_size))) { + int rdev = 0; + umode_t mode = hpfs_sb(sb)->sb_mode; + if (ea_size == 2) { + mode = le16_to_cpu(*(u16*)ea); + hpfs_inode->i_ea_mode = 1; + } + kfree(ea); + i->i_mode = mode; + if (S_ISBLK(mode) || S_ISCHR(mode)) { + if ((ea = hpfs_get_ea(i->i_sb, fnode, "DEV", &ea_size))) { + if (ea_size == 4) + rdev = le32_to_cpu(*(u32*)ea); + kfree(ea); + } + } + if (S_ISBLK(mode) || S_ISCHR(mode) || S_ISFIFO(mode) || S_ISSOCK(mode)) { + brelse(bh); + i->i_nlink = 1; + i->i_size = 0; + i->i_blocks = 1; + init_special_inode(i, mode, + new_decode_dev(rdev)); + return; + } + } + } + if (fnode->dirflag) { + unsigned n_dnodes, n_subdirs; + i->i_mode |= S_IFDIR; + i->i_op = &hpfs_dir_iops; + i->i_fop = &hpfs_dir_ops; + hpfs_inode->i_parent_dir = fnode->up; + hpfs_inode->i_dno = fnode->u.external[0].disk_secno; + if (hpfs_sb(sb)->sb_chk >= 2) { + struct buffer_head *bh0; + if (hpfs_map_fnode(sb, hpfs_inode->i_parent_dir, &bh0)) brelse(bh0); + } + n_dnodes = 0; n_subdirs = 0; + hpfs_count_dnodes(i->i_sb, hpfs_inode->i_dno, &n_dnodes, &n_subdirs, NULL); + i->i_blocks = 4 * n_dnodes; + i->i_size = 2048 * n_dnodes; + i->i_nlink = 2 + n_subdirs; + } else { + i->i_mode |= S_IFREG; + if (!hpfs_inode->i_ea_mode) i->i_mode &= ~0111; + i->i_op = &hpfs_file_iops; + i->i_fop = &hpfs_file_ops; + i->i_nlink = 1; + i->i_size = fnode->file_size; + i->i_blocks = ((i->i_size + 511) >> 9) + 1; + i->i_data.a_ops = &hpfs_aops; + hpfs_i(i)->mmu_private = i->i_size; + } + brelse(bh); +} + +static void hpfs_write_inode_ea(struct inode *i, struct fnode *fnode) +{ + struct hpfs_inode_info *hpfs_inode = hpfs_i(i); + /*if (fnode->acl_size_l || fnode->acl_size_s) { + Some unknown structures like ACL may be in fnode, + we'd better not overwrite them + hpfs_error(i->i_sb, "fnode %08x has some unknown HPFS386 stuctures", i->i_ino); + } else*/ if (hpfs_sb(i->i_sb)->sb_eas >= 2) { + u32 ea; + if ((i->i_uid != hpfs_sb(i->i_sb)->sb_uid) || hpfs_inode->i_ea_uid) { + ea = cpu_to_le32(i->i_uid); + hpfs_set_ea(i, fnode, "UID", (char*)&ea, 2); + hpfs_inode->i_ea_uid = 1; + } + if ((i->i_gid != hpfs_sb(i->i_sb)->sb_gid) || hpfs_inode->i_ea_gid) { + ea = cpu_to_le32(i->i_gid); + hpfs_set_ea(i, fnode, "GID", (char *)&ea, 2); + hpfs_inode->i_ea_gid = 1; + } + if (!S_ISLNK(i->i_mode)) + if ((i->i_mode != ((hpfs_sb(i->i_sb)->sb_mode & ~(S_ISDIR(i->i_mode) ? 0 : 0111)) + | (S_ISDIR(i->i_mode) ? S_IFDIR : S_IFREG)) + && i->i_mode != ((hpfs_sb(i->i_sb)->sb_mode & ~(S_ISDIR(i->i_mode) ? 0222 : 0333)) + | (S_ISDIR(i->i_mode) ? S_IFDIR : S_IFREG))) || hpfs_inode->i_ea_mode) { + ea = cpu_to_le32(i->i_mode); + hpfs_set_ea(i, fnode, "MODE", (char *)&ea, 2); + hpfs_inode->i_ea_mode = 1; + } + if (S_ISBLK(i->i_mode) || S_ISCHR(i->i_mode)) { + ea = cpu_to_le32(new_encode_dev(i->i_rdev)); + hpfs_set_ea(i, fnode, "DEV", (char *)&ea, 4); + } + } +} + +void hpfs_write_inode(struct inode *i) +{ + struct hpfs_inode_info *hpfs_inode = hpfs_i(i); + struct inode *parent; + if (i->i_ino == hpfs_sb(i->i_sb)->sb_root) return; + if (hpfs_inode->i_rddir_off && !atomic_read(&i->i_count)) { + if (*hpfs_inode->i_rddir_off) printk("HPFS: write_inode: some position still there\n"); + kfree(hpfs_inode->i_rddir_off); + hpfs_inode->i_rddir_off = NULL; + } + down(&hpfs_inode->i_parent); + if (!i->i_nlink) { + up(&hpfs_inode->i_parent); + return; + } + parent = iget_locked(i->i_sb, hpfs_inode->i_parent_dir); + if (parent) { + hpfs_inode->i_dirty = 0; + if (parent->i_state & I_NEW) { + hpfs_init_inode(parent); + hpfs_read_inode(parent); + unlock_new_inode(parent); + } + down(&hpfs_inode->i_sem); + hpfs_write_inode_nolock(i); + up(&hpfs_inode->i_sem); + iput(parent); + } else { + mark_inode_dirty(i); + } + up(&hpfs_inode->i_parent); +} + +void hpfs_write_inode_nolock(struct inode *i) +{ + struct hpfs_inode_info *hpfs_inode = hpfs_i(i); + struct buffer_head *bh; + struct fnode *fnode; + struct quad_buffer_head qbh; + struct hpfs_dirent *de; + if (i->i_ino == hpfs_sb(i->i_sb)->sb_root) return; + if (!(fnode = hpfs_map_fnode(i->i_sb, i->i_ino, &bh))) return; + if (i->i_ino != hpfs_sb(i->i_sb)->sb_root && i->i_nlink) { + if (!(de = map_fnode_dirent(i->i_sb, i->i_ino, fnode, &qbh))) { + brelse(bh); + return; + } + } else de = NULL; + if (S_ISREG(i->i_mode)) { + fnode->file_size = i->i_size; + if (de) de->file_size = i->i_size; + } else if (S_ISDIR(i->i_mode)) { + fnode->file_size = 0; + if (de) de->file_size = 0; + } + hpfs_write_inode_ea(i, fnode); + if (de) { + de->write_date = gmt_to_local(i->i_sb, i->i_mtime.tv_sec); + de->read_date = gmt_to_local(i->i_sb, i->i_atime.tv_sec); + de->creation_date = gmt_to_local(i->i_sb, i->i_ctime.tv_sec); + de->read_only = !(i->i_mode & 0222); + de->ea_size = hpfs_inode->i_ea_size; + hpfs_mark_4buffers_dirty(&qbh); + hpfs_brelse4(&qbh); + } + if (S_ISDIR(i->i_mode)) { + if ((de = map_dirent(i, hpfs_inode->i_dno, "\001\001", 2, NULL, &qbh))) { + de->write_date = gmt_to_local(i->i_sb, i->i_mtime.tv_sec); + de->read_date = gmt_to_local(i->i_sb, i->i_atime.tv_sec); + de->creation_date = gmt_to_local(i->i_sb, i->i_ctime.tv_sec); + de->read_only = !(i->i_mode & 0222); + de->ea_size = /*hpfs_inode->i_ea_size*/0; + de->file_size = 0; + hpfs_mark_4buffers_dirty(&qbh); + hpfs_brelse4(&qbh); + } else hpfs_error(i->i_sb, "directory %08x doesn't have '.' entry", i->i_ino); + } + mark_buffer_dirty(bh); + brelse(bh); +} + +int hpfs_notify_change(struct dentry *dentry, struct iattr *attr) +{ + struct inode *inode = dentry->d_inode; + int error=0; + lock_kernel(); + if ( ((attr->ia_valid & ATTR_SIZE) && attr->ia_size > inode->i_size) || + (hpfs_sb(inode->i_sb)->sb_root == inode->i_ino) ) { + error = -EINVAL; + } else if ((error = inode_change_ok(inode, attr))) { + } else if ((error = inode_setattr(inode, attr))) { + } else { + hpfs_write_inode(inode); + } + unlock_kernel(); + return error; +} + +void hpfs_write_if_changed(struct inode *inode) +{ + struct hpfs_inode_info *hpfs_inode = hpfs_i(inode); + + if (hpfs_inode->i_dirty) + hpfs_write_inode(inode); +} + +void hpfs_delete_inode(struct inode *inode) +{ + lock_kernel(); + hpfs_remove_fnode(inode->i_sb, inode->i_ino); + unlock_kernel(); + clear_inode(inode); +} diff --git a/fs/hpfs/map.c b/fs/hpfs/map.c new file mode 100644 index 00000000000..0fecdac22e4 --- /dev/null +++ b/fs/hpfs/map.c @@ -0,0 +1,275 @@ +/* + * linux/fs/hpfs/map.c + * + * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999 + * + * mapping structures to memory with some minimal checks + */ + +#include "hpfs_fn.h" + +unsigned *hpfs_map_dnode_bitmap(struct super_block *s, struct quad_buffer_head *qbh) +{ + return hpfs_map_4sectors(s, hpfs_sb(s)->sb_dmap, qbh, 0); +} + +unsigned int *hpfs_map_bitmap(struct super_block *s, unsigned bmp_block, + struct quad_buffer_head *qbh, char *id) +{ + secno sec; + if (hpfs_sb(s)->sb_chk) if (bmp_block * 16384 > hpfs_sb(s)->sb_fs_size) { + hpfs_error(s, "hpfs_map_bitmap called with bad parameter: %08x at %s", bmp_block, id); + return NULL; + } + sec = hpfs_sb(s)->sb_bmp_dir[bmp_block]; + if (!sec || sec > hpfs_sb(s)->sb_fs_size-4) { + hpfs_error(s, "invalid bitmap block pointer %08x -> %08x at %s", bmp_block, sec, id); + return NULL; + } + return hpfs_map_4sectors(s, sec, qbh, 4); +} + +/* + * Load first code page into kernel memory, return pointer to 256-byte array, + * first 128 bytes are uppercasing table for chars 128-255, next 128 bytes are + * lowercasing table + */ + +char *hpfs_load_code_page(struct super_block *s, secno cps) +{ + struct buffer_head *bh; + secno cpds; + unsigned cpi; + unsigned char *ptr; + unsigned char *cp_table; + int i; + struct code_page_data *cpd; + struct code_page_directory *cp = hpfs_map_sector(s, cps, &bh, 0); + if (!cp) return NULL; + if (cp->magic != CP_DIR_MAGIC) { + printk("HPFS: Code page directory magic doesn't match (magic = %08x)\n", cp->magic); + brelse(bh); + return NULL; + } + if (!cp->n_code_pages) { + printk("HPFS: n_code_pages == 0\n"); + brelse(bh); + return NULL; + } + cpds = cp->array[0].code_page_data; + cpi = cp->array[0].index; + brelse(bh); + + if (cpi >= 3) { + printk("HPFS: Code page index out of array\n"); + return NULL; + } + + if (!(cpd = hpfs_map_sector(s, cpds, &bh, 0))) return NULL; + if ((unsigned)cpd->offs[cpi] > 0x178) { + printk("HPFS: Code page index out of sector\n"); + brelse(bh); + return NULL; + } + ptr = (char *)cpd + cpd->offs[cpi] + 6; + if (!(cp_table = kmalloc(256, GFP_KERNEL))) { + printk("HPFS: out of memory for code page table\n"); + brelse(bh); + return NULL; + } + memcpy(cp_table, ptr, 128); + brelse(bh); + + /* Try to build lowercasing table from uppercasing one */ + + for (i=128; i<256; i++) cp_table[i]=i; + for (i=128; i<256; i++) if (cp_table[i-128]!=i && cp_table[i-128]>=128) + cp_table[cp_table[i-128]] = i; + + return cp_table; +} + +secno *hpfs_load_bitmap_directory(struct super_block *s, secno bmp) +{ + struct buffer_head *bh; + int n = (hpfs_sb(s)->sb_fs_size + 0x200000 - 1) >> 21; + int i; + secno *b; + if (!(b = kmalloc(n * 512, GFP_KERNEL))) { + printk("HPFS: can't allocate memory for bitmap directory\n"); + return NULL; + } + for (i=0;i<n;i++) { + secno *d = hpfs_map_sector(s, bmp+i, &bh, n - i - 1); + if (!d) { + kfree(b); + return NULL; + } + memcpy((char *)b + 512 * i, d, 512); + brelse(bh); + } + return b; +} + +/* + * Load fnode to memory + */ + +struct fnode *hpfs_map_fnode(struct super_block *s, ino_t ino, struct buffer_head **bhp) +{ + struct fnode *fnode; + if (hpfs_sb(s)->sb_chk) if (hpfs_chk_sectors(s, ino, 1, "fnode")) { + return NULL; + } + if ((fnode = hpfs_map_sector(s, ino, bhp, FNODE_RD_AHEAD))) { + if (hpfs_sb(s)->sb_chk) { + struct extended_attribute *ea; + struct extended_attribute *ea_end; + if (fnode->magic != FNODE_MAGIC) { + hpfs_error(s, "bad magic on fnode %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->acl_size_s + 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 (hpfs_sb(s)->sb_chk) if (hpfs_chk_sectors(s, ano, 1, "anode")) return NULL; + if ((anode = hpfs_map_sector(s, ano, bhp, ANODE_RD_AHEAD))) + if (hpfs_sb(s)->sb_chk) { + if (anode->magic != ANODE_MAGIC || anode->self != ano) { + hpfs_error(s, "bad magic on anode %08x", ano); + goto bail; + } + if ((unsigned)anode->btree.n_used_nodes + (unsigned)anode->btree.n_free_nodes != + (anode->btree.internal ? 60 : 40)) { + hpfs_error(s, "bad number of nodes in anode %08x", ano); + goto bail; + } + if (anode->btree.first_free != + 8 + anode->btree.n_used_nodes * (anode->btree.internal ? 8 : 12)) { + hpfs_error(s, "bad first_free pointer in anode %08x", ano); + goto bail; + } + } + return anode; + bail: + brelse(*bhp); + return NULL; +} + +/* + * Load dnode to memory and do some checks + */ + +struct dnode *hpfs_map_dnode(struct super_block *s, unsigned secno, + struct quad_buffer_head *qbh) +{ + struct dnode *dnode; + if (hpfs_sb(s)->sb_chk) { + if (hpfs_chk_sectors(s, secno, 4, "dnode")) return NULL; + if (secno & 3) { + hpfs_error(s, "dnode %08x not byte-aligned", secno); + return NULL; + } + } + if ((dnode = hpfs_map_4sectors(s, secno, qbh, DNODE_RD_AHEAD))) + if (hpfs_sb(s)->sb_chk) { + unsigned p, pp = 0; + unsigned char *d = (char *)dnode; + int b = 0; + if (dnode->magic != DNODE_MAGIC) { + hpfs_error(s, "bad magic on dnode %08x", secno); + goto bail; + } + if (dnode->self != secno) + hpfs_error(s, "bad self pointer on dnode %08x self = %08x", secno, dnode->self); + /* Check dirents - bad dirents would cause infinite + loops or shooting to memory */ + if (dnode->first_free > 2048/* || dnode->first_free < 84*/) { + hpfs_error(s, "dnode %08x has first_free == %08x", secno, dnode->first_free); + goto bail; + } + for (p = 20; p < dnode->first_free; p += d[p] + (d[p+1] << 8)) { + struct hpfs_dirent *de = (struct hpfs_dirent *)((char *)dnode + p); + if (de->length > 292 || (de->length < 32) || (de->length & 3) || p + de->length > 2048) { + hpfs_error(s, "bad dirent size in dnode %08x, dirent %03x, last %03x", secno, p, pp); + goto bail; + } + if (((31 + de->namelen + de->down*4 + 3) & ~3) != de->length) { + if (((31 + de->namelen + de->down*4 + 3) & ~3) < de->length && s->s_flags & MS_RDONLY) goto ok; + hpfs_error(s, "namelen does not match dirent size in dnode %08x, dirent %03x, last %03x", secno, p, pp); + goto bail; + } + ok: + if (hpfs_sb(s)->sb_chk >= 2) b |= 1 << de->down; + if (de->down) if (de_down_pointer(de) < 0x10) { + hpfs_error(s, "bad down pointer in dnode %08x, dirent %03x, last %03x", secno, p, pp); + goto bail; + } + pp = p; + + } + if (p != dnode->first_free) { + hpfs_error(s, "size on last dirent does not match first_free; dnode %08x", secno); + goto bail; + } + if (d[pp + 30] != 1 || d[pp + 31] != 255) { + hpfs_error(s, "dnode %08x does not end with \\377 entry", secno); + goto bail; + } + if (b == 3) printk("HPFS: warning: unbalanced dnode tree, dnode %08x; see hpfs.txt 4 more info\n", secno); + } + return dnode; + bail: + hpfs_brelse4(qbh); + return NULL; +} + +dnode_secno hpfs_fnode_dno(struct super_block *s, ino_t ino) +{ + struct buffer_head *bh; + struct fnode *fnode; + dnode_secno dno; + + fnode = hpfs_map_fnode(s, ino, &bh); + if (!fnode) + return 0; + + dno = fnode->u.external[0].disk_secno; + brelse(bh); + return dno; +} diff --git a/fs/hpfs/name.c b/fs/hpfs/name.c new file mode 100644 index 00000000000..1f4a964384e --- /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 "hpfs_fn.h" + +static char *text_postfix[]={ +".ASM", ".BAS", ".BAT", ".C", ".CC", ".CFG", ".CMD", ".CON", ".CPP", ".DEF", +".DOC", ".DPR", ".ERX", ".H", ".HPP", ".HTM", ".HTML", ".JAVA", ".LOG", ".PAS", +".RC", ".TEX", ".TXT", ".Y", ""}; + +static char *text_prefix[]={ +"AUTOEXEC.", "CHANGES", "COPYING", "CONFIG.", "CREDITS", "FAQ", "FILE_ID.DIZ", +"MAKEFILE", "READ.ME", "README", "TERMCAP", ""}; + +void hpfs_decide_conv(struct inode *inode, unsigned char *name, unsigned len) +{ + struct hpfs_inode_info *hpfs_inode = hpfs_i(inode); + int i; + if (hpfs_inode->i_conv != CONV_AUTO) return; + for (i = 0; *text_postfix[i]; i++) { + int l = strlen(text_postfix[i]); + if (l <= len) + if (!hpfs_compare_names(inode->i_sb, text_postfix[i], l, name + len - l, l, 0)) + goto text; + } + for (i = 0; *text_prefix[i]; i++) { + int l = strlen(text_prefix[i]); + if (l <= len) + if (!hpfs_compare_names(inode->i_sb, text_prefix[i], l, name, l, 0)) + goto text; + } + hpfs_inode->i_conv = CONV_BINARY; + return; + text: + hpfs_inode->i_conv = CONV_TEXT; + return; +} + +static inline int not_allowed_char(unsigned char c) +{ + return c<' ' || c=='"' || c=='*' || c=='/' || c==':' || c=='<' || + c=='>' || c=='?' || c=='\\' || c=='|'; +} + +static inline int no_dos_char(unsigned char c) +{ /* Characters that are allowed in HPFS but not in DOS */ + return c=='+' || c==',' || c==';' || c=='=' || c=='[' || c==']'; +} + +static inline unsigned char upcase(unsigned char *dir, unsigned char a) +{ + if (a<128 || a==255) return a>='a' && a<='z' ? a - 0x20 : a; + if (!dir) return a; + return dir[a-128]; +} + +unsigned char hpfs_upcase(unsigned char *dir, unsigned char a) +{ + return upcase(dir, a); +} + +static inline unsigned char locase(unsigned char *dir, unsigned char a) +{ + if (a<128 || a==255) return a>='A' && a<='Z' ? a + 0x20 : a; + if (!dir) return a; + return dir[a]; +} + +int hpfs_chk_name(unsigned char *name, unsigned *len) +{ + int i; + if (*len > 254) return -ENAMETOOLONG; + hpfs_adjust_length(name, len); + if (!*len) return -EINVAL; + for (i = 0; i < *len; i++) if (not_allowed_char(name[i])) return -EINVAL; + if (*len == 1) if (name[0] == '.') return -EINVAL; + if (*len == 2) if (name[0] == '.' && name[1] == '.') return -EINVAL; + return 0; +} + +char *hpfs_translate_name(struct super_block *s, unsigned char *from, + unsigned len, int lc, int lng) +{ + char *to; + int i; + if (hpfs_sb(s)->sb_chk >= 2) if (hpfs_is_name_long(from, len) != lng) { + printk("HPFS: Long name flag mismatch - name "); + for (i=0; i<len; i++) printk("%c", from[i]); + printk(" misidentified as %s.\n", lng ? "short" : "long"); + printk("HPFS: It's nothing serious. It could happen because of bug in OS/2.\nHPFS: Set checks=normal to disable this message.\n"); + } + if (!lc) return from; + if (!(to = kmalloc(len, GFP_KERNEL))) { + printk("HPFS: can't allocate memory for name conversion buffer\n"); + return from; + } + for (i = 0; i < len; i++) to[i] = locase(hpfs_sb(s)->sb_cp_table,from[i]); + return to; +} + +int hpfs_compare_names(struct super_block *s, unsigned char *n1, unsigned l1, + unsigned char *n2, unsigned l2, int last) +{ + unsigned l = l1 < l2 ? l1 : l2; + unsigned i; + if (last) return -1; + for (i = 0; i < l; i++) { + unsigned char c1 = upcase(hpfs_sb(s)->sb_cp_table,n1[i]); + unsigned char c2 = upcase(hpfs_sb(s)->sb_cp_table,n2[i]); + if (c1 < c2) return -1; + if (c1 > c2) return 1; + } + if (l1 < l2) return -1; + if (l1 > l2) return 1; + return 0; +} + +int hpfs_is_name_long(unsigned char *name, unsigned len) +{ + int i,j; + for (i = 0; i < len && name[i] != '.'; i++) + if (no_dos_char(name[i])) return 1; + if (!i || i > 8) return 1; + if (i == len) return 0; + for (j = i + 1; j < len; j++) + if (name[j] == '.' || no_dos_char(name[i])) return 1; + return j - i > 4; +} + +/* OS/2 clears dots and spaces at the end of file name, so we have to */ + +void hpfs_adjust_length(unsigned char *name, unsigned *len) +{ + if (!*len) return; + if (*len == 1 && name[0] == '.') return; + if (*len == 2 && name[0] == '.' && name[1] == '.') return; + while (*len && (name[*len - 1] == '.' || name[*len - 1] == ' ')) + (*len)--; +} diff --git a/fs/hpfs/namei.c b/fs/hpfs/namei.c new file mode 100644 index 00000000000..8ff8fc433fc --- /dev/null +++ b/fs/hpfs/namei.c @@ -0,0 +1,673 @@ +/* + * linux/fs/hpfs/namei.c + * + * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999 + * + * adding & removing files & directories + */ + +#include "hpfs_fn.h" + +static int hpfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) +{ + const char *name = dentry->d_name.name; + unsigned len = dentry->d_name.len; + struct quad_buffer_head qbh0; + struct buffer_head *bh; + struct hpfs_dirent *de; + struct fnode *fnode; + struct dnode *dnode; + struct inode *result; + fnode_secno fno; + dnode_secno dno; + int r; + struct hpfs_dirent dee; + int err; + if ((err = hpfs_chk_name((char *)name, &len))) return err==-ENOENT ? -EINVAL : err; + lock_kernel(); + err = -ENOSPC; + fnode = hpfs_alloc_fnode(dir->i_sb, hpfs_i(dir)->i_dno, &fno, &bh); + if (!fnode) + goto bail; + dnode = hpfs_alloc_dnode(dir->i_sb, fno, &dno, &qbh0, 1); + if (!dnode) + goto bail1; + memset(&dee, 0, sizeof dee); + dee.directory = 1; + if (!(mode & 0222)) dee.read_only = 1; + /*dee.archive = 0;*/ + dee.hidden = name[0] == '.'; + dee.fnode = fno; + dee.creation_date = dee.write_date = dee.read_date = gmt_to_local(dir->i_sb, get_seconds()); + result = new_inode(dir->i_sb); + if (!result) + goto bail2; + hpfs_init_inode(result); + result->i_ino = fno; + hpfs_i(result)->i_parent_dir = dir->i_ino; + hpfs_i(result)->i_dno = dno; + result->i_ctime.tv_sec = result->i_mtime.tv_sec = result->i_atime.tv_sec = local_to_gmt(dir->i_sb, dee.creation_date); + result->i_ctime.tv_nsec = 0; + result->i_mtime.tv_nsec = 0; + result->i_atime.tv_nsec = 0; + hpfs_i(result)->i_ea_size = 0; + result->i_mode |= S_IFDIR; + result->i_op = &hpfs_dir_iops; + result->i_fop = &hpfs_dir_ops; + result->i_blocks = 4; + result->i_size = 2048; + result->i_nlink = 2; + if (dee.read_only) + result->i_mode &= ~0222; + + down(&hpfs_i(dir)->i_sem); + r = hpfs_add_dirent(dir, (char *)name, len, &dee, 0); + if (r == 1) + goto bail3; + if (r == -1) { + err = -EEXIST; + goto bail3; + } + fnode->len = len; + memcpy(fnode->name, name, len > 15 ? 15 : len); + fnode->up = dir->i_ino; + fnode->dirflag = 1; + fnode->btree.n_free_nodes = 7; + fnode->btree.n_used_nodes = 1; + fnode->btree.first_free = 0x14; + fnode->u.external[0].disk_secno = dno; + fnode->u.external[0].file_secno = -1; + dnode->root_dnode = 1; + dnode->up = fno; + de = hpfs_add_de(dir->i_sb, dnode, "\001\001", 2, 0); + de->creation_date = de->write_date = de->read_date = gmt_to_local(dir->i_sb, get_seconds()); + if (!(mode & 0222)) de->read_only = 1; + de->first = de->directory = 1; + /*de->hidden = de->system = 0;*/ + de->fnode = fno; + mark_buffer_dirty(bh); + brelse(bh); + hpfs_mark_4buffers_dirty(&qbh0); + hpfs_brelse4(&qbh0); + dir->i_nlink++; + insert_inode_hash(result); + + if (result->i_uid != current->fsuid || + result->i_gid != current->fsgid || + result->i_mode != (mode | S_IFDIR)) { + result->i_uid = current->fsuid; + result->i_gid = current->fsgid; + result->i_mode = mode | S_IFDIR; + hpfs_write_inode_nolock(result); + } + d_instantiate(dentry, result); + up(&hpfs_i(dir)->i_sem); + unlock_kernel(); + return 0; +bail3: + up(&hpfs_i(dir)->i_sem); + iput(result); +bail2: + hpfs_brelse4(&qbh0); + hpfs_free_dnode(dir->i_sb, dno); +bail1: + brelse(bh); + hpfs_free_sectors(dir->i_sb, fno, 1); +bail: + unlock_kernel(); + return err; +} + +static int hpfs_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *nd) +{ + const char *name = dentry->d_name.name; + unsigned len = dentry->d_name.len; + struct inode *result = NULL; + struct buffer_head *bh; + struct fnode *fnode; + fnode_secno fno; + int r; + struct hpfs_dirent dee; + int err; + if ((err = hpfs_chk_name((char *)name, &len))) + return err==-ENOENT ? -EINVAL : err; + lock_kernel(); + err = -ENOSPC; + fnode = hpfs_alloc_fnode(dir->i_sb, hpfs_i(dir)->i_dno, &fno, &bh); + if (!fnode) + goto bail; + memset(&dee, 0, sizeof dee); + if (!(mode & 0222)) dee.read_only = 1; + dee.archive = 1; + dee.hidden = name[0] == '.'; + dee.fnode = fno; + dee.creation_date = dee.write_date = dee.read_date = gmt_to_local(dir->i_sb, get_seconds()); + + result = new_inode(dir->i_sb); + if (!result) + goto bail1; + + hpfs_init_inode(result); + result->i_ino = fno; + result->i_mode |= S_IFREG; + result->i_mode &= ~0111; + result->i_op = &hpfs_file_iops; + result->i_fop = &hpfs_file_ops; + result->i_nlink = 1; + hpfs_decide_conv(result, (char *)name, len); + hpfs_i(result)->i_parent_dir = dir->i_ino; + result->i_ctime.tv_sec = result->i_mtime.tv_sec = result->i_atime.tv_sec = local_to_gmt(dir->i_sb, dee.creation_date); + result->i_ctime.tv_nsec = 0; + result->i_mtime.tv_nsec = 0; + result->i_atime.tv_nsec = 0; + hpfs_i(result)->i_ea_size = 0; + if (dee.read_only) + result->i_mode &= ~0222; + result->i_blocks = 1; + result->i_size = 0; + result->i_data.a_ops = &hpfs_aops; + hpfs_i(result)->mmu_private = 0; + + down(&hpfs_i(dir)->i_sem); + r = hpfs_add_dirent(dir, (char *)name, len, &dee, 0); + if (r == 1) + goto bail2; + if (r == -1) { + err = -EEXIST; + goto bail2; + } + fnode->len = len; + memcpy(fnode->name, name, len > 15 ? 15 : len); + fnode->up = dir->i_ino; + mark_buffer_dirty(bh); + brelse(bh); + + insert_inode_hash(result); + + if (result->i_uid != current->fsuid || + result->i_gid != current->fsgid || + result->i_mode != (mode | S_IFREG)) { + result->i_uid = current->fsuid; + result->i_gid = current->fsgid; + result->i_mode = mode | S_IFREG; + hpfs_write_inode_nolock(result); + } + d_instantiate(dentry, result); + up(&hpfs_i(dir)->i_sem); + unlock_kernel(); + return 0; + +bail2: + up(&hpfs_i(dir)->i_sem); + iput(result); +bail1: + brelse(bh); + hpfs_free_sectors(dir->i_sb, fno, 1); +bail: + unlock_kernel(); + return err; +} + +static int hpfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev) +{ + const char *name = dentry->d_name.name; + unsigned len = dentry->d_name.len; + struct buffer_head *bh; + struct fnode *fnode; + fnode_secno fno; + int r; + struct hpfs_dirent dee; + struct inode *result = NULL; + int err; + if ((err = hpfs_chk_name((char *)name, &len))) return err==-ENOENT ? -EINVAL : err; + if (hpfs_sb(dir->i_sb)->sb_eas < 2) return -EPERM; + if (!new_valid_dev(rdev)) + return -EINVAL; + lock_kernel(); + err = -ENOSPC; + fnode = hpfs_alloc_fnode(dir->i_sb, hpfs_i(dir)->i_dno, &fno, &bh); + if (!fnode) + goto bail; + memset(&dee, 0, sizeof dee); + if (!(mode & 0222)) dee.read_only = 1; + dee.archive = 1; + dee.hidden = name[0] == '.'; + dee.fnode = fno; + dee.creation_date = dee.write_date = dee.read_date = gmt_to_local(dir->i_sb, get_seconds()); + + result = new_inode(dir->i_sb); + if (!result) + goto bail1; + + hpfs_init_inode(result); + result->i_ino = fno; + hpfs_i(result)->i_parent_dir = dir->i_ino; + result->i_ctime.tv_sec = result->i_mtime.tv_sec = result->i_atime.tv_sec = local_to_gmt(dir->i_sb, dee.creation_date); + result->i_ctime.tv_nsec = 0; + result->i_mtime.tv_nsec = 0; + result->i_atime.tv_nsec = 0; + hpfs_i(result)->i_ea_size = 0; + result->i_uid = current->fsuid; + result->i_gid = current->fsgid; + result->i_nlink = 1; + result->i_size = 0; + result->i_blocks = 1; + init_special_inode(result, mode, rdev); + + down(&hpfs_i(dir)->i_sem); + r = hpfs_add_dirent(dir, (char *)name, len, &dee, 0); + if (r == 1) + goto bail2; + if (r == -1) { + err = -EEXIST; + goto bail2; + } + fnode->len = len; + memcpy(fnode->name, name, len > 15 ? 15 : len); + fnode->up = dir->i_ino; + mark_buffer_dirty(bh); + + insert_inode_hash(result); + + hpfs_write_inode_nolock(result); + d_instantiate(dentry, result); + up(&hpfs_i(dir)->i_sem); + brelse(bh); + unlock_kernel(); + return 0; +bail2: + up(&hpfs_i(dir)->i_sem); + iput(result); +bail1: + brelse(bh); + hpfs_free_sectors(dir->i_sb, fno, 1); +bail: + unlock_kernel(); + return err; +} + +static int hpfs_symlink(struct inode *dir, struct dentry *dentry, const char *symlink) +{ + const char *name = dentry->d_name.name; + unsigned len = dentry->d_name.len; + struct buffer_head *bh; + struct fnode *fnode; + fnode_secno fno; + int r; + struct hpfs_dirent dee; + struct inode *result; + int err; + if ((err = hpfs_chk_name((char *)name, &len))) return err==-ENOENT ? -EINVAL : err; + lock_kernel(); + if (hpfs_sb(dir->i_sb)->sb_eas < 2) { + unlock_kernel(); + return -EPERM; + } + err = -ENOSPC; + fnode = hpfs_alloc_fnode(dir->i_sb, hpfs_i(dir)->i_dno, &fno, &bh); + if (!fnode) + goto bail; + memset(&dee, 0, sizeof dee); + dee.archive = 1; + dee.hidden = name[0] == '.'; + dee.fnode = fno; + dee.creation_date = dee.write_date = dee.read_date = gmt_to_local(dir->i_sb, get_seconds()); + + result = new_inode(dir->i_sb); + if (!result) + goto bail1; + result->i_ino = fno; + hpfs_init_inode(result); + hpfs_i(result)->i_parent_dir = dir->i_ino; + result->i_ctime.tv_sec = result->i_mtime.tv_sec = result->i_atime.tv_sec = local_to_gmt(dir->i_sb, dee.creation_date); + result->i_ctime.tv_nsec = 0; + result->i_mtime.tv_nsec = 0; + result->i_atime.tv_nsec = 0; + hpfs_i(result)->i_ea_size = 0; + result->i_mode = S_IFLNK | 0777; + result->i_uid = current->fsuid; + result->i_gid = current->fsgid; + result->i_blocks = 1; + result->i_nlink = 1; + result->i_size = strlen(symlink); + result->i_op = &page_symlink_inode_operations; + result->i_data.a_ops = &hpfs_symlink_aops; + + down(&hpfs_i(dir)->i_sem); + r = hpfs_add_dirent(dir, (char *)name, len, &dee, 0); + if (r == 1) + goto bail2; + if (r == -1) { + err = -EEXIST; + goto bail2; + } + fnode->len = len; + memcpy(fnode->name, name, len > 15 ? 15 : len); + fnode->up = dir->i_ino; + hpfs_set_ea(result, fnode, "SYMLINK", (char *)symlink, strlen(symlink)); + mark_buffer_dirty(bh); + brelse(bh); + + insert_inode_hash(result); + + hpfs_write_inode_nolock(result); + d_instantiate(dentry, result); + up(&hpfs_i(dir)->i_sem); + unlock_kernel(); + return 0; +bail2: + up(&hpfs_i(dir)->i_sem); + iput(result); +bail1: + brelse(bh); + hpfs_free_sectors(dir->i_sb, fno, 1); +bail: + unlock_kernel(); + return err; +} + +static int hpfs_unlink(struct inode *dir, struct dentry *dentry) +{ + const char *name = dentry->d_name.name; + unsigned len = dentry->d_name.len; + struct quad_buffer_head qbh; + struct hpfs_dirent *de; + struct inode *inode = dentry->d_inode; + dnode_secno dno; + fnode_secno fno; + int r; + int rep = 0; + int err; + + lock_kernel(); + hpfs_adjust_length((char *)name, &len); +again: + down(&hpfs_i(inode)->i_parent); + down(&hpfs_i(dir)->i_sem); + err = -ENOENT; + de = map_dirent(dir, hpfs_i(dir)->i_dno, (char *)name, len, &dno, &qbh); + if (!de) + goto out; + + err = -EPERM; + if (de->first) + goto out1; + + err = -EISDIR; + if (de->directory) + goto out1; + + fno = de->fnode; + r = hpfs_remove_dirent(dir, dno, de, &qbh, 1); + switch (r) { + case 1: + hpfs_error(dir->i_sb, "there was error when removing dirent"); + err = -EFSERROR; + break; + case 2: /* no space for deleting, try to truncate file */ + + err = -ENOSPC; + if (rep++) + break; + + up(&hpfs_i(dir)->i_sem); + up(&hpfs_i(inode)->i_parent); + d_drop(dentry); + spin_lock(&dentry->d_lock); + if (atomic_read(&dentry->d_count) > 1 || + permission(inode, MAY_WRITE, NULL) || + !S_ISREG(inode->i_mode) || + get_write_access(inode)) { + spin_unlock(&dentry->d_lock); + d_rehash(dentry); + } else { + struct iattr newattrs; + spin_unlock(&dentry->d_lock); + /*printk("HPFS: truncating file before delete.\n");*/ + newattrs.ia_size = 0; + newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME; + err = notify_change(dentry, &newattrs); + put_write_access(inode); + if (!err) + goto again; + } + unlock_kernel(); + return -ENOSPC; + default: + inode->i_nlink--; + err = 0; + } + goto out; + +out1: + hpfs_brelse4(&qbh); +out: + up(&hpfs_i(dir)->i_sem); + up(&hpfs_i(inode)->i_parent); + unlock_kernel(); + return err; +} + +static int hpfs_rmdir(struct inode *dir, struct dentry *dentry) +{ + const char *name = dentry->d_name.name; + unsigned len = dentry->d_name.len; + struct quad_buffer_head qbh; + struct hpfs_dirent *de; + struct inode *inode = dentry->d_inode; + dnode_secno dno; + fnode_secno fno; + int n_items = 0; + int err; + int r; + + hpfs_adjust_length((char *)name, &len); + lock_kernel(); + down(&hpfs_i(inode)->i_parent); + down(&hpfs_i(dir)->i_sem); + err = -ENOENT; + de = map_dirent(dir, hpfs_i(dir)->i_dno, (char *)name, len, &dno, &qbh); + if (!de) + goto out; + + err = -EPERM; + if (de->first) + goto out1; + + err = -ENOTDIR; + if (!de->directory) + goto out1; + + hpfs_count_dnodes(dir->i_sb, hpfs_i(inode)->i_dno, NULL, NULL, &n_items); + err = -ENOTEMPTY; + if (n_items) + goto out1; + + fno = de->fnode; + r = hpfs_remove_dirent(dir, dno, de, &qbh, 1); + switch (r) { + case 1: + hpfs_error(dir->i_sb, "there was error when removing dirent"); + err = -EFSERROR; + break; + case 2: + err = -ENOSPC; + break; + default: + dir->i_nlink--; + inode->i_nlink = 0; + err = 0; + } + goto out; +out1: + hpfs_brelse4(&qbh); +out: + up(&hpfs_i(dir)->i_sem); + up(&hpfs_i(inode)->i_parent); + unlock_kernel(); + return err; +} + +static int hpfs_symlink_readpage(struct file *file, struct page *page) +{ + char *link = kmap(page); + struct inode *i = page->mapping->host; + struct fnode *fnode; + struct buffer_head *bh; + int err; + + err = -EIO; + lock_kernel(); + if (!(fnode = hpfs_map_fnode(i->i_sb, i->i_ino, &bh))) + goto fail; + err = hpfs_read_ea(i->i_sb, fnode, "SYMLINK", link, PAGE_SIZE); + brelse(bh); + if (err) + goto fail; + unlock_kernel(); + SetPageUptodate(page); + kunmap(page); + unlock_page(page); + return 0; + +fail: + unlock_kernel(); + SetPageError(page); + kunmap(page); + unlock_page(page); + return err; +} + +struct address_space_operations hpfs_symlink_aops = { + .readpage = hpfs_symlink_readpage +}; + +static int hpfs_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) +{ + char *old_name = (char *)old_dentry->d_name.name; + int old_len = old_dentry->d_name.len; + char *new_name = (char *)new_dentry->d_name.name; + int new_len = new_dentry->d_name.len; + struct inode *i = old_dentry->d_inode; + struct inode *new_inode = new_dentry->d_inode; + struct quad_buffer_head qbh, qbh1; + struct hpfs_dirent *dep, *nde; + struct hpfs_dirent de; + dnode_secno dno; + int r; + struct buffer_head *bh; + struct fnode *fnode; + int err; + if ((err = hpfs_chk_name((char *)new_name, &new_len))) return err; + err = 0; + hpfs_adjust_length((char *)old_name, &old_len); + + lock_kernel(); + /* order doesn't matter, due to VFS exclusion */ + down(&hpfs_i(i)->i_parent); + if (new_inode) + down(&hpfs_i(new_inode)->i_parent); + down(&hpfs_i(old_dir)->i_sem); + if (new_dir != old_dir) + down(&hpfs_i(new_dir)->i_sem); + + /* Erm? Moving over the empty non-busy directory is perfectly legal */ + if (new_inode && S_ISDIR(new_inode->i_mode)) { + err = -EINVAL; + goto end1; + } + + if (!(dep = map_dirent(old_dir, hpfs_i(old_dir)->i_dno, (char *)old_name, old_len, &dno, &qbh))) { + hpfs_error(i->i_sb, "lookup succeeded but map dirent failed"); + err = -ENOENT; + goto end1; + } + copy_de(&de, dep); + de.hidden = new_name[0] == '.'; + + if (new_inode) { + int r; + if ((r = hpfs_remove_dirent(old_dir, dno, dep, &qbh, 1)) != 2) { + if ((nde = map_dirent(new_dir, hpfs_i(new_dir)->i_dno, (char *)new_name, new_len, NULL, &qbh1))) { + 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, hpfs_i(old_dir)->i_dno, (char *)old_name, old_len, &dno, &qbh))) { + hpfs_unlock_creation(i->i_sb); + hpfs_error(i->i_sb, "lookup succeeded but map dirent failed at #2"); + err = -ENOENT; + goto end1; + } + + if ((r = hpfs_remove_dirent(old_dir, dno, dep, &qbh, 0))) { + hpfs_unlock_creation(i->i_sb); + hpfs_error(i->i_sb, "hpfs_rename: could not remove dirent"); + err = r == 2 ? -ENOSPC : -EFSERROR; + goto end1; + } + hpfs_unlock_creation(i->i_sb); + + end: + hpfs_i(i)->i_parent_dir = new_dir->i_ino; + if (S_ISDIR(i->i_mode)) { + 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); + brelse(bh); + } + hpfs_i(i)->i_conv = hpfs_sb(i->i_sb)->sb_conv; + hpfs_decide_conv(i, (char *)new_name, new_len); +end1: + if (old_dir != new_dir) + up(&hpfs_i(new_dir)->i_sem); + up(&hpfs_i(old_dir)->i_sem); + up(&hpfs_i(i)->i_parent); + if (new_inode) + up(&hpfs_i(new_inode)->i_parent); + unlock_kernel(); + return err; +} + +struct inode_operations hpfs_dir_iops = +{ + .create = hpfs_create, + .lookup = hpfs_lookup, + .unlink = hpfs_unlink, + .symlink = hpfs_symlink, + .mkdir = hpfs_mkdir, + .rmdir = hpfs_rmdir, + .mknod = hpfs_mknod, + .rename = hpfs_rename, + .setattr = hpfs_notify_change, +}; diff --git a/fs/hpfs/super.c b/fs/hpfs/super.c new file mode 100644 index 00000000000..8eefa6366db --- /dev/null +++ b/fs/hpfs/super.c @@ -0,0 +1,701 @@ +/* + * linux/fs/hpfs/super.c + * + * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999 + * + * mounting, unmounting, error handling + */ + +#include "hpfs_fn.h" +#include <linux/module.h> +#include <linux/parser.h> +#include <linux/init.h> +#include <linux/statfs.h> + +/* Mark the filesystem dirty, so that chkdsk checks it when os/2 booted */ + +static void mark_dirty(struct super_block *s) +{ + if (hpfs_sb(s)->sb_chkdsk && !(s->s_flags & MS_RDONLY)) { + struct buffer_head *bh; + struct hpfs_spare_block *sb; + if ((sb = hpfs_map_sector(s, 17, &bh, 0))) { + sb->dirty = 1; + sb->old_wrote = 0; + mark_buffer_dirty(bh); + brelse(bh); + } + } +} + +/* Mark the filesystem clean (mark it dirty for chkdsk if chkdsk==2 or if there + were errors) */ + +static void unmark_dirty(struct super_block *s) +{ + struct buffer_head *bh; + struct hpfs_spare_block *sb; + if (s->s_flags & MS_RDONLY) return; + if ((sb = hpfs_map_sector(s, 17, &bh, 0))) { + sb->dirty = hpfs_sb(s)->sb_chkdsk > 1 - hpfs_sb(s)->sb_was_error; + sb->old_wrote = hpfs_sb(s)->sb_chkdsk >= 2 && !hpfs_sb(s)->sb_was_error; + mark_buffer_dirty(bh); + brelse(bh); + } +} + +/* Filesystem error... */ + +#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 (!hpfs_sb(s)->sb_was_error) { + if (hpfs_sb(s)->sb_err == 2) { + printk("; crashing the system because you wanted it\n"); + mark_dirty(s); + panic("HPFS panic"); + } else if (hpfs_sb(s)->sb_err == 1) { + if (s->s_flags & MS_RDONLY) printk("; already mounted read-only\n"); + else { + printk("; remounting read-only\n"); + mark_dirty(s); + s->s_flags |= MS_RDONLY; + } + } else if (s->s_flags & MS_RDONLY) printk("; going on - but anything won't be destroyed because it's read-only\n"); + else printk("; corrupted filesystem mounted read/write - your computer will explode within 20 seconds ... but you wanted it so!\n"); + } else printk("\n"); + if (buf) kfree(buf); + hpfs_sb(s)->sb_was_error = 1; +} + +/* + * A little trick to detect cycles in many hpfs structures and don't let the + * kernel crash on corrupted filesystem. When first called, set c2 to 0. + * + * BTW. chkdsk doesn't detect cycles correctly. When I had 2 lost directories + * nested each in other, chkdsk locked up happilly. + */ + +int hpfs_stop_cycles(struct super_block *s, int key, int *c1, int *c2, + char *msg) +{ + if (*c2 && *c1 == key) { + hpfs_error(s, "cycle detected on key %08x in %s", key, msg); + return 1; + } + (*c2)++; + if (!((*c2 - 1) & *c2)) *c1 = key; + return 0; +} + +static void hpfs_put_super(struct super_block *s) +{ + struct hpfs_sb_info *sbi = hpfs_sb(s); + if (sbi->sb_cp_table) kfree(sbi->sb_cp_table); + if (sbi->sb_bmp_dir) kfree(sbi->sb_bmp_dir); + unmark_dirty(s); + s->s_fs_info = NULL; + kfree(sbi); +} + +unsigned hpfs_count_one_bitmap(struct super_block *s, secno secno) +{ + struct quad_buffer_head qbh; + unsigned *bits; + unsigned i, count; + if (!(bits = hpfs_map_4sectors(s, secno, &qbh, 4))) return 0; + count = 0; + for (i = 0; i < 2048 / sizeof(unsigned); i++) { + unsigned b; + if (!bits[i]) continue; + for (b = bits[i]; b; b>>=1) count += b & 1; + } + hpfs_brelse4(&qbh); + return count; +} + +static unsigned count_bitmaps(struct super_block *s) +{ + unsigned n, count, n_bands; + n_bands = (hpfs_sb(s)->sb_fs_size + 0x3fff) >> 14; + count = 0; + for (n = 0; n < n_bands; n++) + count += hpfs_count_one_bitmap(s, hpfs_sb(s)->sb_bmp_dir[n]); + return count; +} + +static int hpfs_statfs(struct super_block *s, struct kstatfs *buf) +{ + struct hpfs_sb_info *sbi = hpfs_sb(s); + lock_kernel(); + + /*if (sbi->sb_n_free == -1) {*/ + sbi->sb_n_free = count_bitmaps(s); + sbi->sb_n_free_dnodes = hpfs_count_one_bitmap(s, sbi->sb_dmap); + /*}*/ + buf->f_type = s->s_magic; + buf->f_bsize = 512; + buf->f_blocks = sbi->sb_fs_size; + buf->f_bfree = sbi->sb_n_free; + buf->f_bavail = sbi->sb_n_free; + buf->f_files = sbi->sb_dirband_size / 4; + buf->f_ffree = sbi->sb_n_free_dnodes; + buf->f_namelen = 254; + + unlock_kernel(); + + return 0; +} + +static kmem_cache_t * hpfs_inode_cachep; + +static struct inode *hpfs_alloc_inode(struct super_block *sb) +{ + struct hpfs_inode_info *ei; + ei = (struct hpfs_inode_info *)kmem_cache_alloc(hpfs_inode_cachep, SLAB_NOFS); + if (!ei) + return NULL; + ei->vfs_inode.i_version = 1; + return &ei->vfs_inode; +} + +static void hpfs_destroy_inode(struct inode *inode) +{ + kmem_cache_free(hpfs_inode_cachep, hpfs_i(inode)); +} + +static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) +{ + struct hpfs_inode_info *ei = (struct hpfs_inode_info *) foo; + + if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == + SLAB_CTOR_CONSTRUCTOR) { + init_MUTEX(&ei->i_sem); + init_MUTEX(&ei->i_parent); + inode_init_once(&ei->vfs_inode); + } +} + +static int init_inodecache(void) +{ + hpfs_inode_cachep = kmem_cache_create("hpfs_inode_cache", + sizeof(struct hpfs_inode_info), + 0, SLAB_RECLAIM_ACCOUNT, + init_once, NULL); + if (hpfs_inode_cachep == NULL) + return -ENOMEM; + return 0; +} + +static void destroy_inodecache(void) +{ + if (kmem_cache_destroy(hpfs_inode_cachep)) + printk(KERN_INFO "hpfs_inode_cache: not all structures were freed\n"); +} + +/* + * A tiny parser for option strings, stolen from dosfs. + * Stolen again from read-only hpfs. + * And updated for table-driven option parsing. + */ + +enum { + Opt_help, Opt_uid, Opt_gid, Opt_umask, Opt_case_lower, Opt_case_asis, + Opt_conv_binary, Opt_conv_text, Opt_conv_auto, + Opt_check_none, Opt_check_normal, Opt_check_strict, + Opt_err_cont, Opt_err_ro, Opt_err_panic, + Opt_eas_no, Opt_eas_ro, Opt_eas_rw, + Opt_chkdsk_no, Opt_chkdsk_errors, Opt_chkdsk_always, + Opt_timeshift, Opt_err, +}; + +static match_table_t tokens = { + {Opt_help, "help"}, + {Opt_uid, "uid=%u"}, + {Opt_gid, "gid=%u"}, + {Opt_umask, "umask=%o"}, + {Opt_case_lower, "case=lower"}, + {Opt_case_asis, "case=asis"}, + {Opt_conv_binary, "conv=binary"}, + {Opt_conv_text, "conv=text"}, + {Opt_conv_auto, "conv=auto"}, + {Opt_check_none, "check=none"}, + {Opt_check_normal, "check=normal"}, + {Opt_check_strict, "check=strict"}, + {Opt_err_cont, "errors=continue"}, + {Opt_err_ro, "errors=remount-ro"}, + {Opt_err_panic, "errors=panic"}, + {Opt_eas_no, "eas=no"}, + {Opt_eas_ro, "eas=ro"}, + {Opt_eas_rw, "eas=rw"}, + {Opt_chkdsk_no, "chkdsk=no"}, + {Opt_chkdsk_errors, "chkdsk=errors"}, + {Opt_chkdsk_always, "chkdsk=always"}, + {Opt_timeshift, "timeshift=%d"}, + {Opt_err, NULL}, +}; + +static int parse_opts(char *opts, uid_t *uid, gid_t *gid, umode_t *umask, + int *lowercase, int *conv, int *eas, int *chk, int *errs, + int *chkdsk, int *timeshift) +{ + char *p; + int option; + + if (!opts) + return 1; + + /*printk("Parsing opts: '%s'\n",opts);*/ + + while ((p = strsep(&opts, ",")) != NULL) { + substring_t args[MAX_OPT_ARGS]; + int token; + if (!*p) + continue; + + token = match_token(p, tokens, args); + switch (token) { + case Opt_help: + return 2; + case Opt_uid: + if (match_int(args, &option)) + return 0; + *uid = option; + break; + case Opt_gid: + if (match_int(args, &option)) + return 0; + *gid = option; + break; + case Opt_umask: + if (match_octal(args, &option)) + return 0; + *umask = option; + break; + case Opt_case_lower: + *lowercase = 1; + break; + case Opt_case_asis: + *lowercase = 0; + break; + case Opt_conv_binary: + *conv = CONV_BINARY; + break; + case Opt_conv_text: + *conv = CONV_TEXT; + break; + case Opt_conv_auto: + *conv = CONV_AUTO; + break; + case Opt_check_none: + *chk = 0; + break; + case Opt_check_normal: + *chk = 1; + break; + case Opt_check_strict: + *chk = 2; + break; + case Opt_err_cont: + *errs = 0; + break; + case Opt_err_ro: + *errs = 1; + break; + case Opt_err_panic: + *errs = 2; + break; + case Opt_eas_no: + *eas = 0; + break; + case Opt_eas_ro: + *eas = 1; + break; + case Opt_eas_rw: + *eas = 2; + break; + case Opt_chkdsk_no: + *chkdsk = 0; + break; + case Opt_chkdsk_errors: + *chkdsk = 1; + break; + case Opt_chkdsk_always: + *chkdsk = 2; + break; + case Opt_timeshift: + { + int m = 1; + char *rhs = args[0].from; + if (!rhs || !*rhs) + return 0; + if (*rhs == '-') m = -1; + if (*rhs == '+' || *rhs == '-') rhs++; + *timeshift = simple_strtoul(rhs, &rhs, 0) * m; + if (*rhs) + return 0; + break; + } + default: + return 0; + } + } + return 1; +} + +static inline void hpfs_help(void) +{ + printk("\n\ +HPFS filesystem options:\n\ + help do not mount and display this text\n\ + uid=xxx set uid of files that don't have uid specified in eas\n\ + gid=xxx set gid of files that don't have gid specified in eas\n\ + umask=xxx set mode of files that don't have mode specified in eas\n\ + case=lower lowercase all files\n\ + case=asis do not lowercase files (default)\n\ + conv=binary do not convert CR/LF -> LF (default)\n\ + conv=auto convert only files with known text extensions\n\ + conv=text convert all files\n\ + check=none no fs checks - kernel may crash on corrupted filesystem\n\ + check=normal do some checks - it should not crash (default)\n\ + check=strict do extra time-consuming checks, used for debugging\n\ + errors=continue continue on errors\n\ + errors=remount-ro remount read-only if errors found (default)\n\ + errors=panic panic on errors\n\ + chkdsk=no do not mark fs for chkdsking even if there were errors\n\ + chkdsk=errors mark fs dirty if errors found (default)\n\ + chkdsk=always always mark fs dirty - used for debugging\n\ + eas=no ignore extended attributes\n\ + eas=ro read but do not write extended attributes\n\ + eas=rw r/w eas => enables chmod, chown, mknod, ln -s (default)\n\ + timeshift=nnn add nnn seconds to file times\n\ +\n"); +} + +static int hpfs_remount_fs(struct super_block *s, int *flags, char *data) +{ + uid_t uid; + gid_t gid; + umode_t umask; + int lowercase, conv, eas, chk, errs, chkdsk, timeshift; + int o; + struct hpfs_sb_info *sbi = hpfs_sb(s); + + *flags |= MS_NOATIME; + + uid = sbi->sb_uid; gid = sbi->sb_gid; + umask = 0777 & ~sbi->sb_mode; + lowercase = sbi->sb_lowercase; conv = sbi->sb_conv; + eas = sbi->sb_eas; chk = sbi->sb_chk; chkdsk = sbi->sb_chkdsk; + errs = sbi->sb_err; timeshift = sbi->sb_timeshift; + + if (!(o = parse_opts(data, &uid, &gid, &umask, &lowercase, &conv, + &eas, &chk, &errs, &chkdsk, ×hift))) { + printk("HPFS: bad mount options.\n"); + return 1; + } + if (o == 2) { + hpfs_help(); + return 1; + } + if (timeshift != sbi->sb_timeshift) { + printk("HPFS: timeshift can't be changed using remount.\n"); + return 1; + } + + unmark_dirty(s); + + sbi->sb_uid = uid; sbi->sb_gid = gid; + sbi->sb_mode = 0777 & ~umask; + sbi->sb_lowercase = lowercase; sbi->sb_conv = conv; + sbi->sb_eas = eas; sbi->sb_chk = chk; sbi->sb_chkdsk = chkdsk; + sbi->sb_err = errs; sbi->sb_timeshift = timeshift; + + if (!(*flags & MS_RDONLY)) mark_dirty(s); + + return 0; +} + +/* Super operations */ + +static struct super_operations hpfs_sops = +{ + .alloc_inode = hpfs_alloc_inode, + .destroy_inode = hpfs_destroy_inode, + .delete_inode = hpfs_delete_inode, + .put_super = hpfs_put_super, + .statfs = hpfs_statfs, + .remount_fs = hpfs_remount_fs, +}; + +static int hpfs_fill_super(struct super_block *s, void *options, int silent) +{ + struct buffer_head *bh0, *bh1, *bh2; + struct hpfs_boot_block *bootblock; + struct hpfs_super_block *superblock; + struct hpfs_spare_block *spareblock; + struct hpfs_sb_info *sbi; + struct inode *root; + + uid_t uid; + gid_t gid; + umode_t umask; + int lowercase, conv, eas, chk, errs, chkdsk, timeshift; + + dnode_secno root_dno; + struct hpfs_dirent *de = NULL; + struct quad_buffer_head qbh; + + int o; + + sbi = kmalloc(sizeof(*sbi), GFP_KERNEL); + if (!sbi) + return -ENOMEM; + s->s_fs_info = sbi; + memset(sbi, 0, sizeof(*sbi)); + + sbi->sb_bmp_dir = NULL; + sbi->sb_cp_table = NULL; + + init_MUTEX(&sbi->hpfs_creation_de); + + uid = current->uid; + gid = current->gid; + umask = current->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; + } + + /*sbi->sb_mounting = 1;*/ + sb_set_blocksize(s, 512); + sbi->sb_fs_size = -1; + if (!(bootblock = hpfs_map_sector(s, 0, &bh0, 0))) goto bail1; + if (!(superblock = hpfs_map_sector(s, 16, &bh1, 1))) goto bail2; + if (!(spareblock = hpfs_map_sector(s, 17, &bh2, 0))) goto bail3; + + /* Check magics */ + if (/*bootblock->magic != BB_MAGIC + ||*/ superblock->magic != SB_MAGIC + || spareblock->magic != SP_MAGIC) { + if (!silent) printk("HPFS: Bad magic ... probably not HPFS\n"); + goto bail4; + } + + /* Check version */ + if (!(s->s_flags & MS_RDONLY) && + superblock->funcversion != 2 && superblock->funcversion != 3) { + printk("HPFS: Bad version %d,%d. Mount readonly to go around\n", + (int)superblock->version, (int)superblock->funcversion); + printk("HPFS: please try recent version of HPFS driver at http://artax.karlin.mff.cuni.cz/~mikulas/vyplody/hpfs/index-e.cgi and if it still can't understand this format, contact author - mikulas@artax.karlin.mff.cuni.cz\n"); + goto bail4; + } + + s->s_flags |= MS_NOATIME; + + /* Fill superblock stuff */ + s->s_magic = HPFS_SUPER_MAGIC; + s->s_op = &hpfs_sops; + + sbi->sb_root = superblock->root; + sbi->sb_fs_size = superblock->n_sectors; + sbi->sb_bitmaps = superblock->bitmaps; + sbi->sb_dirband_start = superblock->dir_band_start; + sbi->sb_dirband_size = superblock->n_dir_band; + sbi->sb_dmap = superblock->dir_band_bitmap; + sbi->sb_uid = uid; + sbi->sb_gid = gid; + sbi->sb_mode = 0777 & ~umask; + sbi->sb_n_free = -1; + sbi->sb_n_free_dnodes = -1; + sbi->sb_lowercase = lowercase; + sbi->sb_conv = conv; + sbi->sb_eas = eas; + sbi->sb_chk = chk; + sbi->sb_chkdsk = chkdsk; + sbi->sb_err = errs; + sbi->sb_timeshift = timeshift; + sbi->sb_was_error = 0; + sbi->sb_cp_table = NULL; + sbi->sb_c_bitmap = -1; + sbi->sb_max_fwd_alloc = 0xffffff; + + /* Load bitmap directory */ + if (!(sbi->sb_bmp_dir = hpfs_load_bitmap_directory(s, superblock->bitmaps))) + goto bail4; + + /* Check for general fs errors*/ + if (spareblock->dirty && !spareblock->old_wrote) { + if (errs == 2) { + printk("HPFS: Improperly stopped, not mounted\n"); + goto bail4; + } + hpfs_error(s, "improperly stopped"); + } + + if (!(s->s_flags & MS_RDONLY)) { + spareblock->dirty = 1; + spareblock->old_wrote = 0; + mark_buffer_dirty(bh2); + } + + if (spareblock->hotfixes_used || spareblock->n_spares_used) { + if (errs >= 2) { + printk("HPFS: Hotfixes not supported here, try chkdsk\n"); + mark_dirty(s); + goto bail4; + } + hpfs_error(s, "hotfixes not supported here, try chkdsk"); + if (errs == 0) printk("HPFS: Proceeding, but your filesystem will be probably corrupted by this driver...\n"); + else printk("HPFS: This driver may read bad files or crash when operating on disk with hotfixes.\n"); + } + if (spareblock->n_dnode_spares != spareblock->n_dnode_spares_free) { + if (errs >= 2) { + printk("HPFS: Spare dnodes used, try chkdsk\n"); + mark_dirty(s); + goto bail4; + } + hpfs_error(s, "warning: spare dnodes used, try chkdsk"); + if (errs == 0) printk("HPFS: Proceeding, but your filesystem could be corrupted if you delete files or directories\n"); + } + if (chk) { + unsigned a; + if (superblock->dir_band_end - superblock->dir_band_start + 1 != superblock->n_dir_band || + superblock->dir_band_end < superblock->dir_band_start || superblock->n_dir_band > 0x4000) { + hpfs_error(s, "dir band size mismatch: dir_band_start==%08x, dir_band_end==%08x, n_dir_band==%08x", + superblock->dir_band_start, superblock->dir_band_end, superblock->n_dir_band); + goto bail4; + } + a = sbi->sb_dirband_size; + sbi->sb_dirband_size = 0; + if (hpfs_chk_sectors(s, superblock->dir_band_start, superblock->n_dir_band, "dir_band") || + hpfs_chk_sectors(s, superblock->dir_band_bitmap, 4, "dir_band_bitmap") || + hpfs_chk_sectors(s, superblock->bitmaps, 4, "bitmaps")) { + mark_dirty(s); + goto bail4; + } + sbi->sb_dirband_size = a; + } else printk("HPFS: You really don't want any checks? You are crazy...\n"); + + /* Load code page table */ + if (spareblock->n_code_pages) + if (!(sbi->sb_cp_table = hpfs_load_code_page(s, spareblock->code_page_dir))) + printk("HPFS: Warning: code page support is disabled\n"); + + brelse(bh2); + brelse(bh1); + brelse(bh0); + + root = iget_locked(s, sbi->sb_root); + if (!root) + goto bail0; + hpfs_init_inode(root); + hpfs_read_inode(root); + unlock_new_inode(root); + s->s_root = d_alloc_root(root); + if (!s->s_root) { + iput(root); + goto bail0; + } + hpfs_set_dentry_operations(s->s_root); + + /* + * find the root directory's . pointer & finish filling in the inode + */ + + root_dno = hpfs_fnode_dno(s, sbi->sb_root); + if (root_dno) + de = map_dirent(root, root_dno, "\001\001", 2, NULL, &qbh); + if (!de) + hpfs_error(s, "unable to find root dir"); + else { + root->i_atime.tv_sec = local_to_gmt(s, de->read_date); + root->i_atime.tv_nsec = 0; + root->i_mtime.tv_sec = local_to_gmt(s, de->write_date); + root->i_mtime.tv_nsec = 0; + root->i_ctime.tv_sec = local_to_gmt(s, de->creation_date); + root->i_ctime.tv_nsec = 0; + hpfs_i(root)->i_ea_size = de->ea_size; + hpfs_i(root)->i_parent_dir = root->i_ino; + if (root->i_size == -1) + root->i_size = 2048; + if (root->i_blocks == -1) + root->i_blocks = 5; + hpfs_brelse4(&qbh); + } + return 0; + +bail4: brelse(bh2); +bail3: brelse(bh1); +bail2: brelse(bh0); +bail1: +bail0: + if (sbi->sb_bmp_dir) kfree(sbi->sb_bmp_dir); + if (sbi->sb_cp_table) kfree(sbi->sb_cp_table); + s->s_fs_info = NULL; + kfree(sbi); + return -EINVAL; +} + +static struct super_block *hpfs_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data) +{ + return get_sb_bdev(fs_type, flags, dev_name, data, hpfs_fill_super); +} + +static struct file_system_type hpfs_fs_type = { + .owner = THIS_MODULE, + .name = "hpfs", + .get_sb = hpfs_get_sb, + .kill_sb = kill_block_super, + .fs_flags = FS_REQUIRES_DEV, +}; + +static int __init init_hpfs_fs(void) +{ + int err = init_inodecache(); + if (err) + goto out1; + err = register_filesystem(&hpfs_fs_type); + if (err) + goto out; + return 0; +out: + destroy_inodecache(); +out1: + return err; +} + +static void __exit exit_hpfs_fs(void) +{ + unregister_filesystem(&hpfs_fs_type); + destroy_inodecache(); +} + +module_init(init_hpfs_fs) +module_exit(exit_hpfs_fs) +MODULE_LICENSE("GPL"); |