summaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1999-06-17 14:08:29 +0000
committerRalf Baechle <ralf@linux-mips.org>1999-06-17 14:08:29 +0000
commit57d569635c05dc4ea9b9f1f8dcec69b9ddc989b2 (patch)
tree1f703abf7d95dcd50ee52da3b96eb1b4b2b4ea53 /fs
parent59223edaa18759982db0a8aced0e77457d10c68e (diff)
The rest of 2.3.6.
Diffstat (limited to 'fs')
-rw-r--r--fs/hpfs/alloc.c434
-rw-r--r--fs/hpfs/anode.c482
-rw-r--r--fs/hpfs/buffer.c265
-rw-r--r--fs/hpfs/dentry.c62
-rw-r--r--fs/hpfs/dir.c253
-rw-r--r--fs/hpfs/dnode.c1070
-rw-r--r--fs/hpfs/ea.c305
-rw-r--r--fs/hpfs/file.c195
-rw-r--r--fs/hpfs/hpfs_fn.h318
-rw-r--r--fs/hpfs/inode.c381
-rw-r--r--fs/hpfs/map.c268
-rw-r--r--fs/hpfs/mmap.c128
-rw-r--r--fs/hpfs/name.c144
-rw-r--r--fs/hpfs/namei.c549
-rw-r--r--fs/hpfs/super.c598
-rw-r--r--fs/proc/sysvipc.c138
16 files changed, 5590 insertions, 0 deletions
diff --git a/fs/hpfs/alloc.c b/fs/hpfs/alloc.c
new file mode 100644
index 000000000..bca0497ae
--- /dev/null
+++ b/fs/hpfs/alloc.c
@@ -0,0 +1,434 @@
+/*
+ * linux/fs/hpfs/alloc.c
+ *
+ * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
+ *
+ * HPFS bitmap operations
+ */
+
+#include "hpfs_fn.h"
+
+/*
+ * Check if a sector is allocated in bitmap
+ * This is really slow. Turned on only if chk==2
+ */
+
+static int chk_if_allocated(struct super_block *s, secno sec, char *msg)
+{
+ struct quad_buffer_head qbh;
+ unsigned *bmp;
+ if (!(bmp = hpfs_map_bitmap(s, sec >> 14, &qbh, "chk"))) goto fail;
+ if ((bmp[(sec & 0x3fff) >> 5] >> (sec & 0x1f)) & 1) {
+ hpfs_error(s, "sector '%s' - %08x not allocated in bitmap", msg, sec);
+ goto fail1;
+ }
+ hpfs_brelse4(&qbh);
+ if (sec >= s->s_hpfs_dirband_start && sec < s->s_hpfs_dirband_start + s->s_hpfs_dirband_size) {
+ unsigned ssec = (sec - s->s_hpfs_dirband_start) / 4;
+ if (!(bmp = hpfs_map_dnode_bitmap(s, &qbh))) goto fail;
+ if ((bmp[ssec >> 5] >> (ssec & 0x1f)) & 1) {
+ hpfs_error(s, "sector '%s' - %08x not allocated in directory bitmap", msg, sec);
+ goto fail1;
+ }
+ hpfs_brelse4(&qbh);
+ }
+ return 0;
+ fail1:
+ hpfs_brelse4(&qbh);
+ fail:
+ return 1;
+}
+
+/*
+ * Check if sector(s) have proper number and additionally check if they're
+ * allocated in bitmap.
+ */
+
+int hpfs_chk_sectors(struct super_block *s, secno start, int len, char *msg)
+{
+ if (start + len < start || start < 0x12 ||
+ start + len > s->s_hpfs_fs_size) {
+ hpfs_error(s, "sector(s) '%s' badly placed at %08x", msg, start);
+ return 1;
+ }
+ if (s->s_hpfs_chk>=2) {
+ int i;
+ for (i = 0; i < len; i++)
+ if (chk_if_allocated(s, start + i, msg)) return 1;
+ }
+ return 0;
+}
+
+static secno alloc_in_bmp(struct super_block *s, secno near, unsigned n, unsigned forward)
+{
+ struct quad_buffer_head qbh;
+ unsigned *bmp;
+ unsigned bs = near & ~0x3fff;
+ unsigned nr = (near & 0x3fff) & ~(n - 1);
+ /*unsigned mnr;*/
+ unsigned i, q;
+ int a, b;
+ secno ret = 0;
+ if (n != 1 && n != 4) {
+ hpfs_error(s, "Bad allocation size: %d", n);
+ return 0;
+ }
+ lock_super(s);
+ if (bs != ~0x3fff) {
+ if (!(bmp = hpfs_map_bitmap(s, near >> 14, &qbh, "aib"))) goto uls;
+ } else {
+ if (!(bmp = hpfs_map_dnode_bitmap(s, &qbh))) goto uls;
+ }
+ /*if (!tstbits(bmp, nr + n, n + forward)) {
+ ret = bs + nr;
+ goto rt;
+ }
+ if (!tstbits(bmp, nr + 2*n, n + forward)) {
+ ret = bs + nr + n;
+ goto rt;
+ }*/
+ q = nr + n; b = 0;
+ while ((a = tstbits(bmp, q, n + forward))) {
+ q += a;
+ if (n != 1) q = ((q-1)&~(n-1))+n;
+ if (!b) {
+ if (q>>5 != nr>>5) {
+ b = 1;
+ q = nr & 0x1f;
+ }
+ } else if (q > nr) break;
+ }
+ if (!a) {
+ ret = bs + q;
+ goto rt;
+ }
+ nr >>= 5;
+ for (i = nr + 1; i != nr; i++, i &= 0x1ff) {
+ if (!bmp[i]) continue;
+ if (n + forward >= 0x3f && bmp[i] != -1) continue;
+ q = i<<5;
+ if (i > 0) {
+ unsigned k = bmp[i-1];
+ while (k & 0x80000000) {
+ q--; k <<= 1;
+ }
+ }
+ if (n != 1) q = ((q-1)&~(n-1))+n;
+ while ((a = tstbits(bmp, q, n + forward))) {
+ q += a;
+ if (n != 1) q = ((q-1)&~(n-1))+n;
+ if (q>>5 > i) break;
+ }
+ if (!a) {
+ ret = bs + q;
+ goto rt;
+ }
+ }
+ rt:
+ if (ret) {
+ if (s->s_hpfs_chk && ((ret >> 14) != (bs >> 14) || (bmp[(ret & 0x3fff) >> 5] | ~(((1 << n) - 1) << (ret & 0x1f))) != 0xffffffff)) {
+ hpfs_error(s, "Allocation doesn't work! Wanted %d, allocated at %08x", n, ret);
+ ret = 0;
+ goto b;
+ }
+ bmp[(ret & 0x3fff) >> 5] &= ~(((1 << n) - 1) << (ret & 0x1f));
+ hpfs_mark_4buffers_dirty(&qbh);
+ }
+ b:
+ hpfs_brelse4(&qbh);
+ uls:
+ unlock_super(s);
+ return ret;
+}
+
+/*
+ * Allocation strategy: 1) search place near the sector specified
+ * 2) search bitmap where free sectors last found
+ * 3) search all bitmaps
+ * 4) search all bitmaps ignoring number of pre-allocated
+ * sectors
+ */
+
+secno hpfs_alloc_sector(struct super_block *s, secno near, unsigned n, int forward, int lock)
+{
+ secno sec;
+ unsigned i;
+ unsigned n_bmps;
+ int b = s->s_hpfs_c_bitmap;
+ int f_p = 0;
+ if (forward < 0) {
+ forward = -forward;
+ f_p = 1;
+ }
+ if (lock) hpfs_lock_creation(s);
+ if (near && near < s->s_hpfs_fs_size)
+ if ((sec = alloc_in_bmp(s, near, n, f_p ? forward : forward/4))) goto ret;
+ if (b != -1) {
+ if ((sec = alloc_in_bmp(s, b<<14, n, f_p ? forward : forward/2))) {
+ b &= 0x0fffffff;
+ goto ret;
+ }
+ if (b > 0x10000000) if ((sec = alloc_in_bmp(s, (b&0xfffffff)<<14, n, f_p ? forward : 0))) goto ret;
+ }
+ n_bmps = (s->s_hpfs_fs_size + 0x4000 - 1) >> 14;
+ for (i = 0; i < n_bmps / 2; i++) {
+ if ((sec = alloc_in_bmp(s, (n_bmps/2+i) << 14, n, forward))) {
+ s->s_hpfs_c_bitmap = n_bmps/2+i;
+ goto ret;
+ }
+ if ((sec = alloc_in_bmp(s, (n_bmps/2-i-1) << 14, n, forward))) {
+ s->s_hpfs_c_bitmap = n_bmps/2-i-1;
+ goto ret;
+ }
+ }
+ if ((sec = alloc_in_bmp(s, (n_bmps-1) << 14, n, forward))) {
+ s->s_hpfs_c_bitmap = n_bmps-1;
+ goto ret;
+ }
+ if (!f_p) {
+ for (i = 0; i < n_bmps; i++)
+ if ((sec = alloc_in_bmp(s, i << 14, n, 0))) {
+ s->s_hpfs_c_bitmap = 0x10000000 + i;
+ goto ret;
+ }
+ }
+ sec = 0;
+ ret:
+ if (sec && f_p) {
+ for (i = 0; i < forward; i++) {
+ if (!hpfs_alloc_if_possible_nolock(s, sec + i + 1)) {
+ hpfs_error(s, "Prealloc doesn't work! Wanted %d, allocated at %08x, can't allocate %d", forward, sec, i);
+ sec = 0;
+ break;
+ }
+ }
+ }
+ if (lock) hpfs_unlock_creation(s);
+ return sec;
+}
+
+static secno alloc_in_dirband(struct super_block *s, secno near, int lock)
+{
+ unsigned nr = near;
+ secno sec;
+ if (nr < s->s_hpfs_dirband_start)
+ nr = s->s_hpfs_dirband_start;
+ if (nr >= s->s_hpfs_dirband_start + s->s_hpfs_dirband_size)
+ nr = s->s_hpfs_dirband_start + s->s_hpfs_dirband_size - 4;
+ nr -= s->s_hpfs_dirband_start;
+ nr >>= 2;
+ if (lock) hpfs_lock_creation(s);
+ sec = alloc_in_bmp(s, (~0x3fff) | nr, 1, 0);
+ if (lock) hpfs_unlock_creation(s);
+ if (!sec) return 0;
+ return ((sec & 0x3fff) << 2) + s->s_hpfs_dirband_start;
+}
+
+/* Alloc sector if it's free */
+
+int hpfs_alloc_if_possible_nolock(struct super_block *s, secno sec)
+{
+ struct quad_buffer_head qbh;
+ unsigned *bmp;
+ lock_super(s);
+ if (!(bmp = hpfs_map_bitmap(s, sec >> 14, &qbh, "aip"))) goto end;
+ if (bmp[(sec & 0x3fff) >> 5] & (1 << (sec & 0x1f))) {
+ bmp[(sec & 0x3fff) >> 5] &= ~(1 << (sec & 0x1f));
+ hpfs_mark_4buffers_dirty(&qbh);
+ hpfs_brelse4(&qbh);
+ unlock_super(s);
+ return 1;
+ }
+ hpfs_brelse4(&qbh);
+ end:
+ unlock_super(s);
+ return 0;
+}
+
+int hpfs_alloc_if_possible(struct super_block *s, secno sec)
+{
+ int r;
+ hpfs_lock_creation(s);
+ r = hpfs_alloc_if_possible_nolock(s, sec);
+ hpfs_unlock_creation(s);
+ return r;
+}
+
+/* Free sectors in bitmaps */
+
+void hpfs_free_sectors(struct super_block *s, secno sec, unsigned n)
+{
+ struct quad_buffer_head qbh;
+ unsigned *bmp;
+ /*printk("2 - ");*/
+ if (!n) return;
+ if (sec < 0x12) {
+ hpfs_error(s, "Trying to free reserved sector %08x", sec);
+ return;
+ }
+ lock_super(s);
+ new_map:
+ if (!(bmp = hpfs_map_bitmap(s, sec >> 14, &qbh, "free"))) {
+ unlock_super(s);
+ return;
+ }
+ new_tst:
+ if ((bmp[(sec & 0x3fff) >> 5] >> (sec & 0x1f) & 1)) {
+ hpfs_error(s, "sector %08x not allocated", sec);
+ hpfs_brelse4(&qbh);
+ unlock_super(s);
+ return;
+ }
+ bmp[(sec & 0x3fff) >> 5] |= 1 << (sec & 0x1f);
+ if (!--n) {
+ hpfs_mark_4buffers_dirty(&qbh);
+ hpfs_brelse4(&qbh);
+ unlock_super(s);
+ return;
+ }
+ if (!(++sec & 0x3fff)) {
+ hpfs_mark_4buffers_dirty(&qbh);
+ hpfs_brelse4(&qbh);
+ goto new_map;
+ }
+ goto new_tst;
+}
+
+/*
+ * Check if there are at least n free dnodes on the filesystem.
+ * Called before adding to dnode. If we run out of space while
+ * splitting dnodes, it would corrupt dnode tree.
+ */
+
+int hpfs_check_free_dnodes(struct super_block *s, int n)
+{
+ int n_bmps = (s->s_hpfs_fs_size + 0x4000 - 1) >> 14;
+ int b = s->s_hpfs_c_bitmap & 0x0fffffff;
+ int i, j;
+ unsigned *bmp;
+ struct quad_buffer_head qbh;
+ if ((bmp = hpfs_map_dnode_bitmap(s, &qbh))) {
+ for (j = 0; j < 512; j++) {
+ unsigned k;
+ if (!bmp[j]) continue;
+ for (k = bmp[j]; k; k >>= 1) if (k & 1) if (!--n) {
+ hpfs_brelse4(&qbh);
+ return 0;
+ }
+ }
+ }
+ hpfs_brelse4(&qbh);
+ i = 0;
+ if (s->s_hpfs_c_bitmap != -1 ) {
+ bmp = hpfs_map_bitmap(s, b, &qbh, "chkdn1");
+ goto chk_bmp;
+ }
+ chk_next:
+ if (i == b) i++;
+ if (i >= n_bmps) return 1;
+ bmp = hpfs_map_bitmap(s, i, &qbh, "chkdn2");
+ chk_bmp:
+ if (bmp) {
+ for (j = 0; j < 512; j++) {
+ unsigned k;
+ if (!bmp[j]) continue;
+ for (k = 0xf; k; k <<= 4)
+ if ((bmp[j] & k) == k) {
+ if (!--n) {
+ hpfs_brelse4(&qbh);
+ return 0;
+ }
+ }
+ }
+ hpfs_brelse4(&qbh);
+ }
+ i++;
+ goto chk_next;
+}
+
+void hpfs_free_dnode(struct super_block *s, dnode_secno dno)
+{
+ if (s->s_hpfs_chk) if (dno & 3) {
+ hpfs_error(s, "hpfs_free_dnode: dnode %08x not aligned", dno);
+ return;
+ }
+ if (dno < s->s_hpfs_dirband_start ||
+ dno >= s->s_hpfs_dirband_start + s->s_hpfs_dirband_size) {
+ hpfs_free_sectors(s, dno, 4);
+ } else {
+ struct quad_buffer_head qbh;
+ unsigned *bmp;
+ unsigned ssec = (dno - s->s_hpfs_dirband_start) / 4;
+ lock_super(s);
+ if (!(bmp = hpfs_map_dnode_bitmap(s, &qbh))) {
+ unlock_super(s);
+ return;
+ }
+ bmp[ssec >> 5] |= 1 << (ssec & 0x1f);
+ hpfs_mark_4buffers_dirty(&qbh);
+ hpfs_brelse4(&qbh);
+ unlock_super(s);
+ }
+}
+
+struct dnode *hpfs_alloc_dnode(struct super_block *s, secno near,
+ dnode_secno *dno, struct quad_buffer_head *qbh,
+ int lock)
+{
+ struct dnode *d;
+ if (hpfs_count_one_bitmap(s, s->s_hpfs_dmap) > FREE_DNODES_ADD) {
+ if (!(*dno = alloc_in_dirband(s, near, lock)))
+ if (!(*dno = hpfs_alloc_sector(s, near, 4, 0, lock))) return NULL;
+ } else {
+ if (!(*dno = hpfs_alloc_sector(s, near, 4, 0, lock)))
+ if (!(*dno = alloc_in_dirband(s, near, lock))) return NULL;
+ }
+ if (!(d = hpfs_get_4sectors(s, *dno, qbh))) {
+ hpfs_free_dnode(s, *dno);
+ return NULL;
+ }
+ memset(d, 0, 2048);
+ d->magic = DNODE_MAGIC;
+ d->first_free = 52;
+ d->dirent[0] = 32;
+ d->dirent[2] = 8;
+ d->dirent[30] = 1;
+ d->dirent[31] = 255;
+ d->self = *dno;
+ return d;
+}
+
+struct fnode *hpfs_alloc_fnode(struct super_block *s, secno near, fnode_secno *fno,
+ struct buffer_head **bh)
+{
+ struct fnode *f;
+ if (!(*fno = hpfs_alloc_sector(s, near, 1, FNODE_ALLOC_FWD, 1))) return NULL;
+ if (!(f = hpfs_get_sector(s, *fno, bh))) {
+ hpfs_free_sectors(s, *fno, 1);
+ return NULL;
+ }
+ memset(f, 0, 512);
+ f->magic = FNODE_MAGIC;
+ f->ea_offs = 0xc4;
+ f->btree.n_free_nodes = 8;
+ f->btree.first_free = 8;
+ return f;
+}
+
+struct anode *hpfs_alloc_anode(struct super_block *s, secno near, anode_secno *ano,
+ struct buffer_head **bh)
+{
+ struct anode *a;
+ if (!(*ano = hpfs_alloc_sector(s, near, 1, ANODE_ALLOC_FWD, 1))) return NULL;
+ if (!(a = hpfs_get_sector(s, *ano, bh))) {
+ hpfs_free_sectors(s, *ano, 1);
+ return NULL;
+ }
+ memset(a, 0, 512);
+ a->magic = ANODE_MAGIC;
+ a->self = *ano;
+ a->btree.n_free_nodes = 40;
+ a->btree.n_used_nodes = 0;
+ a->btree.first_free = 8;
+ return a;
+}
diff --git a/fs/hpfs/anode.c b/fs/hpfs/anode.c
new file mode 100644
index 000000000..f2c4a3532
--- /dev/null
+++ b/fs/hpfs/anode.c
@@ -0,0 +1,482 @@
+/*
+ * linux/fs/hpfs/anode.c
+ *
+ * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
+ *
+ * handling HPFS anode tree that contains file allocation info
+ */
+
+#include "hpfs_fn.h"
+
+/* Find a sector in allocation tree */
+
+secno hpfs_bplus_lookup(struct super_block *s, struct inode *inode,
+ struct bplus_header *btree, unsigned sec,
+ struct buffer_head *bh)
+{
+ anode_secno a = -1;
+ struct anode *anode;
+ int i;
+ int c1, c2 = 0;
+ go_down:
+ if (s->s_hpfs_chk) if (hpfs_stop_cycles(s, a, &c1, &c2, "hpfs_bplus_lookup")) return -1;
+ if (btree->internal) {
+ for (i = 0; i < btree->n_used_nodes; i++)
+ if (btree->u.internal[i].file_secno > sec) {
+ a = btree->u.internal[i].down;
+ brelse(bh);
+ if (!(anode = hpfs_map_anode(s, a, &bh))) return -1;
+ btree = &anode->btree;
+ goto go_down;
+ }
+ hpfs_error(s, "sector %08x not found in internal anode %08x", sec, a);
+ brelse(bh);
+ return -1;
+ }
+ for (i = 0; i < btree->n_used_nodes; i++)
+ if (btree->u.external[i].file_secno <= sec &&
+ btree->u.external[i].file_secno + btree->u.external[i].length > sec) {
+ a = btree->u.external[i].disk_secno + sec - btree->u.external[i].file_secno;
+ if (s->s_hpfs_chk) if (hpfs_chk_sectors(s, a, 1, "data")) {
+ brelse(bh);
+ return -1;
+ }
+ if (inode) {
+ inode->i_hpfs_file_sec = btree->u.external[i].file_secno;
+ inode->i_hpfs_disk_sec = btree->u.external[i].disk_secno;
+ inode->i_hpfs_n_secs = btree->u.external[i].length;
+ }
+ brelse(bh);
+ return a;
+ }
+ hpfs_error(s, "sector %08x not found in external anode %08x", sec, a);
+ brelse(bh);
+ return -1;
+}
+
+/* Add a sector to tree */
+
+secno hpfs_add_sector_to_btree(struct super_block *s, secno node, int fnod, unsigned fsecno)
+{
+ struct bplus_header *btree;
+ struct anode *anode = NULL, *ranode = NULL;
+ struct fnode *fnode;
+ anode_secno a, na = -1, ra, up = -1;
+ secno se;
+ struct buffer_head *bh, *bh1, *bh2;
+ int n;
+ unsigned fs;
+ int c1, c2 = 0;
+ if (fnod) {
+ if (!(fnode = hpfs_map_fnode(s, node, &bh))) return -1;
+ btree = &fnode->btree;
+ } else {
+ if (!(anode = hpfs_map_anode(s, node, &bh))) return -1;
+ btree = &anode->btree;
+ }
+ a = node;
+ go_down:
+ if ((n = btree->n_used_nodes - 1) < -!!fnod) {
+ hpfs_error(s, "anode %08x has no entries", a);
+ brelse(bh);
+ return -1;
+ }
+ if (btree->internal) {
+ a = btree->u.internal[n].down;
+ btree->u.internal[n].file_secno = -1;
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+ if (s->s_hpfs_chk)
+ if (hpfs_stop_cycles(s, a, &c1, &c2, "hpfs_add_sector_to_btree #1")) return -1;
+ if (!(anode = hpfs_map_anode(s, a, &bh))) return -1;
+ btree = &anode->btree;
+ goto go_down;
+ }
+ if (n >= 0) {
+ if (btree->u.external[n].file_secno + btree->u.external[n].length != fsecno) {
+ hpfs_error(s, "allocated size %08x, trying to add sector %08x, %cnode %08x",
+ btree->u.external[n].file_secno + btree->u.external[n].length, fsecno,
+ fnod?'f':'a', node);
+ brelse(bh);
+ return -1;
+ }
+ if (hpfs_alloc_if_possible(s, se = btree->u.external[n].disk_secno + btree->u.external[n].length)) {
+ btree->u.external[n].length++;
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+ return se;
+ }
+ } else {
+ if (fsecno) {
+ hpfs_error(s, "empty file %08x, trying to add sector %08x", node, fsecno);
+ brelse(bh);
+ return -1;
+ }
+ se = node;
+ }
+ if (!(se = hpfs_alloc_sector(s, se, 1, fsecno*ALLOC_M>ALLOC_FWD_MAX ? ALLOC_FWD_MAX : fsecno*ALLOC_M<ALLOC_FWD_MIN ? ALLOC_FWD_MIN : fsecno*ALLOC_M, 1))) {
+ brelse(bh);
+ return -1;
+ }
+ fs = n < 0 ? 0 : btree->u.external[n].file_secno + btree->u.external[n].length;
+ if (!btree->n_free_nodes) {
+ up = a != node ? anode->up : -1;
+ if (!(anode = hpfs_alloc_anode(s, a, &na, &bh1))) {
+ brelse(bh);
+ hpfs_free_sectors(s, se, 1);
+ return -1;
+ }
+ if (a == node && fnod) {
+ anode->up = node;
+ anode->btree.fnode_parent = 1;
+ anode->btree.n_used_nodes = btree->n_used_nodes;
+ anode->btree.first_free = btree->first_free;
+ anode->btree.n_free_nodes = 40 - anode->btree.n_used_nodes;
+ memcpy(&anode->u, &btree->u, btree->n_used_nodes * 12);
+ btree->internal = 1;
+ btree->n_free_nodes = 11;
+ btree->n_used_nodes = 1;
+ btree->first_free = (char *)&(btree->u.internal[1]) - (char *)btree;
+ btree->u.internal[0].file_secno = -1;
+ btree->u.internal[0].down = na;
+ mark_buffer_dirty(bh, 1);
+ } else if (!(ranode = hpfs_alloc_anode(s, /*a*/0, &ra, &bh2))) {
+ brelse(bh);
+ brelse(bh1);
+ hpfs_free_sectors(s, se, 1);
+ hpfs_free_sectors(s, na, 1);
+ return -1;
+ }
+ brelse(bh);
+ bh = bh1;
+ btree = &anode->btree;
+ }
+ btree->n_free_nodes--; n = btree->n_used_nodes++;
+ btree->first_free += 12;
+ btree->u.external[n].disk_secno = se;
+ btree->u.external[n].file_secno = fs;
+ btree->u.external[n].length = 1;
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+ if ((a == node && fnod) || na == -1) return se;
+ c2 = 0;
+ while (up != -1) {
+ if (s->s_hpfs_chk)
+ if (hpfs_stop_cycles(s, up, &c1, &c2, "hpfs_add_sector_to_btree #2")) return -1;
+ if (up != node || !fnod) {
+ if (!(anode = hpfs_map_anode(s, up, &bh))) return -1;
+ btree = &anode->btree;
+ } else {
+ if (!(fnode = hpfs_map_fnode(s, up, &bh))) return -1;
+ btree = &fnode->btree;
+ }
+ if (btree->n_free_nodes) {
+ btree->n_free_nodes--; n = btree->n_used_nodes++;
+ btree->first_free += 8;
+ btree->u.internal[n].file_secno = -1;
+ btree->u.internal[n].down = na;
+ btree->u.internal[n-1].file_secno = fs;
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+ brelse(bh2);
+ hpfs_free_sectors(s, ra, 1);
+ if ((anode = hpfs_map_anode(s, na, &bh))) {
+ anode->up = up;
+ anode->btree.fnode_parent = up == node && fnod;
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+ }
+ return se;
+ }
+ up = up != node ? anode->up : -1;
+ btree->u.internal[btree->n_used_nodes - 1].file_secno = /*fs*/-1;
+ if (up == -1) anode->up = ra;
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+ a = na;
+ if ((anode = hpfs_alloc_anode(s, a, &na, &bh))) {
+ /*anode->up = up != -1 ? up : ra;*/
+ anode->btree.internal = 1;
+ anode->btree.n_used_nodes = 1;
+ anode->btree.n_free_nodes = 59;
+ anode->btree.first_free = 16;
+ anode->btree.u.internal[0].down = a;
+ anode->btree.u.internal[0].file_secno = -1;
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+ if ((anode = hpfs_map_anode(s, a, &bh))) {
+ anode->up = na;
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+ }
+ } else na = a;
+ }
+ if ((anode = hpfs_map_anode(s, na, &bh))) {
+ anode->up = node;
+ if (fnod) anode->btree.fnode_parent = 1;
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+ }
+ if (!fnod) {
+ if (!(anode = hpfs_map_anode(s, node, &bh))) {
+ brelse(bh2);
+ return -1;
+ }
+ btree = &anode->btree;
+ } else {
+ if (!(fnode = hpfs_map_fnode(s, node, &bh))) {
+ brelse(bh2);
+ return -1;
+ }
+ btree = &fnode->btree;
+ }
+ ranode->up = node;
+ memcpy(&ranode->btree, btree, btree->first_free);
+ if (fnod) ranode->btree.fnode_parent = 1;
+ ranode->btree.n_free_nodes = (ranode->btree.internal ? 60 : 40) - ranode->btree.n_used_nodes;
+ if (ranode->btree.internal) for (n = 0; n < ranode->btree.n_used_nodes; n++) {
+ struct anode *unode;
+ if ((unode = hpfs_map_anode(s, ranode->u.internal[n].down, &bh1))) {
+ unode->up = ra;
+ unode->btree.fnode_parent = 0;
+ mark_buffer_dirty(bh1, 1);
+ brelse(bh1);
+ }
+ }
+ btree->internal = 1;
+ btree->n_free_nodes = fnod ? 10 : 58;
+ btree->n_used_nodes = 2;
+ btree->first_free = (char *)&btree->u.internal[2] - (char *)btree;
+ btree->u.internal[0].file_secno = fs;
+ btree->u.internal[0].down = ra;
+ btree->u.internal[1].file_secno = -1;
+ btree->u.internal[1].down = na;
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+ mark_buffer_dirty(bh2, 1);
+ brelse(bh2);
+ return se;
+}
+
+/*
+ * Remove allocation tree. Recursion would look much nicer but
+ * I want to avoid it because it can cause stack overflow.
+ */
+
+void hpfs_remove_btree(struct super_block *s, struct bplus_header *btree)
+{
+ struct bplus_header *btree1 = btree;
+ struct anode *anode = NULL;
+ anode_secno ano = 0, oano;
+ struct buffer_head *bh;
+ int level = 0;
+ int pos = 0;
+ int i;
+ int c1, c2 = 0;
+ int d1, d2;
+ go_down:
+ d2 = 0;
+ while (btree1->internal) {
+ ano = btree1->u.internal[pos].down;
+ if (level) brelse(bh);
+ if (s->s_hpfs_chk)
+ if (hpfs_stop_cycles(s, ano, &d1, &d2, "hpfs_remove_btree #1"))
+ return;
+ anode = hpfs_map_anode(s, ano, &bh);
+ btree1 = &anode->btree;
+ level++;
+ pos = 0;
+ }
+ for (i = 0; i < btree1->n_used_nodes; i++)
+ hpfs_free_sectors(s, btree1->u.external[i].disk_secno, btree1->u.external[i].length);
+ go_up:
+ if (!level) return;
+ if (s->s_hpfs_chk)
+ if (hpfs_stop_cycles(s, ano, &c1, &c2, "hpfs_remove_btree #2")) return;
+ hpfs_free_sectors(s, ano, 1);
+ oano = ano;
+ ano = anode->up;
+ brelse(bh);
+ if (--level) {
+ anode = hpfs_map_anode(s, ano, &bh);
+ btree1 = &anode->btree;
+ } else btree1 = btree;
+ for (i = 0; i < btree1->n_used_nodes; i++)
+ if (btree1->u.internal[i].down == oano)
+ if ((pos = i + 1) < btree1->n_used_nodes) goto go_down;
+ else goto go_up;
+ hpfs_error(s, "reference to anode %08x not found in anode %08x (probably bad up pointer)",
+ oano, level ? ano : -1);
+ if (level) brelse(bh);
+}
+
+/* Just a wrapper around hpfs_bplus_lookup .. used for reading eas */
+
+static secno anode_lookup(struct super_block *s, anode_secno a, unsigned sec)
+{
+ struct anode *anode;
+ struct buffer_head *bh;
+ if (!(anode = hpfs_map_anode(s, a, &bh))) return -1;
+ return hpfs_bplus_lookup(s, NULL, &anode->btree, sec, bh);
+}
+
+int hpfs_ea_read(struct super_block *s, secno a, int ano, unsigned pos,
+ unsigned len, char *buf)
+{
+ struct buffer_head *bh;
+ char *data;
+ secno sec;
+ unsigned l;
+ while (len) {
+ if (ano) {
+ if ((sec = anode_lookup(s, a, pos >> 9)) == -1)
+ return -1;
+ } else sec = a + (pos >> 9);
+ if (s->s_hpfs_chk) if (hpfs_chk_sectors(s, sec, 1, "ea #1")) return -1;
+ if (!(data = hpfs_map_sector(s, sec, &bh, (len - 1) >> 9)))
+ return -1;
+ l = 0x200 - (pos & 0x1ff); if (l > len) l = len;
+ memcpy(buf, data + (pos & 0x1ff), l);
+ brelse(bh);
+ buf += l; pos += l; len -= l;
+ }
+ return 0;
+}
+
+int hpfs_ea_write(struct super_block *s, secno a, int ano, unsigned pos,
+ unsigned len, char *buf)
+{
+ struct buffer_head *bh;
+ char *data;
+ secno sec;
+ unsigned l;
+ while (len) {
+ if (ano) {
+ if ((sec = anode_lookup(s, a, pos >> 9)) == -1)
+ return -1;
+ } else sec = a + (pos >> 9);
+ if (s->s_hpfs_chk) if (hpfs_chk_sectors(s, sec, 1, "ea #2")) return -1;
+ if (!(data = hpfs_map_sector(s, sec, &bh, (len - 1) >> 9)))
+ return -1;
+ l = 0x200 - (pos & 0x1ff); if (l > len) l = len;
+ memcpy(data + (pos & 0x1ff), buf, l);
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+ buf += l; pos += l; len -= l;
+ }
+ return 0;
+}
+
+void hpfs_ea_remove(struct super_block *s, secno a, int ano, unsigned len)
+{
+ struct anode *anode;
+ struct buffer_head *bh;
+ if (ano) {
+ if (!(anode = hpfs_map_anode(s, a, &bh))) return;
+ hpfs_remove_btree(s, &anode->btree);
+ brelse(bh);
+ hpfs_free_sectors(s, a, 1);
+ } else hpfs_free_sectors(s, a, (len + 511) >> 9);
+}
+
+/* Truncate allocation tree. Doesn't join anodes - I hope it doesn't matter */
+
+void hpfs_truncate_btree(struct super_block *s, secno f, int fno, unsigned secs)
+{
+ struct fnode *fnode;
+ struct anode *anode;
+ struct buffer_head *bh;
+ struct bplus_header *btree;
+ anode_secno node = f;
+ int i, j, nodes;
+ int c1, c2 = 0;
+ if (fno) {
+ if (!(fnode = hpfs_map_fnode(s, f, &bh))) return;
+ btree = &fnode->btree;
+ } else {
+ if (!(anode = hpfs_map_anode(s, f, &bh))) return;
+ btree = &anode->btree;
+ }
+ if (!secs) {
+ hpfs_remove_btree(s, btree);
+ if (fno) {
+ btree->n_free_nodes = 8;
+ btree->n_used_nodes = 0;
+ btree->first_free = 8;
+ btree->internal = 0;
+ mark_buffer_dirty(bh, 1);
+ } else hpfs_free_sectors(s, f, 1);
+ brelse(bh);
+ return;
+ }
+ while (btree->internal) {
+ nodes = btree->n_used_nodes + btree->n_free_nodes;
+ for (i = 0; i < btree->n_used_nodes; i++)
+ if (btree->u.internal[i].file_secno >= secs) goto f;
+ brelse(bh);
+ hpfs_error(s, "internal btree %08x doesn't end with -1", node);
+ return;
+ f:
+ for (j = i + 1; j < btree->n_used_nodes; j++)
+ hpfs_ea_remove(s, btree->u.internal[j].down, 1, 0);
+ btree->n_used_nodes = i + 1;
+ btree->n_free_nodes = nodes - btree->n_used_nodes;
+ btree->first_free = 8 + 8 * btree->n_used_nodes;
+ mark_buffer_dirty(bh, 1);
+ if (btree->u.internal[i].file_secno == secs) {
+ brelse(bh);
+ return;
+ }
+ node = btree->u.internal[i].down;
+ brelse(bh);
+ if (s->s_hpfs_chk)
+ if (hpfs_stop_cycles(s, node, &c1, &c2, "hpfs_truncate_btree"))
+ return;
+ if (!(anode = hpfs_map_anode(s, node, &bh))) return;
+ btree = &anode->btree;
+ }
+ nodes = btree->n_used_nodes + btree->n_free_nodes;
+ for (i = 0; i < btree->n_used_nodes; i++)
+ if (btree->u.external[i].file_secno + btree->u.external[i].length >= secs) goto ff;
+ brelse(bh);
+ return;
+ ff:
+ if (secs <= btree->u.external[i].file_secno) {
+ hpfs_error(s, "there is an allocation error in file %08x, sector %08x", f, secs);
+ if (i) i--;
+ }
+ else if (btree->u.external[i].file_secno + btree->u.external[i].length > secs) {
+ hpfs_free_sectors(s, btree->u.external[i].disk_secno + secs -
+ btree->u.external[i].file_secno, btree->u.external[i].length
+ - secs + btree->u.external[i].file_secno); /* I hope gcc optimizes this :-) */
+ btree->u.external[i].length = secs - btree->u.external[i].file_secno;
+ }
+ for (j = i + 1; j < btree->n_used_nodes; j++)
+ hpfs_free_sectors(s, btree->u.external[j].disk_secno, btree->u.external[j].length);
+ btree->n_used_nodes = i + 1;
+ btree->n_free_nodes = nodes - btree->n_used_nodes;
+ btree->first_free = 8 + 12 * btree->n_used_nodes;
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+}
+
+/* Remove file or directory and it's eas - note that directory must
+ be empty when this is called. */
+
+void hpfs_remove_fnode(struct super_block *s, fnode_secno fno)
+{
+ struct buffer_head *bh;
+ struct fnode *fnode;
+ struct extended_attribute *ea;
+ struct extended_attribute *ea_end;
+ if (!(fnode = hpfs_map_fnode(s, fno, &bh))) return;
+ if (!fnode->dirflag) hpfs_remove_btree(s, &fnode->btree);
+ else hpfs_remove_dtree(s, fnode->u.external[0].disk_secno);
+ ea_end = fnode_end_ea(fnode);
+ for (ea = fnode_ea(fnode); ea < ea_end; ea = next_ea(ea))
+ if (ea->indirect)
+ hpfs_ea_remove(s, ea_sec(ea), ea->anode, ea_len(ea));
+ hpfs_ea_ext_remove(s, fnode->ea_secno, fnode->ea_anode, fnode->ea_size_l);
+ brelse(bh);
+ hpfs_free_sectors(s, fno, 1);
+}
diff --git a/fs/hpfs/buffer.c b/fs/hpfs/buffer.c
new file mode 100644
index 000000000..8bcbf28fc
--- /dev/null
+++ b/fs/hpfs/buffer.c
@@ -0,0 +1,265 @@
+/*
+ * linux/fs/hpfs/buffer.c
+ *
+ * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
+ *
+ * general buffer i/o
+ */
+
+#include <linux/string.h>
+#include "hpfs_fn.h"
+
+void hpfs_lock_creation(struct super_block *s)
+{
+#ifdef DEBUG_LOCKS
+ printk("lock creation\n");
+#endif
+ while (s->s_hpfs_creation_de_lock) sleep_on(&s->s_hpfs_creation_de);
+ s->s_hpfs_creation_de_lock = 1;
+}
+
+void hpfs_unlock_creation(struct super_block *s)
+{
+#ifdef DEBUG_LOCKS
+ printk("unlock creation\n");
+#endif
+ s->s_hpfs_creation_de_lock = 0;
+ wake_up(&s->s_hpfs_creation_de);
+}
+
+void hpfs_lock_iget(struct super_block *s, int mode)
+{
+#ifdef DEBUG_LOCKS
+ printk("lock iget\n");
+#endif
+ while (s->s_hpfs_rd_inode) sleep_on(&s->s_hpfs_iget_q);
+ s->s_hpfs_rd_inode = mode;
+}
+
+void hpfs_unlock_iget(struct super_block *s)
+{
+#ifdef DEBUG_LOCKS
+ printk("unlock iget\n");
+#endif
+ s->s_hpfs_rd_inode = 0;
+ wake_up(&s->s_hpfs_iget_q);
+}
+
+void hpfs_lock_inode(struct inode *i)
+{
+ if (i) down(&i->i_hpfs_sem);
+}
+
+void hpfs_unlock_inode(struct inode *i)
+{
+ if (i) up(&i->i_hpfs_sem);
+}
+
+void hpfs_lock_2inodes(struct inode *i1, struct inode *i2)
+{
+ if (!i1) { if (i2) down(&i2->i_hpfs_sem); return; }
+ if (!i2) { if (i1) down(&i1->i_hpfs_sem); return; }
+ if (i1->i_ino < i2->i_ino) {
+ down(&i1->i_hpfs_sem);
+ down(&i2->i_hpfs_sem);
+ } else if (i1->i_ino > i2->i_ino) {
+ down(&i2->i_hpfs_sem);
+ down(&i1->i_hpfs_sem);
+ } else down(&i1->i_hpfs_sem);
+}
+
+void hpfs_unlock_2inodes(struct inode *i1, struct inode *i2)
+{
+ if (!i1) { if (i2) up(&i2->i_hpfs_sem); return; }
+ if (!i2) { if (i1) up(&i1->i_hpfs_sem); return; }
+ if (i1->i_ino < i2->i_ino) {
+ up(&i2->i_hpfs_sem);
+ up(&i1->i_hpfs_sem);
+ } else if (i1->i_ino > i2->i_ino) {
+ up(&i1->i_hpfs_sem);
+ up(&i2->i_hpfs_sem);
+ } else up(&i1->i_hpfs_sem);
+}
+
+void hpfs_lock_3inodes(struct inode *i1, struct inode *i2, struct inode *i3)
+{
+ if (!i1) { hpfs_lock_2inodes(i2, i3); return; }
+ if (!i2) { hpfs_lock_2inodes(i1, i3); return; }
+ if (!i3) { hpfs_lock_2inodes(i1, i2); return; }
+ if (i1->i_ino < i2->i_ino && i1->i_ino < i3->i_ino) {
+ down(&i1->i_hpfs_sem);
+ hpfs_lock_2inodes(i2, i3);
+ } else if (i2->i_ino < i1->i_ino && i2->i_ino < i3->i_ino) {
+ down(&i2->i_hpfs_sem);
+ hpfs_lock_2inodes(i1, i3);
+ } else if (i3->i_ino < i1->i_ino && i3->i_ino < i2->i_ino) {
+ down(&i3->i_hpfs_sem);
+ hpfs_lock_2inodes(i1, i2);
+ } else if (i1->i_ino != i2->i_ino) hpfs_lock_2inodes(i1, i2);
+ else hpfs_lock_2inodes(i1, i3);
+}
+
+void hpfs_unlock_3inodes(struct inode *i1, struct inode *i2, struct inode *i3)
+{
+ if (!i1) { hpfs_unlock_2inodes(i2, i3); return; }
+ if (!i2) { hpfs_unlock_2inodes(i1, i3); return; }
+ if (!i3) { hpfs_unlock_2inodes(i1, i2); return; }
+ if (i1->i_ino < i2->i_ino && i1->i_ino < i3->i_ino) {
+ hpfs_unlock_2inodes(i2, i3);
+ up(&i1->i_hpfs_sem);
+ } else if (i2->i_ino < i1->i_ino && i2->i_ino < i3->i_ino) {
+ hpfs_unlock_2inodes(i1, i3);
+ up(&i2->i_hpfs_sem);
+ } else if (i3->i_ino < i1->i_ino && i3->i_ino < i2->i_ino) {
+ hpfs_unlock_2inodes(i1, i2);
+ up(&i3->i_hpfs_sem);
+ } else if (i1->i_ino != i2->i_ino) hpfs_unlock_2inodes(i1, i2);
+ else hpfs_unlock_2inodes(i1, i3);
+}
+
+/* Map a sector into a buffer and return pointers to it and to the buffer. */
+
+void *hpfs_map_sector(struct super_block *s, unsigned secno, struct buffer_head **bhp,
+ int ahead)
+{
+ kdev_t dev = s->s_dev;
+ struct buffer_head *bh;
+
+ if (!ahead || secno + ahead >= s->s_hpfs_fs_size)
+ *bhp = bh = bread(dev, secno, 512);
+ else *bhp = bh = breada(dev, secno, 512, 0, (ahead + 1) << 9);
+ if (bh != NULL)
+ return bh->b_data;
+ else {
+ printk("HPFS: hpfs_map_sector: read error\n");
+ return NULL;
+ }
+}
+
+/* Like hpfs_map_sector but don't read anything */
+
+void *hpfs_get_sector(struct super_block *s, unsigned secno, struct buffer_head **bhp)
+{
+ struct buffer_head *bh;
+ /*return hpfs_map_sector(s, secno, bhp, 0);*/
+
+ if ((*bhp = bh = getblk(s->s_dev, secno, 512)) != NULL) {
+ mark_buffer_uptodate(bh, 1);
+ return bh->b_data;
+ } else {
+ printk("HPFS: hpfs_get_sector: getblk failed\n");
+ return NULL;
+ }
+}
+
+/* Map 4 sectors into a 4buffer and return pointers to it and to the buffer. */
+
+void *hpfs_map_4sectors(struct super_block *s, unsigned secno, struct quad_buffer_head *qbh,
+ int ahead)
+{
+ kdev_t dev = s->s_dev;
+ struct buffer_head *bh;
+ char *data;
+
+ if (secno & 3) {
+ printk("HPFS: hpfs_map_4sectors: unaligned read\n");
+ return 0;
+ }
+
+ qbh->data = data = (char *)kmalloc(2048, GFP_KERNEL);
+ if (!data) {
+ printk("HPFS: hpfs_map_4sectors: out of memory\n");
+ goto bail;
+ }
+
+ if (!ahead || secno + 4 + ahead > s->s_hpfs_fs_size)
+ qbh->bh[0] = bh = breada(dev, secno, 512, 0, 2048);
+ else qbh->bh[0] = bh = breada(dev, secno, 512, 0, (ahead + 4) << 9);
+ if (!bh)
+ goto bail0;
+ memcpy(data, bh->b_data, 512);
+
+ qbh->bh[1] = bh = bread(dev, secno + 1, 512);
+ if (!bh)
+ goto bail1;
+ memcpy(data + 512, bh->b_data, 512);
+
+ qbh->bh[2] = bh = bread(dev, secno + 2, 512);
+ if (!bh)
+ goto bail2;
+ memcpy(data + 2 * 512, bh->b_data, 512);
+
+ qbh->bh[3] = bh = bread(dev, secno + 3, 512);
+ if (!bh)
+ goto bail3;
+ memcpy(data + 3 * 512, bh->b_data, 512);
+
+ return data;
+
+ bail3:
+ brelse(qbh->bh[2]);
+ bail2:
+ brelse(qbh->bh[1]);
+ bail1:
+ brelse(qbh->bh[0]);
+ bail0:
+ kfree_s(data, 2048);
+ printk("HPFS: hpfs_map_4sectors: read error\n");
+ bail:
+ return NULL;
+}
+
+/* Don't read sectors */
+
+void *hpfs_get_4sectors(struct super_block *s, unsigned secno,
+ struct quad_buffer_head *qbh)
+{
+ if (secno & 3) {
+ printk("HPFS: hpfs_get_4sectors: unaligned read\n");
+ return 0;
+ }
+
+ /*return hpfs_map_4sectors(s, secno, qbh, 0);*/
+ if (!(qbh->data = kmalloc(2048, GFP_KERNEL))) {
+ printk("HPFS: hpfs_get_4sectors: out of memory\n");
+ return NULL;
+ }
+ if (!(hpfs_get_sector(s, secno, &qbh->bh[0]))) goto bail0;
+ if (!(hpfs_get_sector(s, secno + 1, &qbh->bh[1]))) goto bail1;
+ if (!(hpfs_get_sector(s, secno + 2, &qbh->bh[2]))) goto bail2;
+ if (!(hpfs_get_sector(s, secno + 3, &qbh->bh[3]))) goto bail3;
+ memcpy(qbh->data, qbh->bh[0]->b_data, 512);
+ memcpy(qbh->data + 512, qbh->bh[1]->b_data, 512);
+ memcpy(qbh->data + 2*512, qbh->bh[2]->b_data, 512);
+ memcpy(qbh->data + 3*512, qbh->bh[3]->b_data, 512);
+ return qbh->data;
+
+ bail3: brelse(qbh->bh[2]);
+ bail2: brelse(qbh->bh[1]);
+ bail1: brelse(qbh->bh[0]);
+ bail0:
+ return NULL;
+}
+
+
+void hpfs_brelse4(struct quad_buffer_head *qbh)
+{
+ brelse(qbh->bh[3]);
+ brelse(qbh->bh[2]);
+ brelse(qbh->bh[1]);
+ brelse(qbh->bh[0]);
+ kfree_s(qbh->data, 2048);
+}
+
+void hpfs_mark_4buffers_dirty(struct quad_buffer_head *qbh)
+{
+ PRINTK(("hpfs_mark_4buffers_dirty\n"));
+ memcpy(qbh->bh[0]->b_data, qbh->data, 512);
+ memcpy(qbh->bh[1]->b_data, qbh->data + 512, 512);
+ memcpy(qbh->bh[2]->b_data, qbh->data + 2 * 512, 512);
+ memcpy(qbh->bh[3]->b_data, qbh->data + 3 * 512, 512);
+ mark_buffer_dirty(qbh->bh[0],1);
+ mark_buffer_dirty(qbh->bh[1],1);
+ mark_buffer_dirty(qbh->bh[2],1);
+ mark_buffer_dirty(qbh->bh[3],1);
+}
diff --git a/fs/hpfs/dentry.c b/fs/hpfs/dentry.c
new file mode 100644
index 000000000..b5c3cf2e8
--- /dev/null
+++ b/fs/hpfs/dentry.c
@@ -0,0 +1,62 @@
+/*
+ * linux/fs/hpfs/dentry.c
+ *
+ * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
+ *
+ * dcache operations
+ */
+
+#include "hpfs_fn.h"
+
+/*
+ * Note: the dentry argument is the parent dentry.
+ */
+
+int hpfs_hash_dentry(struct dentry *dentry, struct qstr *qstr)
+{
+ unsigned long hash;
+ int i;
+ unsigned l = qstr->len;
+
+ if (l == 1) if (qstr->name[0]=='.') goto x;
+ if (l == 2) if (qstr->name[0]=='.' || qstr->name[1]=='.') goto x;
+ hpfs_adjust_length((char *)qstr->name, &l);
+ /*if (hpfs_chk_name((char *)qstr->name,&l))*/
+ /*return -ENAMETOOLONG;*/
+ /*return -ENOENT;*/
+ x:
+
+ hash = init_name_hash();
+ for (i = 0; i < l; i++)
+ hash = partial_name_hash(hpfs_upcase(dentry->d_sb->s_hpfs_cp_table,qstr->name[i]), hash);
+ qstr->hash = end_name_hash(hash);
+
+ return 0;
+}
+
+int hpfs_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b)
+{
+ unsigned al=a->len;
+ unsigned bl=b->len;
+ hpfs_adjust_length((char *)a->name, &al);
+ /*hpfs_adjust_length((char *)b->name, &bl);*/
+ /* 'a' is the qstr of an already existing dentry, so the name
+ * must be valid. 'b' must be validated first.
+ */
+
+ if (hpfs_chk_name((char *)b->name, &bl)) return 1;
+ if (hpfs_compare_names(dentry->d_sb, (char *)a->name, al, (char *)b->name, bl, 0)) return 1;
+ return 0;
+}
+
+struct dentry_operations hpfs_dentry_operations = {
+ NULL, /* d_validate */
+ hpfs_hash_dentry, /* d_hash */
+ hpfs_compare_dentry, /* d_compare */
+ NULL /* d_delete */
+};
+
+void hpfs_set_dentry_operations(struct dentry *dentry)
+{
+ dentry->d_op = &hpfs_dentry_operations;
+}
diff --git a/fs/hpfs/dir.c b/fs/hpfs/dir.c
new file mode 100644
index 000000000..8af35847d
--- /dev/null
+++ b/fs/hpfs/dir.c
@@ -0,0 +1,253 @@
+/*
+ * linux/fs/hpfs/dir.c
+ *
+ * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
+ *
+ * directory VFS functions
+ */
+
+#include "hpfs_fn.h"
+
+int hpfs_dir_read(struct file *filp, char *name, size_t len, loff_t *loff)
+{
+ return -EISDIR;
+}
+
+int hpfs_dir_release(struct inode *inode, struct file *filp)
+{
+ hpfs_del_pos(inode, &filp->f_pos);
+ /*hpfs_write_if_changed(inode);*/
+ return 0;
+}
+
+int hpfs_readdir(struct file *filp, void * dirent, filldir_t filldir)
+{
+ struct inode *inode = filp->f_dentry->d_inode;
+ struct quad_buffer_head qbh;
+ struct hpfs_dirent *de;
+ int lc;
+ long old_pos;
+ char *tempname;
+ int c1, c2 = 0;
+
+ if (inode->i_sb->s_hpfs_chk) {
+ if (hpfs_chk_sectors(inode->i_sb, inode->i_ino, 1, "dir_fnode"))
+ return -EFSERROR;
+ if (hpfs_chk_sectors(inode->i_sb, inode->i_hpfs_dno, 4, "dir_dnode"))
+ return -EFSERROR;
+ }
+ if (inode->i_sb->s_hpfs_chk >= 2) {
+ struct buffer_head *bh;
+ struct fnode *fno;
+ int e = 0;
+ if (!(fno = hpfs_map_fnode(inode->i_sb, inode->i_ino, &bh)))
+ return -EIOERROR;
+ if (!fno->dirflag) {
+ e = 1;
+ hpfs_error(inode->i_sb, "not a directory, fnode %08x",inode->i_ino);
+ }
+ if (inode->i_hpfs_dno != fno->u.external[0].disk_secno) {
+ e = 1;
+ hpfs_error(inode->i_sb, "corrupted inode: i_hpfs_dno == %08x, fnode -> dnode == %08x", inode->i_hpfs_dno, fno->u.external[0].disk_secno);
+ }
+ brelse(bh);
+ if (e) return -EFSERROR;
+ }
+ lc = inode->i_sb->s_hpfs_lowercase;
+ if (filp->f_pos == -2) { /* diff -r requires this (note, that diff -r */
+ filp->f_pos = -3; /* also fails on msdos filesystem in 2.0) */
+ return 0;
+ }
+ if (filp->f_pos == -3) return -ENOENT;
+
+ hpfs_lock_inode(inode);
+
+ while (1) {
+ again:
+ /* This won't work when cycle is longer than number of dirents
+ accepted by filldir, but what can I do?
+ maybe killall -9 ls helps */
+ if (inode->i_sb->s_hpfs_chk)
+ if (hpfs_stop_cycles(inode->i_sb, filp->f_pos, &c1, &c2, "hpfs_readdir")) {
+ hpfs_unlock_inode(inode);
+ return -EFSERROR;
+ }
+ if (filp->f_pos == -2) {
+ hpfs_unlock_inode(inode);
+ return 0;
+ }
+ if (filp->f_pos == 3 || filp->f_pos == 4 || filp->f_pos == 5) {
+ printk("HPFS: warning: pos==%d\n",(int)filp->f_pos);
+ hpfs_unlock_inode(inode);
+ return 0;
+ }
+ if (filp->f_pos == 0) {
+ if (filldir(dirent, ".", 1, filp->f_pos, inode->i_ino) < 0) {
+ hpfs_unlock_inode(inode);
+ return 0;
+ }
+ filp->f_pos = -1;
+ }
+ if (filp->f_pos == -1) {
+ if (filldir(dirent, "..", 2, filp->f_pos, inode->i_hpfs_parent_dir) < 0) {
+ hpfs_unlock_inode(inode);
+ return 0;
+ }
+ filp->f_pos = 1;
+ }
+ if (filp->f_pos == 1) {
+ filp->f_pos = ((loff_t) hpfs_de_as_down_as_possible(inode->i_sb, inode->i_hpfs_dno) << 4) + 1;
+ hpfs_add_pos(inode, &filp->f_pos);
+ filp->f_version = inode->i_version;
+ }
+ /*if (filp->f_version != inode->i_version) {
+ hpfs_unlock_inode(inode);
+ return -ENOENT;
+ }*/
+ old_pos = filp->f_pos;
+ if (!(de = map_pos_dirent(inode, &filp->f_pos, &qbh))) {
+ hpfs_unlock_inode(inode);
+ return -EIOERROR;
+ }
+ if (de->first || de->last) {
+ if (inode->i_sb->s_hpfs_chk) {
+ if (de->first && !de->last && (de->namelen != 2 || de ->name[0] != 1 || de->name[1] != 1)) hpfs_error(inode->i_sb, "hpfs_readdir: bad ^A^A entry; pos = %08x", old_pos);
+ if (de->last && (de->namelen != 1 || de ->name[0] != 255)) hpfs_error(inode->i_sb, "hpfs_readdir: bad \\377 entry; pos = %08x", old_pos);
+ }
+ hpfs_brelse4(&qbh);
+ goto again;
+ }
+ tempname = hpfs_translate_name(inode->i_sb, de->name, de->namelen, lc, de->not_8x3);
+ if (filldir(dirent, tempname, de->namelen, old_pos, de->fnode) < 0) {
+ filp->f_pos = old_pos;
+ if (tempname != (char *)de->name) kfree(tempname);
+ hpfs_brelse4(&qbh);
+ hpfs_unlock_inode(inode);
+ return 0;
+ }
+ if (tempname != (char *)de->name) kfree(tempname);
+ hpfs_brelse4(&qbh);
+ }
+}
+
+/*
+ * lookup. Search the specified directory for the specified name, set
+ * *result to the corresponding inode.
+ *
+ * lookup uses the inode number to tell read_inode whether it is reading
+ * the inode of a directory or a file -- file ino's are odd, directory
+ * ino's are even. read_inode avoids i/o for file inodes; everything
+ * needed is up here in the directory. (And file fnodes are out in
+ * the boondocks.)
+ *
+ * - M.P.: this is over, sometimes we've got to read file's fnode for eas
+ * inode numbers are just fnode sector numbers; iget lock is used
+ * to tell read_inode to read fnode or not.
+ */
+
+struct dentry *hpfs_lookup(struct inode *dir, struct dentry *dentry)
+{
+ const char *name = dentry->d_name.name;
+ unsigned len = dentry->d_name.len;
+ struct quad_buffer_head qbh;
+ struct hpfs_dirent *de;
+ ino_t ino;
+ int err;
+ struct inode *result = NULL;
+
+ if ((err = hpfs_chk_name((char *)name, &len))) {
+ if (err == -ENAMETOOLONG) return ERR_PTR(-ENAMETOOLONG);
+ goto end_add;
+ }
+
+ hpfs_lock_inode(dir);
+ /*
+ * '.' and '..' will never be passed here.
+ */
+
+ de = map_dirent(dir, dir->i_hpfs_dno, (char *) name, len, NULL, &qbh);
+
+ /*
+ * This is not really a bailout, just means file not found.
+ */
+
+ if (!de) goto end;
+
+ /*
+ * Get inode number, what we're after.
+ */
+
+ ino = de->fnode;
+
+ /*
+ * Go find or make an inode.
+ */
+
+ hpfs_lock_iget(dir->i_sb, de->directory || (de->ea_size && dir->i_sb->s_hpfs_eas) ? 1 : 2);
+ if (!(result = iget(dir->i_sb, ino))) {
+ hpfs_unlock_iget(dir->i_sb);
+ hpfs_error(result->i_sb, "hpfs_lookup: can't get inode");
+ goto bail1;
+ }
+ if (!de->directory) result->i_hpfs_parent_dir = dir->i_ino;
+ hpfs_unlock_iget(dir->i_sb);
+
+ hpfs_decide_conv(result, (char *)name, len);
+
+ if (de->has_acl || de->has_xtd_perm) if (!(dir->i_sb->s_flags & MS_RDONLY)) {
+ hpfs_error(result->i_sb, "ACLs or XPERM found. This is probably HPFS386. This driver doesn't support it now. Send me some info on these structures");
+ goto bail1;
+ }
+
+ /*
+ * Fill in the info from the directory if this is a newly created
+ * inode.
+ */
+
+ if (!result->i_ctime) {
+ if (!(result->i_ctime = local_to_gmt(dir->i_sb, de->creation_date)))
+ result->i_ctime = 1;
+ result->i_mtime = local_to_gmt(dir->i_sb, de->write_date);
+ result->i_atime = local_to_gmt(dir->i_sb, de->read_date);
+ result->i_hpfs_ea_size = de->ea_size;
+ if (!result->i_hpfs_ea_mode && de->read_only)
+ result->i_mode &= ~0222;
+ if (!de->directory) {
+ if (result->i_size == -1) {
+ result->i_size = de->file_size;
+ /*
+ * i_blocks should count the fnode and any anodes.
+ * We count 1 for the fnode and don't bother about
+ * anodes -- the disk heads are on the directory band
+ * and we want them to stay there.
+ */
+ result->i_blocks = 1 + ((result->i_size + 511) >> 9);
+ }
+ }
+ }
+
+ hpfs_brelse4(&qbh);
+
+ /*
+ * Made it.
+ */
+
+ end:
+ hpfs_unlock_inode(dir);
+ end_add:
+ hpfs_set_dentry_operations(dentry);
+ d_add(dentry, result);
+ return NULL;
+
+ /*
+ * Didn't.
+ */
+ bail1:
+
+ hpfs_brelse4(&qbh);
+
+ /*bail:*/
+
+ hpfs_unlock_inode(dir);
+ return ERR_PTR(-ENOENT);
+}
diff --git a/fs/hpfs/dnode.c b/fs/hpfs/dnode.c
new file mode 100644
index 000000000..e4b4bbc91
--- /dev/null
+++ b/fs/hpfs/dnode.c
@@ -0,0 +1,1070 @@
+/*
+ * linux/fs/hpfs/dnode.c
+ *
+ * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
+ *
+ * handling directory dnode tree - adding, deleteing & searching for dirents
+ */
+
+#include "hpfs_fn.h"
+
+static loff_t get_pos(struct dnode *d, struct hpfs_dirent *fde)
+{
+ struct hpfs_dirent *de;
+ struct hpfs_dirent *de_end = dnode_end_de(d);
+ int i = 1;
+ for (de = dnode_first_de(d); de < de_end; de = de_next_de(de)) {
+ if (de == fde) return ((loff_t) d->self << 4) | (loff_t)i;
+ i++;
+ }
+ printk("HPFS: get_pos: not_found\n");
+ return ((loff_t)d->self << 4) | (loff_t)1;
+}
+
+void hpfs_add_pos(struct inode *inode, loff_t *pos)
+{
+ int i = 0;
+ loff_t **ppos;
+ if (inode->i_hpfs_rddir_off)
+ for (; inode->i_hpfs_rddir_off[i]; i++)
+ if (inode->i_hpfs_rddir_off[i] == pos) return;
+ if (!(i&0x0f)) {
+ if (!(ppos = kmalloc((i+0x11) * sizeof(loff_t*), GFP_KERNEL))) {
+ printk("HPFS: out of memory for position list\n");
+ return;
+ }
+ if (inode->i_hpfs_rddir_off) {
+ memcpy(ppos, inode->i_hpfs_rddir_off, i * sizeof(loff_t));
+ kfree(inode->i_hpfs_rddir_off);
+ }
+ inode->i_hpfs_rddir_off = ppos;
+ }
+ inode->i_hpfs_rddir_off[i] = pos;
+ inode->i_hpfs_rddir_off[i + 1] = NULL;
+}
+
+void hpfs_del_pos(struct inode *inode, loff_t *pos)
+{
+ loff_t **i, **j;
+ if (!inode->i_hpfs_rddir_off) goto not_f;
+ for (i = inode->i_hpfs_rddir_off; *i; i++) if (*i == pos) goto fnd;
+ goto not_f;
+ fnd:
+ for (j = i + 1; *j; j++) ;
+ *i = *(j - 1);
+ *(j - 1) = NULL;
+ if (j - 1 == inode->i_hpfs_rddir_off) {
+ kfree(inode->i_hpfs_rddir_off);
+ inode->i_hpfs_rddir_off = NULL;
+ }
+ return;
+ not_f:
+ /*printk("HPFS: warning: position pointer %p->%08x not found\n", pos, (int)*pos);*/
+ return;
+}
+
+static void for_all_poss(struct inode *inode, void (*f)(loff_t *, loff_t, loff_t),
+ loff_t p1, loff_t p2)
+{
+ loff_t **i;
+ if (!inode->i_hpfs_rddir_off) return;
+ for (i = inode->i_hpfs_rddir_off; *i; i++) (*f)(*i, p1, p2);
+ return;
+}
+
+void hpfs_pos_subst(loff_t *p, loff_t f, loff_t t)
+{
+ if (*p == f) *p = t;
+}
+
+/*void hpfs_hpfs_pos_substd(loff_t *p, loff_t f, loff_t t)
+{
+ if ((*p & ~0x3f) == (f & ~0x3f)) *p = (t & ~0x3f) | (*p & 0x3f);
+}*/
+
+void hpfs_pos_ins(loff_t *p, loff_t d, loff_t c)
+{
+ if ((*p & ~0x3f) == (d & ~0x3f) && (*p & 0x3f) >= (d & 0x3f)) {
+ int n = (*p & 0x3f) + c;
+ if (n > 0x3f) printk("HPFS: hpfs_pos_ins: %08x + %d\n", (int)*p, (int)c >> 8);
+ else *p = (*p & ~0x3f) | n;
+ }
+}
+
+void hpfs_pos_del(loff_t *p, loff_t d, loff_t c)
+{
+ if ((*p & ~0x3f) == (d & ~0x3f) && (*p & 0x3f) >= (d & 0x3f)) {
+ int n = (*p & 0x3f) - c;
+ if (n < 1) printk("HPFS: hpfs_pos_ins: %08x - %d\n", (int)*p, (int)c >> 8);
+ else *p = (*p & ~0x3f) | n;
+ }
+}
+
+static struct hpfs_dirent *dnode_pre_last_de(struct dnode *d)
+{
+ struct hpfs_dirent *de, *de_end, *dee = NULL, *deee = NULL;
+ de_end = dnode_end_de(d);
+ for (de = dnode_first_de(d); de < de_end; de = de_next_de(de)) {
+ deee = dee; dee = de;
+ }
+ return deee;
+}
+
+static struct hpfs_dirent *dnode_last_de(struct dnode *d)
+{
+ struct hpfs_dirent *de, *de_end, *dee = NULL;
+ de_end = dnode_end_de(d);
+ for (de = dnode_first_de(d); de < de_end; de = de_next_de(de)) {
+ dee = de;
+ }
+ return dee;
+}
+
+static void set_last_pointer(struct super_block *s, struct dnode *d, dnode_secno ptr)
+{
+ struct hpfs_dirent *de;
+ if (!(de = dnode_last_de(d))) {
+ hpfs_error(s, "set_last_pointer: empty dnode %08x", d->self);
+ return;
+ }
+ if (s->s_hpfs_chk) {
+ if (de->down) {
+ hpfs_error(s, "set_last_pointer: dnode %08x has already last pointer %08x",
+ d->self, de_down_pointer(de));
+ return;
+ }
+ if (de->length != 32) {
+ hpfs_error(s, "set_last_pointer: bad last dirent in dnode %08x", d->self);
+ return;
+ }
+ }
+ if (ptr) {
+ if ((d->first_free += 4) > 2048) {
+ hpfs_error(s,"set_last_pointer: too long dnode %08x", d->self);
+ d->first_free -= 4;
+ return;
+ }
+ de->length = 36;
+ de->down = 1;
+ *(dnode_secno *)((char *)de + 32) = ptr;
+ }
+}
+
+/* Add an entry to dnode and don't care if it grows over 2048 bytes */
+
+struct hpfs_dirent *hpfs_add_de(struct super_block *s, struct dnode *d, unsigned char *name,
+ unsigned namelen, secno down_ptr)
+{
+ struct hpfs_dirent *de;
+ struct hpfs_dirent *de_end = dnode_end_de(d);
+ unsigned d_size = de_size(namelen, down_ptr);
+ for (de = dnode_first_de(d); de < de_end; de = de_next_de(de)) {
+ int c = hpfs_compare_names(s, name, namelen, de->name, de->namelen, de->last);
+ if (!c) {
+ hpfs_error(s, "name (%c,%d) already exists in dnode %08x", *name, namelen, d->self);
+ return NULL;
+ }
+ if (c < 0) break;
+ }
+ memmove((char *)de + d_size, de, (char *)de_end - (char *)de);
+ memset(de, 0, d_size);
+ if (down_ptr) {
+ *(int *)((char *)de + d_size - 4) = down_ptr;
+ de->down = 1;
+ }
+ de->length = d_size;
+ if (down_ptr) de->down = 1;
+ de->not_8x3 = hpfs_is_name_long(name, namelen);
+ de->namelen = namelen;
+ memcpy(de->name, name, namelen);
+ d->first_free += d_size;
+ return de;
+}
+
+/* Delete dirent and don't care about it's subtree */
+
+void hpfs_delete_de(struct super_block *s, struct dnode *d, struct hpfs_dirent *de)
+{
+ if (de->last) {
+ hpfs_error(s, "attempt to delete last dirent in dnode %08x", d->self);
+ return;
+ }
+ d->first_free -= de->length;
+ memmove(de, de_next_de(de), d->first_free + (char *)d - (char *)de);
+}
+
+static void fix_up_ptrs(struct super_block *s, struct dnode *d)
+{
+ struct hpfs_dirent *de;
+ struct hpfs_dirent *de_end = dnode_end_de(d);
+ dnode_secno dno = d->self;
+ for (de = dnode_first_de(d); de < de_end; de = de_next_de(de))
+ if (de->down) {
+ struct quad_buffer_head qbh;
+ struct dnode *dd;
+ if ((dd = hpfs_map_dnode(s, de_down_pointer(de), &qbh))) {
+ if (dd->up != dno || dd->root_dnode) {
+ dd->up = dno;
+ dd->root_dnode = 0;
+ hpfs_mark_4buffers_dirty(&qbh);
+ }
+ hpfs_brelse4(&qbh);
+ }
+ }
+}
+
+/* Add an entry to dnode and do dnode splitting if required */
+
+int hpfs_add_to_dnode(struct inode *i, dnode_secno dno, unsigned char *name, unsigned namelen,
+ struct hpfs_dirent *new_de, dnode_secno down_ptr)
+{
+ struct quad_buffer_head qbh, qbh1, qbh2;
+ struct dnode *d, *ad, *rd, *nd = NULL;
+ dnode_secno adno, rdno;
+ struct hpfs_dirent *de;
+ struct hpfs_dirent nde;
+ char *nname;
+ int h;
+ int pos;
+ struct buffer_head *bh;
+ struct fnode *fnode;
+ int c1, c2 = 0;
+ if (!(nname = kmalloc(256, GFP_KERNEL))) {
+ printk("HPFS: out of memory, can't add to dnode\n");
+ return 1;
+ }
+ go_up:
+ if (namelen >= 256) {
+ hpfs_error(i->i_sb, "hpfs_add_to_dnode: namelen == %d", namelen);
+ if (nd) kfree(nd);
+ kfree(nname);
+ return 1;
+ }
+ if (!(d = hpfs_map_dnode(i->i_sb, dno, &qbh))) {
+ if (nd) kfree(nd);
+ kfree(nname);
+ return 1;
+ }
+ go_up_a:
+ if (i->i_sb->s_hpfs_chk)
+ if (hpfs_stop_cycles(i->i_sb, dno, &c1, &c2, "hpfs_add_to_dnode")) {
+ hpfs_brelse4(&qbh);
+ if (nd) kfree(nd);
+ kfree(nname);
+ return 1;
+ }
+ if (d->first_free + de_size(namelen, down_ptr) <= 2048) {
+ loff_t t;
+ copy_de(de=hpfs_add_de(i->i_sb, d, name, namelen, down_ptr), new_de);
+ t = get_pos(d, de);
+ for_all_poss(i, hpfs_pos_ins, t, 1);
+ for_all_poss(i, hpfs_pos_subst, 4, t);
+ for_all_poss(i, hpfs_pos_subst, 5, t + 1);
+ hpfs_mark_4buffers_dirty(&qbh);
+ hpfs_brelse4(&qbh);
+ if (nd) kfree(nd);
+ kfree(nname);
+ return 0;
+ }
+ if (!nd) if (!(nd = kmalloc(0x924, GFP_KERNEL))) {
+ /* 0x924 is a max size of dnode after adding a dirent with
+ max name length. We alloc this only once. There must
+ not be any error while splitting dnodes, otherwise the
+ whole directory, not only file we're adding, would
+ be lost. */
+ printk("HPFS: out of memory for dnode splitting\n");
+ hpfs_brelse4(&qbh);
+ kfree(nname);
+ return 1;
+ }
+ memcpy(nd, d, d->first_free);
+ copy_de(de = hpfs_add_de(i->i_sb, nd, name, namelen, down_ptr), new_de);
+ for_all_poss(i, hpfs_pos_ins, get_pos(nd, de), 1);
+ h = ((char *)dnode_last_de(nd) - (char *)nd) / 2 + 10;
+ if (!(ad = hpfs_alloc_dnode(i->i_sb, d->up, &adno, &qbh1, 0))) {
+ hpfs_error(i->i_sb, "unable to alloc dnode - dnode tree will be corrupted");
+ hpfs_brelse4(&qbh);
+ kfree(nd);
+ kfree(nname);
+ return 1;
+ }
+ i->i_size += 2048;
+ i->i_blocks += 4;
+ pos = 1;
+ for (de = dnode_first_de(nd); (char *)de_next_de(de) - (char *)nd < h; de = de_next_de(de)) {
+ copy_de(hpfs_add_de(i->i_sb, ad, de->name, de->namelen, de->down ? de_down_pointer(de) : 0), de);
+ for_all_poss(i, hpfs_pos_subst, ((loff_t)dno << 4) | pos, ((loff_t)adno << 4) | pos);
+ pos++;
+ }
+ copy_de(new_de = &nde, de);
+ memcpy(name = nname, de->name, namelen = de->namelen);
+ for_all_poss(i, hpfs_pos_subst, ((loff_t)dno << 4) | pos, 4);
+ down_ptr = adno;
+ set_last_pointer(i->i_sb, ad, de->down ? de_down_pointer(de) : 0);
+ de = de_next_de(de);
+ memmove((char *)nd + 20, de, nd->first_free + (char *)nd - (char *)de);
+ nd->first_free -= (char *)de - (char *)nd - 20;
+ memcpy(d, nd, nd->first_free);
+ for_all_poss(i, hpfs_pos_del, (loff_t)dno << 4, pos);
+ fix_up_ptrs(i->i_sb, ad);
+ if (!d->root_dnode) {
+ dno = ad->up = d->up;
+ hpfs_mark_4buffers_dirty(&qbh);
+ hpfs_brelse4(&qbh);
+ hpfs_mark_4buffers_dirty(&qbh1);
+ hpfs_brelse4(&qbh1);
+ goto go_up;
+ }
+ if (!(rd = hpfs_alloc_dnode(i->i_sb, d->up, &rdno, &qbh2, 0))) {
+ hpfs_error(i->i_sb, "unable to alloc dnode - dnode tree will be corrupted");
+ hpfs_brelse4(&qbh);
+ hpfs_brelse4(&qbh1);
+ kfree(nd);
+ kfree(nname);
+ return 1;
+ }
+ i->i_size += 2048;
+ i->i_blocks += 4;
+ rd->root_dnode = 1;
+ rd->up = d->up;
+ if (!(fnode = hpfs_map_fnode(i->i_sb, d->up, &bh))) {
+ hpfs_free_dnode(i->i_sb, rdno);
+ hpfs_brelse4(&qbh);
+ hpfs_brelse4(&qbh1);
+ hpfs_brelse4(&qbh2);
+ kfree(nd);
+ kfree(nname);
+ return 1;
+ }
+ fnode->u.external[0].disk_secno = rdno;
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+ d->up = ad->up = i->i_hpfs_dno = rdno;
+ d->root_dnode = ad->root_dnode = 0;
+ hpfs_mark_4buffers_dirty(&qbh);
+ hpfs_brelse4(&qbh);
+ hpfs_mark_4buffers_dirty(&qbh1);
+ hpfs_brelse4(&qbh1);
+ qbh = qbh2;
+ set_last_pointer(i->i_sb, rd, dno);
+ dno = rdno;
+ d = rd;
+ goto go_up_a;
+}
+
+/*
+ * Add an entry to directory btree.
+ * I hate such crazy directory structure.
+ * It's easy to read but terrible to write.
+ * I wrote this directory code 4 times.
+ * I hope, now it's finally bug-free.
+ */
+
+int hpfs_add_dirent(struct inode *i, unsigned char *name, unsigned namelen,
+ struct hpfs_dirent *new_de, int cdepth)
+{
+ struct dnode *d;
+ struct hpfs_dirent *de, *de_end;
+ struct quad_buffer_head qbh;
+ dnode_secno dno;
+ int c;
+ int c1, c2 = 0;
+ dno = i->i_hpfs_dno;
+ down:
+ if (i->i_sb->s_hpfs_chk)
+ if (hpfs_stop_cycles(i->i_sb, dno, &c1, &c2, "hpfs_add_dirent")) return 1;
+ if (!(d = hpfs_map_dnode(i->i_sb, dno, &qbh))) return 1;
+ de_end = dnode_end_de(d);
+ for (de = dnode_first_de(d); de < de_end; de = de_next_de(de)) {
+ if (!(c = hpfs_compare_names(i->i_sb, name, namelen, de->name, de->namelen, de->last))) {
+ hpfs_brelse4(&qbh);
+ return -1;
+ }
+ if (c < 0) {
+ if (de->down) {
+ dno = de_down_pointer(de);
+ hpfs_brelse4(&qbh);
+ goto down;
+ }
+ break;
+ }
+ }
+ hpfs_brelse4(&qbh);
+ if (!cdepth) hpfs_lock_creation(i->i_sb);
+ if (hpfs_check_free_dnodes(i->i_sb, FREE_DNODES_ADD)) {
+ c = 1;
+ goto ret;
+ }
+ i->i_version = ++event;
+ c = hpfs_add_to_dnode(i, dno, name, namelen, new_de, 0);
+ ret:
+ if (!cdepth) hpfs_unlock_creation(i->i_sb);
+ return c;
+}
+
+/*
+ * Find dirent with higher name in 'from' subtree and move it to 'to' dnode.
+ * Return the dnode we moved from (to be checked later if it's empty)
+ */
+
+static secno move_to_top(struct inode *i, dnode_secno from, dnode_secno to)
+{
+ dnode_secno dno, ddno;
+ dnode_secno chk_up = to;
+ struct dnode *dnode;
+ struct quad_buffer_head qbh;
+ struct hpfs_dirent *de, *nde;
+ int a;
+ loff_t t;
+ int c1, c2 = 0;
+ dno = from;
+ while (1) {
+ if (i->i_sb->s_hpfs_chk)
+ if (hpfs_stop_cycles(i->i_sb, dno, &c1, &c2, "move_to_top"))
+ return 0;
+ if (!(dnode = hpfs_map_dnode(i->i_sb, dno, &qbh))) return 0;
+ if (i->i_sb->s_hpfs_chk) {
+ if (dnode->up != chk_up) {
+ hpfs_error(i->i_sb, "move_to_top: up pointer from %08x should be %08x, is %08x",
+ dno, chk_up, dnode->up);
+ hpfs_brelse4(&qbh);
+ return 0;
+ }
+ chk_up = dno;
+ }
+ if (!(de = dnode_last_de(dnode))) {
+ hpfs_error(i->i_sb, "move_to_top: dnode %08x has no last de", dno);
+ hpfs_brelse4(&qbh);
+ return 0;
+ }
+ if (!de->down) break;
+ dno = de_down_pointer(de);
+ hpfs_brelse4(&qbh);
+ }
+ while (!(de = dnode_pre_last_de(dnode))) {
+ dnode_secno up = dnode->up;
+ hpfs_brelse4(&qbh);
+ hpfs_free_dnode(i->i_sb, dno);
+ i->i_size -= 2048;
+ i->i_blocks -= 4;
+ for_all_poss(i, hpfs_pos_subst, ((loff_t)dno << 4) | 1, 5);
+ if (up == to) return to;
+ if (!(dnode = hpfs_map_dnode(i->i_sb, up, &qbh))) return 0;
+ if (dnode->root_dnode) {
+ hpfs_error(i->i_sb, "move_to_top: got to root_dnode while moving from %08x to %08x", from, to);
+ hpfs_brelse4(&qbh);
+ return 0;
+ }
+ de = dnode_last_de(dnode);
+ if (!de || !de->down) {
+ hpfs_error(i->i_sb, "move_to_top: dnode %08x doesn't point down to %08x", up, dno);
+ hpfs_brelse4(&qbh);
+ return 0;
+ }
+ dnode->first_free -= 4;
+ de->length -= 4;
+ de->down = 0;
+ hpfs_mark_4buffers_dirty(&qbh);
+ dno = up;
+ }
+ t = get_pos(dnode, de);
+ for_all_poss(i, hpfs_pos_subst, t, 4);
+ for_all_poss(i, hpfs_pos_subst, t + 1, 5);
+ if (!(nde = kmalloc(de->length, GFP_KERNEL))) {
+ hpfs_error(i->i_sb, "out of memory for dirent - directory will be corrupted");
+ hpfs_brelse4(&qbh);
+ return 0;
+ }
+ memcpy(nde, de, de->length);
+ ddno = de->down ? de_down_pointer(de) : 0;
+ hpfs_delete_de(i->i_sb, dnode, de);
+ set_last_pointer(i->i_sb, dnode, ddno);
+ hpfs_mark_4buffers_dirty(&qbh);
+ hpfs_brelse4(&qbh);
+ a = hpfs_add_to_dnode(i, to, nde->name, nde->namelen, nde, from);
+ kfree(nde);
+ if (a) return 0;
+ return dno;
+}
+
+/*
+ * Check if a dnode is empty and delete it from the tree
+ * (chkdsk doesn't like empty dnodes)
+ */
+
+static void delete_empty_dnode(struct inode *i, dnode_secno dno)
+{
+ struct quad_buffer_head qbh;
+ struct dnode *dnode;
+ dnode_secno down, up, ndown;
+ int p;
+ struct hpfs_dirent *de;
+ int c1, c2 = 0;
+ try_it_again:
+ if (hpfs_stop_cycles(i->i_sb, dno, &c1, &c2, "delete_empty_dnode")) return;
+ if (!(dnode = hpfs_map_dnode(i->i_sb, dno, &qbh))) return;
+ if (dnode->first_free > 56) goto end;
+ if (dnode->first_free == 52 || dnode->first_free == 56) {
+ struct hpfs_dirent *de_end;
+ int root = dnode->root_dnode;
+ up = dnode->up;
+ de = dnode_first_de(dnode);
+ down = de->down ? de_down_pointer(de) : 0;
+ if (i->i_sb->s_hpfs_chk) if (root && !down) {
+ hpfs_error(i->i_sb, "delete_empty_dnode: root dnode %08x is empty", dno);
+ goto end;
+ }
+ hpfs_brelse4(&qbh);
+ hpfs_free_dnode(i->i_sb, dno);
+ i->i_size -= 2048;
+ i->i_blocks -= 4;
+ if (root) {
+ struct fnode *fnode;
+ struct buffer_head *bh;
+ struct dnode *d1;
+ struct quad_buffer_head qbh1;
+ if (i->i_sb->s_hpfs_chk) if (up != i->i_ino) {
+ hpfs_error(i->i_sb, "bad pointer to fnode, dnode %08x, pointing to %08x, should be %08x", dno, up, i->i_ino);
+ return;
+ }
+ if ((d1 = hpfs_map_dnode(i->i_sb, down, &qbh1))) {
+ d1->up = up;
+ d1->root_dnode = 1;
+ hpfs_mark_4buffers_dirty(&qbh1);
+ hpfs_brelse4(&qbh1);
+ }
+ if ((fnode = hpfs_map_fnode(i->i_sb, up, &bh))) {
+ fnode->u.external[0].disk_secno = down;
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+ }
+ i->i_hpfs_dno = down;
+ for_all_poss(i, hpfs_pos_subst, ((loff_t)dno << 4) | 1, (loff_t) -2);
+ return;
+ }
+ if (!(dnode = hpfs_map_dnode(i->i_sb, up, &qbh))) return;
+ p = 1;
+ de_end = dnode_end_de(dnode);
+ for (de = dnode_first_de(dnode); de < de_end; de = de_next_de(de), p++)
+ if (de->down) if (de_down_pointer(de) == dno) goto fnd;
+ hpfs_error(i->i_sb, "delete_empty_dnode: pointer to dnode %08x not found in dnode %08x", dno, up);
+ goto end;
+ fnd:
+ for_all_poss(i, hpfs_pos_subst, ((loff_t)dno << 4) | 1, ((loff_t)up << 4) | p);
+ if (!down) {
+ de->down = 0;
+ de->length -= 4;
+ dnode->first_free -= 4;
+ memmove(de_next_de(de), (char *)de_next_de(de) + 4,
+ (char *)dnode + dnode->first_free - (char *)de_next_de(de));
+ } else {
+ struct dnode *d1;
+ struct quad_buffer_head qbh1;
+ *(dnode_secno *) ((void *) de + de->length - 4) = down;
+ if ((d1 = hpfs_map_dnode(i->i_sb, down, &qbh1))) {
+ d1->up = up;
+ hpfs_mark_4buffers_dirty(&qbh1);
+ hpfs_brelse4(&qbh1);
+ }
+ }
+ } else {
+ hpfs_error(i->i_sb, "delete_empty_dnode: dnode %08x, first_free == %03x", dno, dnode->first_free);
+ goto end;
+ }
+
+ if (!de->last) {
+ struct hpfs_dirent *de_next = de_next_de(de);
+ struct hpfs_dirent *de_cp;
+ struct dnode *d1;
+ struct quad_buffer_head qbh1;
+ if (!de_next->down) goto endm;
+ ndown = de_down_pointer(de_next);
+ if (!(de_cp = kmalloc(de->length, GFP_KERNEL))) {
+ printk("HPFS: out of memory for dtree balancing\n");
+ goto endm;
+ }
+ memcpy(de_cp, de, de->length);
+ hpfs_delete_de(i->i_sb, dnode, de);
+ hpfs_mark_4buffers_dirty(&qbh);
+ hpfs_brelse4(&qbh);
+ for_all_poss(i, hpfs_pos_subst, ((loff_t)up << 4) | p, 4);
+ for_all_poss(i, hpfs_pos_del, ((loff_t)up << 4) | p, 1);
+ if (de_cp->down) if ((d1 = hpfs_map_dnode(i->i_sb, de_down_pointer(de_cp), &qbh1))) {
+ d1->up = ndown;
+ hpfs_mark_4buffers_dirty(&qbh1);
+ hpfs_brelse4(&qbh1);
+ }
+ hpfs_add_to_dnode(i, ndown, de_cp->name, de_cp->namelen, de_cp, de_cp->down ? de_down_pointer(de_cp) : 0);
+ /*printk("UP-TO-DNODE: %08x (ndown = %08x, down = %08x, dno = %08x)\n", up, ndown, down, dno);*/
+ dno = up;
+ kfree(de_cp);
+ goto try_it_again;
+ } else {
+ struct hpfs_dirent *de_prev = dnode_pre_last_de(dnode);
+ struct hpfs_dirent *de_cp;
+ struct dnode *d1;
+ struct quad_buffer_head qbh1;
+ dnode_secno dlp;
+ if (!de_prev) {
+ hpfs_error(i->i_sb, "delete_empty_dnode: empty dnode %08x", up);
+ hpfs_mark_4buffers_dirty(&qbh);
+ hpfs_brelse4(&qbh);
+ dno = up;
+ goto try_it_again;
+ }
+ if (!de_prev->down) goto endm;
+ ndown = de_down_pointer(de_prev);
+ if ((d1 = hpfs_map_dnode(i->i_sb, ndown, &qbh1))) {
+ struct hpfs_dirent *del = dnode_last_de(d1);
+ dlp = del->down ? de_down_pointer(del) : 0;
+ if (!dlp && down) {
+ if (d1->first_free > 2044) {
+ if (i->i_sb->s_hpfs_chk >= 2) {
+ printk("HPFS: warning: unbalanced dnode tree, see hpfs.txt 4 more info\n");
+ printk("HPFS: warning: terminating balancing operation\n");
+ }
+ hpfs_brelse4(&qbh1);
+ goto endm;
+ }
+ if (i->i_sb->s_hpfs_chk >= 2) {
+ printk("HPFS: warning: unbalanced dnode tree, see hpfs.txt 4 more info\n");
+ printk("HPFS: warning: goin'on\n");
+ }
+ del->length += 4;
+ del->down = 1;
+ d1->first_free += 4;
+ }
+ if (dlp && !down) {
+ del->length -= 4;
+ del->down = 0;
+ d1->first_free -= 4;
+ } else if (down)
+ *(dnode_secno *) ((void *) del + del->length - 4) = down;
+ } else goto endm;
+ if (!(de_cp = kmalloc(de_prev->length, GFP_KERNEL))) {
+ printk("HPFS: out of memory for dtree balancing\n");
+ hpfs_brelse4(&qbh1);
+ goto endm;
+ }
+ hpfs_mark_4buffers_dirty(&qbh1);
+ hpfs_brelse4(&qbh1);
+ memcpy(de_cp, de_prev, de_prev->length);
+ hpfs_delete_de(i->i_sb, dnode, de_prev);
+ if (!de_prev->down) {
+ de_prev->length += 4;
+ de_prev->down = 1;
+ dnode->first_free += 4;
+ }
+ *(dnode_secno *) ((void *) de_prev + de_prev->length - 4) = ndown;
+ hpfs_mark_4buffers_dirty(&qbh);
+ hpfs_brelse4(&qbh);
+ for_all_poss(i, hpfs_pos_subst, ((loff_t)up << 4) | (p - 1), 4);
+ for_all_poss(i, hpfs_pos_subst, ((loff_t)up << 4) | p, ((loff_t)up << 4) | (p - 1));
+ if (down) if ((d1 = hpfs_map_dnode(i->i_sb, de_down_pointer(de), &qbh1))) {
+ d1->up = ndown;
+ hpfs_mark_4buffers_dirty(&qbh1);
+ hpfs_brelse4(&qbh1);
+ }
+ hpfs_add_to_dnode(i, ndown, de_cp->name, de_cp->namelen, de_cp, dlp);
+ dno = up;
+ kfree(de_cp);
+ goto try_it_again;
+ }
+ endm:
+ hpfs_mark_4buffers_dirty(&qbh);
+ end:
+ hpfs_brelse4(&qbh);
+}
+
+
+/* Delete dirent from directory */
+
+int hpfs_remove_dirent(struct inode *i, dnode_secno dno, struct hpfs_dirent *de,
+ struct quad_buffer_head *qbh, int depth)
+{
+ struct dnode *dnode = qbh->data;
+ dnode_secno down = 0;
+ int lock = 0;
+ loff_t t;
+ if (de->first || de->last) {
+ hpfs_error(i->i_sb, "hpfs_remove_dirent: attempt to delete first or last dirent in dnode %08x", dno);
+ hpfs_brelse4(qbh);
+ return 1;
+ }
+ if (de->down) down = de_down_pointer(de);
+ if (depth && (de->down || (de == dnode_first_de(dnode) && de_next_de(de)->last))) {
+ lock = 1;
+ hpfs_lock_creation(i->i_sb);
+ if (hpfs_check_free_dnodes(i->i_sb, FREE_DNODES_DEL)) {
+ hpfs_brelse4(qbh);
+ hpfs_unlock_creation(i->i_sb);
+ return 2;
+ }
+ }
+ i->i_version = ++event;
+ for_all_poss(i, hpfs_pos_del, (t = get_pos(dnode, de)) + 1, 1);
+ hpfs_delete_de(i->i_sb, dnode, de);
+ hpfs_mark_4buffers_dirty(qbh);
+ hpfs_brelse4(qbh);
+ if (down) {
+ dnode_secno a = move_to_top(i, down, dno);
+ for_all_poss(i, hpfs_pos_subst, 5, t);
+ if (a) delete_empty_dnode(i, a);
+ if (lock) hpfs_unlock_creation(i->i_sb);
+ return !a;
+ }
+ delete_empty_dnode(i, dno);
+ if (lock) hpfs_unlock_creation(i->i_sb);
+ return 0;
+}
+
+void hpfs_count_dnodes(struct super_block *s, dnode_secno dno, int *n_dnodes,
+ int *n_subdirs, int *n_items)
+{
+ struct dnode *dnode;
+ struct quad_buffer_head qbh;
+ struct hpfs_dirent *de;
+ dnode_secno ptr, odno = 0;
+ int c1, c2 = 0;
+ int d1, d2 = 0;
+ go_down:
+ if (n_dnodes) (*n_dnodes)++;
+ if (s->s_hpfs_chk)
+ if (hpfs_stop_cycles(s, dno, &c1, &c2, "hpfs_count_dnodes #1")) return;
+ ptr = 0;
+ go_up:
+ if (!(dnode = hpfs_map_dnode(s, dno, &qbh))) return;
+ if (s->s_hpfs_chk) if (odno && odno != -1 && dnode->up != odno)
+ hpfs_error(s, "hpfs_count_dnodes: bad up pointer; dnode %08x, down %08x points to %08x", odno, dno, dnode->up);
+ de = dnode_first_de(dnode);
+ if (ptr) while(1) {
+ if (de->down) if (de_down_pointer(de) == ptr) goto process_de;
+ if (de->last) {
+ hpfs_brelse4(&qbh);
+ hpfs_error(s, "hpfs_count_dnodes: pointer to dnode %08x not found in dnode %08x, got here from %08x",
+ ptr, dno, odno);
+ return;
+ }
+ de = de_next_de(de);
+ }
+ next_de:
+ if (de->down) {
+ odno = dno;
+ dno = de_down_pointer(de);
+ hpfs_brelse4(&qbh);
+ goto go_down;
+ }
+ process_de:
+ if (!de->first && !de->last && de->directory && n_subdirs) (*n_subdirs)++;
+ if (!de->first && !de->last && n_items) (*n_items)++;
+ if ((de = de_next_de(de)) < dnode_end_de(dnode)) goto next_de;
+ ptr = dno;
+ dno = dnode->up;
+ if (dnode->root_dnode) {
+ hpfs_brelse4(&qbh);
+ return;
+ }
+ hpfs_brelse4(&qbh);
+ if (s->s_hpfs_chk)
+ if (hpfs_stop_cycles(s, ptr, &d1, &d2, "hpfs_count_dnodes #2")) return;
+ odno = -1;
+ goto go_up;
+}
+
+static struct hpfs_dirent *map_nth_dirent(struct super_block *s, dnode_secno dno, int n,
+ struct quad_buffer_head *qbh, struct dnode **dn)
+{
+ int i;
+ struct hpfs_dirent *de, *de_end;
+ struct dnode *dnode;
+ dnode = hpfs_map_dnode(s, dno, qbh);
+ if (!dnode) return NULL;
+ if (dn) *dn=dnode;
+ de = dnode_first_de(dnode);
+ de_end = dnode_end_de(dnode);
+ for (i = 1; de < de_end; i++, de = de_next_de(de)) {
+ if (i == n) {
+ return de;
+ }
+ if (de->last) break;
+ }
+ hpfs_brelse4(qbh);
+ hpfs_error(s, "map_nth_dirent: n too high; dnode = %08x, requested %08x", dno, n);
+ return NULL;
+}
+
+dnode_secno hpfs_de_as_down_as_possible(struct super_block *s, dnode_secno dno)
+{
+ struct quad_buffer_head qbh;
+ dnode_secno d = dno;
+ dnode_secno up = 0;
+ struct hpfs_dirent *de;
+ int c1, c2 = 0;
+
+ again:
+ if (s->s_hpfs_chk)
+ if (hpfs_stop_cycles(s, d, &c1, &c2, "hpfs_de_as_down_as_possible"))
+ return d;
+ if (!(de = map_nth_dirent(s, d, 1, &qbh, NULL))) return dno;
+ if (s->s_hpfs_chk)
+ if (up && ((struct dnode *)qbh.data)->up != up)
+ hpfs_error(s, "hpfs_de_as_down_as_possible: bad up pointer; dnode %08x, down %08x points to %08x", up, d, ((struct dnode *)qbh.data)->up);
+ if (!de->down) {
+ hpfs_brelse4(&qbh);
+ return d;
+ }
+ up = d;
+ d = de_down_pointer(de);
+ hpfs_brelse4(&qbh);
+ goto again;
+}
+
+struct hpfs_dirent *map_pos_dirent(struct inode *inode, loff_t *posp,
+ struct quad_buffer_head *qbh)
+{
+ loff_t pos;
+ unsigned c;
+ dnode_secno dno;
+ struct hpfs_dirent *de, *d;
+ struct hpfs_dirent *up_de;
+ struct hpfs_dirent *end_up_de;
+ struct dnode *dnode;
+ struct dnode *up_dnode;
+ struct quad_buffer_head qbh0;
+
+ pos = *posp;
+ dno = pos >> 6 << 2;
+ pos &= 077;
+ if (!(de = map_nth_dirent(inode->i_sb, dno, pos, qbh, &dnode)))
+ goto bail;
+
+ /* Going to the next dirent */
+ if ((d = de_next_de(de)) < dnode_end_de(dnode)) {
+ if (!(++*posp & 077)) {
+ hpfs_error(inode->i_sb, "map_pos_dirent: pos crossed dnode boundary; pos = %08x", *posp);
+ goto bail;
+ }
+ /* We're going down the tree */
+ if (d->down) {
+ *posp = ((loff_t) hpfs_de_as_down_as_possible(inode->i_sb, de_down_pointer(d)) << 4) + 1;
+ }
+
+ return de;
+ }
+
+ /* Going up */
+ if (dnode->root_dnode) goto bail;
+
+ if (!(up_dnode = hpfs_map_dnode(inode->i_sb, dnode->up, &qbh0)))
+ goto bail;
+
+ end_up_de = dnode_end_de(up_dnode);
+ c = 0;
+ for (up_de = dnode_first_de(up_dnode); up_de < end_up_de;
+ up_de = de_next_de(up_de)) {
+ if (!(++c & 077)) hpfs_error(inode->i_sb,
+ "map_pos_dirent: pos crossed dnode boundary; dnode = %08x", dnode->up);
+ if (up_de->down && de_down_pointer(up_de) == dno) {
+ *posp = ((loff_t) dnode->up << 4) + c;
+ hpfs_brelse4(&qbh0);
+ return de;
+ }
+ }
+
+ hpfs_error(inode->i_sb, "map_pos_dirent: pointer to dnode %08x not found in parent dnode %08x",
+ dno, dnode->up);
+ hpfs_brelse4(&qbh0);
+
+ bail:
+ *posp = -2;
+ return de;
+}
+
+/* Find a dirent in tree */
+
+struct hpfs_dirent *map_dirent(struct inode *inode, dnode_secno dno, char *name, unsigned len,
+ dnode_secno *dd, struct quad_buffer_head *qbh)
+{
+ struct dnode *dnode;
+ struct hpfs_dirent *de;
+ struct hpfs_dirent *de_end;
+ int c1, c2 = 0;
+
+ if (!S_ISDIR(inode->i_mode)) hpfs_error(inode->i_sb, "map_dirent: not a directory\n");
+ again:
+ if (inode->i_sb->s_hpfs_chk)
+ if (hpfs_stop_cycles(inode->i_sb, dno, &c1, &c2, "map_dirent")) return NULL;
+ if (!(dnode = hpfs_map_dnode(inode->i_sb, dno, qbh))) return NULL;
+
+ de_end = dnode_end_de(dnode);
+ for (de = dnode_first_de(dnode); de < de_end; de = de_next_de(de)) {
+ int t = hpfs_compare_names(inode->i_sb, name, len, de->name, de->namelen, de->last);
+ if (!t) {
+ if (dd) *dd = dno;
+ return de;
+ }
+ if (t < 0) {
+ if (de->down) {
+ dno = de_down_pointer(de);
+ hpfs_brelse4(qbh);
+ goto again;
+ }
+ break;
+ }
+ }
+ hpfs_brelse4(qbh);
+ return NULL;
+}
+
+/*
+ * Remove empty directory. In normal cases it is only one dnode with two
+ * entries, but we must handle also such obscure cases when it's a tree
+ * of empty dnodes.
+ */
+
+void hpfs_remove_dtree(struct super_block *s, dnode_secno dno)
+{
+ struct quad_buffer_head qbh;
+ struct dnode *dnode;
+ struct hpfs_dirent *de;
+ dnode_secno d1, d2, rdno = dno;
+ while (1) {
+ if (!(dnode = hpfs_map_dnode(s, dno, &qbh))) return;
+ de = dnode_first_de(dnode);
+ if (de->last) {
+ if (de->down) d1 = de_down_pointer(de);
+ else goto error;
+ hpfs_brelse4(&qbh);
+ hpfs_free_dnode(s, dno);
+ dno = d1;
+ } else break;
+ }
+ if (!de->first) goto error;
+ d1 = de->down ? de_down_pointer(de) : 0;
+ de = de_next_de(de);
+ if (!de->last) goto error;
+ d2 = de->down ? de_down_pointer(de) : 0;
+ hpfs_brelse4(&qbh);
+ hpfs_free_dnode(s, dno);
+ do {
+ while (d1) {
+ if (!(dnode = hpfs_map_dnode(s, dno = d1, &qbh))) return;
+ de = dnode_first_de(dnode);
+ if (!de->last) goto error;
+ d1 = de->down ? de_down_pointer(de) : 0;
+ hpfs_brelse4(&qbh);
+ hpfs_free_dnode(s, dno);
+ }
+ d1 = d2;
+ d2 = 0;
+ } while (d1);
+ return;
+ error:
+ hpfs_brelse4(&qbh);
+ hpfs_free_dnode(s, dno);
+ hpfs_error(s, "directory %08x is corrupted or not empty", rdno);
+}
+
+/*
+ * Find dirent for specified fnode. Use truncated 15-char name in fnode as
+ * a help for searching.
+ */
+
+struct hpfs_dirent *map_fnode_dirent(struct super_block *s, fnode_secno fno,
+ struct fnode *f, struct quad_buffer_head *qbh)
+{
+ char *name1;
+ char *name2;
+ int name1len, name2len;
+ struct dnode *d;
+ dnode_secno dno, downd;
+ struct fnode *upf;
+ struct buffer_head *bh;
+ struct hpfs_dirent *de, *de_end;
+ int c;
+ int c1, c2 = 0;
+ int d1, d2 = 0;
+ name1 = f->name;
+ if (!(name2 = kmalloc(256, GFP_KERNEL))) {
+ printk("HPFS: out of memory, can't map dirent\n");
+ return NULL;
+ }
+ if (f->len <= 15)
+ memcpy(name2, name1, name1len = name2len = f->len);
+ else {
+ memcpy(name2, name1, 15);
+ memset(name2 + 15, 0xff, 256 - 15);
+ /*name2[15] = 0xff;*/
+ name1len = 15; name2len = 256;
+ }
+ if (!(upf = hpfs_map_fnode(s, f->up, &bh))) {
+ kfree(name2);
+ return NULL;
+ }
+ if (!upf->dirflag) {
+ brelse(bh);
+ hpfs_error(s, "fnode %08x has non-directory parent %08x", fno, f->up);
+ kfree(name2);
+ return NULL;
+ }
+ dno = upf->u.external[0].disk_secno;
+ brelse(bh);
+ go_down:
+ downd = 0;
+ go_up:
+ if (!(d = hpfs_map_dnode(s, dno, qbh))) {
+ kfree(name2);
+ return NULL;
+ }
+ de_end = dnode_end_de(d);
+ de = dnode_first_de(d);
+ if (downd) {
+ while (de < de_end) {
+ if (de->down) if (de_down_pointer(de) == downd) goto f;
+ de = de_next_de(de);
+ }
+ hpfs_error(s, "pointer to dnode %08x not found in dnode %08x", downd, dno);
+ hpfs_brelse4(qbh);
+ kfree(name2);
+ return NULL;
+ }
+ next_de:
+ if (de->fnode == fno) {
+ kfree(name2);
+ return de;
+ }
+ c = hpfs_compare_names(s, name1, name1len, de->name, de->namelen, de->last);
+ if (c < 0 && de->down) {
+ dno = de_down_pointer(de);
+ hpfs_brelse4(qbh);
+ if (s->s_hpfs_chk)
+ if (hpfs_stop_cycles(s, dno, &c1, &c2, "map_fnode_dirent #1")) {
+ kfree(name2);
+ return NULL;
+ }
+ goto go_down;
+ }
+ f:
+ if (de->fnode == fno) {
+ kfree(name2);
+ return de;
+ }
+ c = hpfs_compare_names(s, name2, name2len, de->name, de->namelen, de->last);
+ if (c < 0 && !de->last) goto not_found;
+ if ((de = de_next_de(de)) < de_end) goto next_de;
+ if (d->root_dnode) goto not_found;
+ downd = dno;
+ dno = d->up;
+ hpfs_brelse4(qbh);
+ if (s->s_hpfs_chk)
+ if (hpfs_stop_cycles(s, downd, &d1, &d2, "map_fnode_dirent #2")) {
+ kfree(name2);
+ return NULL;
+ }
+ goto go_up;
+ not_found:
+ hpfs_brelse4(qbh);
+ hpfs_error(s, "dirent for fnode %08x not found", fno);
+ kfree(name2);
+ return NULL;
+}
diff --git a/fs/hpfs/ea.c b/fs/hpfs/ea.c
new file mode 100644
index 000000000..6dac91fd8
--- /dev/null
+++ b/fs/hpfs/ea.c
@@ -0,0 +1,305 @@
+/*
+ * linux/fs/hpfs/ea.c
+ *
+ * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
+ *
+ * handling extended attributes
+ */
+
+#include <linux/string.h>
+#include "hpfs_fn.h"
+
+/* Remove external extended attributes. ano specifies wheter a is a
+ direct sector where eas start or an anode */
+
+void hpfs_ea_ext_remove(struct super_block *s, secno a, int ano, unsigned len)
+{
+ unsigned pos = 0;
+ while (pos < len) {
+ char ex[4 + 255 + 1 + 8];
+ struct extended_attribute *ea = (struct extended_attribute *)ex;
+ if (pos + 4 > len) {
+ hpfs_error(s, "EAs don't end correctly, %s %08x, len %08x",
+ ano ? "anode" : "sectors", a, len);
+ return;
+ }
+ if (hpfs_ea_read(s, a, ano, pos, 4, ex)) return;
+ if (ea->indirect) {
+ if (ea->valuelen != 8) {
+ hpfs_error(s, "ea->indirect set while ea->valuelen!=8, %s %08x, pos %08x",
+ ano ? "anode" : "sectors", a, pos);
+ return;
+ }
+ if (hpfs_ea_read(s, a, ano, pos + 4, ea->namelen + 9, ex+4))
+ return;
+ hpfs_ea_remove(s, ea_sec(ea), ea->anode, ea_len(ea));
+ }
+ pos += ea->namelen + ea->valuelen + 5;
+ }
+ if (!ano) hpfs_free_sectors(s, a, (len+511) >> 9);
+ else {
+ struct buffer_head *bh;
+ struct anode *anode;
+ if ((anode = hpfs_map_anode(s, a, &bh))) {
+ hpfs_remove_btree(s, &anode->btree);
+ brelse(bh);
+ hpfs_free_sectors(s, a, 1);
+ }
+ }
+}
+
+static char *get_indirect_ea(struct super_block *s, int ano, secno a, int size)
+{
+ char *ret;
+ if (!(ret = kmalloc(size + 1, GFP_KERNEL))) {
+ printk("HPFS: out of memory for EA\n");
+ return NULL;
+ }
+ if (hpfs_ea_read(s, a, ano, 0, size, ret)) {
+ kfree(ret);
+ return NULL;
+ }
+ ret[size] = 0;
+ return ret;
+}
+
+static void set_indirect_ea(struct super_block *s, int ano, secno a, char *data,
+ int size)
+{
+ hpfs_ea_write(s, a, ano, 0, size, data);
+}
+
+/* Read an extended attribute named 'key' */
+
+char *hpfs_get_ea(struct super_block *s, struct fnode *fnode, char *key, int *size)
+{
+ char *ret;
+ unsigned pos;
+ int ano, len;
+ secno a;
+ struct extended_attribute *ea;
+ struct extended_attribute *ea_end = fnode_end_ea(fnode);
+ for (ea = fnode_ea(fnode); ea < ea_end; ea = next_ea(ea))
+ if (!strcmp(ea->name, key)) {
+ if (ea->indirect)
+ return get_indirect_ea(s, ea->anode, ea_sec(ea), *size = ea_len(ea));
+ if (!(ret = kmalloc((*size = ea->valuelen) + 1, GFP_KERNEL))) {
+ printk("HPFS: out of memory for EA\n");
+ return NULL;
+ }
+ memcpy(ret, ea_data(ea), ea->valuelen);
+ ret[ea->valuelen] = 0;
+ return ret;
+ }
+ a = fnode->ea_secno;
+ len = fnode->ea_size_l;
+ ano = fnode->ea_anode;
+ pos = 0;
+ while (pos < len) {
+ char ex[4 + 255 + 1 + 8];
+ ea = (struct extended_attribute *)ex;
+ if (pos + 4 > len) {
+ hpfs_error(s, "EAs don't end correctly, %s %08x, len %08x",
+ ano ? "anode" : "sectors", a, len);
+ return NULL;
+ }
+ if (hpfs_ea_read(s, a, ano, pos, 4, ex)) return NULL;
+ if (hpfs_ea_read(s, a, ano, pos + 4, ea->namelen + 1 + (ea->indirect ? 8 : 0), ex + 4))
+ return NULL;
+ if (!strcmp(ea->name, key)) {
+ if (ea->indirect)
+ return get_indirect_ea(s, ea->anode, ea_sec(ea), *size = ea_len(ea));
+ if (!(ret = kmalloc((*size = ea->valuelen) + 1, GFP_KERNEL))) {
+ printk("HPFS: out of memory for EA\n");
+ return NULL;
+ }
+ if (hpfs_ea_read(s, a, ano, pos + 4 + ea->namelen + 1, ea->valuelen, ret)) {
+ kfree(ret);
+ return NULL;
+ }
+ ret[ea->valuelen] = 0;
+ return ret;
+ }
+ pos += ea->namelen + ea->valuelen + 5;
+ }
+ return NULL;
+}
+
+/*
+ * Update or create extended attribute 'key' with value 'data'. Note that
+ * when this ea exists, it MUST have the same size as size of data.
+ * This driver can't change sizes of eas ('cause I just don't need it).
+ */
+
+void hpfs_set_ea(struct inode *inode, struct fnode *fnode, char *key, char *data, int size)
+{
+ fnode_secno fno = inode->i_ino;
+ struct super_block *s = inode->i_sb;
+ unsigned pos;
+ int ano, len;
+ secno a;
+ unsigned char h[4];
+ struct extended_attribute *ea;
+ struct extended_attribute *ea_end = fnode_end_ea(fnode);
+ for (ea = fnode_ea(fnode); ea < ea_end; ea = next_ea(ea))
+ if (!strcmp(ea->name, key)) {
+ if (ea->indirect) {
+ if (ea_len(ea) == size)
+ set_indirect_ea(s, ea->anode, ea_sec(ea), data, size);
+ } else if (ea->valuelen == size) {
+ memcpy(ea_data(ea), data, size);
+ }
+ return;
+ }
+ a = fnode->ea_secno;
+ len = fnode->ea_size_l;
+ ano = fnode->ea_anode;
+ pos = 0;
+ while (pos < len) {
+ char ex[4 + 255 + 1 + 8];
+ ea = (struct extended_attribute *)ex;
+ if (pos + 4 > len) {
+ hpfs_error(s, "EAs don't end correctly, %s %08x, len %08x",
+ ano ? "anode" : "sectors", a, len);
+ return;
+ }
+ if (hpfs_ea_read(s, a, ano, pos, 4, ex)) return;
+ if (hpfs_ea_read(s, a, ano, pos + 4, ea->namelen + 1 + (ea->indirect ? 8 : 0), ex + 4))
+ return;
+ if (!strcmp(ea->name, key)) {
+ if (ea->indirect) {
+ if (ea_len(ea) == size)
+ set_indirect_ea(s, ea->anode, ea_sec(ea), data, size);
+ }
+ else {
+ if (ea->valuelen == size)
+ hpfs_ea_write(s, a, ano, pos + 4 + ea->namelen + 1, size, data);
+ }
+ return;
+ }
+ pos += ea->namelen + ea->valuelen + 5;
+ }
+ if (!fnode->ea_size_s) {
+ /*if (fnode->ea_size_s) {
+ hpfs_error(s, "fnode %08x: ea_size_s == %03x, ea_offs == 0",
+ inode->i_ino, fnode->ea_size_s);
+ return;
+ }*/
+ fnode->ea_offs = 0xc4;
+ }
+ if (fnode->ea_offs < 0xc4 || fnode->ea_offs + fnode->ea_size_s > 0x200) {
+ hpfs_error(s, "fnode %08x: ea_offs == %03x, ea_size_s == %03x",
+ inode->i_ino, fnode->ea_offs, fnode->ea_size_s);
+ return;
+ }
+ if ((fnode->ea_size_s || !fnode->ea_size_l) &&
+ fnode->ea_offs + fnode->ea_size_s + strlen(key) + size + 5 <= 0x200) {
+ /* I'm not sure ... maybe we overwrite ACL here. I have no info
+ on it right now :-( */
+ ea = fnode_end_ea(fnode);
+ *(char *)ea = 0;
+ ea->namelen = strlen(key);
+ ea->valuelen = size;
+ strcpy(ea->name, key);
+ memcpy(ea_data(ea), data, size);
+ fnode->ea_size_s += strlen(key) + size + 5;
+ goto ret;
+ }
+ /* Most the code here is 99.9993422% unused. I hope there are no bugs.
+ But what .. HPFS.IFS has also bugs in ea management. */
+ if (fnode->ea_size_s && !fnode->ea_size_l) {
+ secno n;
+ struct buffer_head *bh;
+ char *data;
+ if (!(n = hpfs_alloc_sector(s, fno, 1, 0, 1))) return;
+ if (!(data = hpfs_get_sector(s, n, &bh))) {
+ hpfs_free_sectors(s, n, 1);
+ return;
+ }
+ memcpy(data, fnode_ea(fnode), fnode->ea_size_s);
+ fnode->ea_size_l = fnode->ea_size_s;
+ fnode->ea_size_s = 0;
+ fnode->ea_secno = n;
+ fnode->ea_anode = 0;
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+ }
+ pos = fnode->ea_size_l + 5 + strlen(key) + size;
+ len = (fnode->ea_size_l + 511) >> 9;
+ if (pos >= 30000) goto bail;
+ while (((pos + 511) >> 9) > len) {
+ if (!len) {
+ if (!(fnode->ea_secno = hpfs_alloc_sector(s, fno, 1, 0, 1)))
+ goto bail;
+ fnode->ea_anode = 0;
+ len++;
+ }
+ else if (!fnode->ea_anode)
+ if (hpfs_alloc_if_possible(s, fnode->ea_secno + len)) len++;
+ else {
+ /* Aargh... don't know how to create ea anodes :-( */
+ /*struct buffer_head *bh;
+ struct anode *anode;
+ anode_secno a_s;
+ if (!(anode = hpfs_alloc_anode(s, fno, &a_s, &bh)))
+ goto bail;
+ anode->up = fno;
+ anode->btree.fnode_parent = 1;
+ anode->btree.n_free_nodes--;
+ anode->btree.n_used_nodes++;
+ anode->btree.first_free += 12;
+ anode->u.external[0].disk_secno = fnode->ea_secno;
+ anode->u.external[0].file_secno = 0;
+ anode->u.external[0].length = len;
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+ fnode->ea_anode = 1;
+ fnode->ea_secno = a_s;*/
+ secno new_sec;
+ int i;
+ if (!(new_sec = hpfs_alloc_sector(s, fno, 1, 1 - ((pos + 511) >> 9), 1)))
+ goto bail;
+ for (i = 0; i < len; i++) {
+ struct buffer_head *bh1, *bh2;
+ void *b1, *b2;
+ if (!(b1 = hpfs_map_sector(s, fnode->ea_secno + i, &bh1, len - i - 1))) {
+ hpfs_free_sectors(s, new_sec, (pos + 511) >> 9);
+ goto bail;
+ }
+ if (!(b2 = hpfs_get_sector(s, new_sec + i, &bh2))) {
+ brelse(bh1);
+ hpfs_free_sectors(s, new_sec, (pos + 511) >> 9);
+ goto bail;
+ }
+ memcpy(b2, b1, 512);
+ brelse(bh1);
+ mark_buffer_dirty(bh2, 1);
+ brelse(bh2);
+ }
+ hpfs_free_sectors(s, fnode->ea_secno, len);
+ fnode->ea_secno = new_sec;
+ len = (pos + 511) >> 9;
+ }
+ if (fnode->ea_anode)
+ if (hpfs_add_sector_to_btree(s, fnode->ea_secno, 0, len) != -1)
+ len++;
+ else goto bail;
+ }
+ h[0] = 0;
+ h[1] = strlen(key);
+ h[2] = size & 0xff;
+ h[3] = size >> 8;
+ if (hpfs_ea_write(s, fnode->ea_secno, fnode->ea_anode, fnode->ea_size_l, 4, h)) goto bail;
+ if (hpfs_ea_write(s, fnode->ea_secno, fnode->ea_anode, fnode->ea_size_l + 4, h[1] + 1, key)) goto bail;
+ if (hpfs_ea_write(s, fnode->ea_secno, fnode->ea_anode, fnode->ea_size_l + 5 + h[1], size, data)) goto bail;
+ fnode->ea_size_l = pos;
+ ret:
+ inode->i_hpfs_ea_size += 5 + strlen(key) + size;
+ return;
+ bail:
+ if (fnode->ea_secno)
+ if (fnode->ea_anode) hpfs_truncate_btree(s, fnode->ea_secno, 1, (fnode->ea_size_l + 511) >> 9);
+ else hpfs_free_sectors(s, fnode->ea_secno + ((fnode->ea_size_l + 511) >> 9), len - ((fnode->ea_size_l + 511) >> 9));
+ else fnode->ea_secno = fnode->ea_size_l = 0;
+}
+
diff --git a/fs/hpfs/file.c b/fs/hpfs/file.c
new file mode 100644
index 000000000..7cea8dcdc
--- /dev/null
+++ b/fs/hpfs/file.c
@@ -0,0 +1,195 @@
+/*
+ * linux/fs/hpfs/file.c
+ *
+ * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
+ *
+ * file VFS functions
+ */
+
+#include <linux/string.h>
+#include "hpfs_fn.h"
+
+int hpfs_open(struct inode *i, struct file *f)
+{
+ hpfs_lock_inode(i);
+ hpfs_unlock_inode(i); /* make sure nobody is deleting the file */
+ if (!i->i_nlink) return -ENOENT;
+ return 0;
+}
+
+int hpfs_file_release(struct inode *inode, struct file *file)
+{
+ hpfs_write_if_changed(inode);
+ return 0;
+}
+
+int hpfs_file_fsync(struct file *file, struct dentry *dentry)
+{
+ /*return file_fsync(file, dentry);*/
+ return 0; /* Don't fsync :-) */
+}
+
+/*
+ * generic_file_read often calls bmap with non-existing sector,
+ * so we must ignore such errors.
+ */
+
+secno hpfs_bmap(struct inode *inode, unsigned file_secno)
+{
+ unsigned n, disk_secno;
+ struct fnode *fnode;
+ struct buffer_head *bh;
+ if (((inode->i_size + 511) >> 9) <= file_secno) return 0;
+ n = file_secno - inode->i_hpfs_file_sec;
+ if (n < inode->i_hpfs_n_secs) return inode->i_hpfs_disk_sec + n;
+ if (!(fnode = hpfs_map_fnode(inode->i_sb, inode->i_ino, &bh))) return 0;
+ disk_secno = hpfs_bplus_lookup(inode->i_sb, inode, &fnode->btree, file_secno, bh);
+ if (disk_secno == -1) return 0;
+ if (hpfs_chk_sectors(inode->i_sb, disk_secno, 1, "bmap")) return 0;
+ return disk_secno;
+}
+
+void hpfs_truncate(struct inode *i)
+{
+ if (IS_IMMUTABLE(i)) return /*-EPERM*/;
+ i->i_hpfs_n_secs = 0;
+ hpfs_truncate_btree(i->i_sb, i->i_ino, 1, ((i->i_size + 511) >> 9));
+ i->i_blocks = 1 + ((i->i_size + 511) >> 9);
+ hpfs_write_inode(i);
+}
+
+ssize_t hpfs_file_read(struct file *filp, char *buf, size_t count, loff_t *ppos)
+{
+ struct inode *inode = filp->f_dentry->d_inode;
+ int i,j;
+ int a = generic_file_read(filp, buf, count, ppos);
+ if (inode->i_hpfs_conv != CONV_TEXT || a < 0) {
+ return a;
+ }
+ for (i = 0, j = 0; i < a; i++) {
+ char c;
+ int error;
+ if ((error = get_user(c, buf + i))) return error;
+ if (c != '\r') {
+ if (i != j) put_user(c, buf + j);
+ j++;
+ }
+ }
+ return j;
+}
+
+ssize_t hpfs_file_write(struct file *filp, const char *buf, size_t count,
+ loff_t *ppos)
+{
+ struct inode *i = filp->f_dentry->d_inode;
+ int carry, error = 0;
+ const char *start = buf;
+ if (!i) return -EINVAL;
+ if (!S_ISREG(i->i_mode)) return -EINVAL;
+ if (IS_IMMUTABLE(i)) return -EPERM;
+ if (filp->f_flags & O_APPEND) *ppos = i->i_size;
+ if (count <= 0) return 0;
+ if ((unsigned)(*ppos+count) >= 0x80000000U || (unsigned)count >= 0x80000000U) return -EFBIG;
+ carry = 0;
+ while (count || carry) {
+ int ii, add = 0;
+ secno sec = 0; /* Go away, uninitialized variable warning */
+ int offset, size, written;
+ char ch;
+ struct buffer_head *bh;
+ char *data;
+ offset = *ppos & 0x1ff;
+ size = count > 0x200 - offset ? 0x200 - offset : count;
+ if ((*ppos >> 9) < ((i->i_size + 0x1ff) >> 9)) {
+ i->i_hpfs_n_secs = 0;
+ if (!(sec = hpfs_bmap(i, *ppos >> 9))) {
+ hpfs_error(i->i_sb, "bmap failed, file %08x, fsec %08x",
+ i->i_ino, *ppos >> 9);
+ error =- EFSERROR;
+ break;
+ }
+ } else for (ii = (i->i_size + 0x1ff) >> 9, add = 1; ii <= *ppos >> 9; ii++) {
+ if ((sec = hpfs_add_sector_to_btree(i->i_sb, i->i_ino, 1, ii)) == -1) {
+ hpfs_truncate(i);
+ return -ENOSPC;
+ }
+ if (*ppos != i->i_size)
+ if ((data = hpfs_get_sector(i->i_sb, sec, &bh))) {
+ memset(data, 0, 512);
+ mark_buffer_dirty(bh, 0);
+ brelse(bh);
+ }
+ i->i_size = 0x200 * ii + 1;
+ i->i_blocks++;
+ /*mark_inode_dirty(i);*/i->i_hpfs_dirty = 1;
+ if (i->i_sb->s_hpfs_chk >= 2) {
+ secno bsec;
+ bsec = hpfs_bmap(i, ii);
+ if (sec != bsec) {
+ hpfs_error(i->i_sb, "sec == %08x, bmap returns %08x", sec, bsec);
+ error = -EFSERROR;
+ break;
+ }
+ }
+ PRINTK(("file_write: added %08x\n", sec));
+ }
+ if (!sec || sec == 15) {
+ hpfs_error(i->i_sb, "bmap returned empty sector");
+ error = -EFSERROR;
+ break;
+ }
+ if (i->i_sb->s_hpfs_chk)
+ if (hpfs_chk_sectors(i->i_sb, sec, 1, "data")) {
+ error = -EFSERROR;
+ break;
+ }
+ if ((!offset && size == 0x200) || add)
+ data = hpfs_get_sector(i->i_sb, sec, &bh);
+ else data = hpfs_map_sector(i->i_sb, sec, &bh, 0);
+ if (!data) {
+ error = -EIO;
+ break;
+ }
+ if (i->i_hpfs_conv != CONV_TEXT) {
+ memcpy_fromfs(data + offset, buf, written = size);
+ buf += size;
+ } else {
+ int left;
+ char *to;
+ /* LF->CR/LF conversion, stolen from fat fs */
+ written = left = 0x200 - offset;
+ to = (char *) bh->b_data + (*ppos & 0x1ff);
+ if (carry) {
+ *to++ = '\n';
+ left--;
+ carry = 0;
+ }
+ for (size = 0; size < count && left; size++) {
+ if ((error = get_user(ch, buf++))) break;
+ if (ch == '\n') {
+ *to++ = '\r';
+ left--;
+ }
+ if (!left) carry = 1;
+ else {
+ *to++ = ch;
+ left--;
+ }
+ }
+ written -= left;
+ }
+ update_vm_cache(i, *ppos, bh->b_data + (*ppos & 0x1ff), written);
+ *ppos += written;
+ if (*ppos > i->i_size) {
+ i->i_size = *ppos;
+ /*mark_inode_dirty(i);*/i->i_hpfs_dirty = 1;
+ }
+ mark_buffer_dirty(bh, 0);
+ brelse(bh);
+ count -= size;
+ }
+ if (start == buf) return error;
+ i->i_mtime = CURRENT_TIME;
+ /*mark_inode_dirty(i);*/i->i_hpfs_dirty = 1;
+ return buf - start;
+}
diff --git a/fs/hpfs/hpfs_fn.h b/fs/hpfs/hpfs_fn.h
new file mode 100644
index 000000000..cdb2c709e
--- /dev/null
+++ b/fs/hpfs/hpfs_fn.h
@@ -0,0 +1,318 @@
+/*
+ * linux/fs/hpfs/hpfs_fn.h
+ *
+ * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
+ *
+ * function headers
+ */
+
+//#define DBG
+//#define DEBUG_LOCKS
+
+#include <linux/fs.h>
+#include <linux/hpfs_fs.h>
+#include <linux/errno.h>
+#include <linux/malloc.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/locks.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <asm/bitops.h>
+#include <asm/segment.h>
+#include <asm/uaccess.h>
+
+#include <stdarg.h>
+
+#include "hpfs.h"
+
+#define memcpy_tofs memcpy
+#define memcpy_fromfs memcpy
+
+#define EIOERROR EIO
+#define EFSERROR EPERM
+#define EMEMERROR ENOMEM
+
+#define ANODE_ALLOC_FWD 512
+#define FNODE_ALLOC_FWD 0
+#define ALLOC_FWD_MIN 16
+#define ALLOC_FWD_MAX 128
+#define ALLOC_M 1
+#define FNODE_RD_AHEAD 16
+#define ANODE_RD_AHEAD 16
+#define DNODE_RD_AHEAD 4
+
+#define FREE_DNODES_ADD 58
+#define FREE_DNODES_DEL 29
+
+#define CHKCOND(x,y) if (!(x)) printk y
+
+#ifdef DBG
+#define PRINTK(x) printk x
+#else
+#undef PRINTK
+#define PRINTK(x)
+#endif
+
+typedef void nonconst; /* What this is for ? */
+
+/*
+ * local time (HPFS) to GMT (Unix)
+ */
+
+extern inline time_t local_to_gmt(struct super_block *s, time_t t)
+{
+ extern struct timezone sys_tz;
+ return t + sys_tz.tz_minuteswest * 60 - (sys_tz.tz_dsttime ? 3600 : 0) +s->s_hpfs_timeshift;
+}
+
+extern inline time_t gmt_to_local(struct super_block *s, time_t t)
+{
+ extern struct timezone sys_tz;
+ return t - sys_tz.tz_minuteswest * 60 + (sys_tz.tz_dsttime ? 3600 : 0) -s->s_hpfs_timeshift;
+}
+
+/*
+ * conv= options
+ */
+
+#define CONV_BINARY 0 /* no conversion */
+#define CONV_TEXT 1 /* crlf->newline */
+#define CONV_AUTO 2 /* decide based on file contents */
+
+/* Four 512-byte buffers and the 2k block obtained by concatenating them */
+
+struct quad_buffer_head {
+ struct buffer_head *bh[4];
+ void *data;
+};
+
+/* The b-tree down pointer from a dir entry */
+
+extern inline dnode_secno de_down_pointer (struct hpfs_dirent *de)
+{
+ CHKCOND(de->down,("HPFS: de_down_pointer: !de->down\n"));
+ return *(dnode_secno *) ((void *) de + de->length - 4);
+}
+
+/* The first dir entry in a dnode */
+
+extern inline struct hpfs_dirent *dnode_first_de (struct dnode *dnode)
+{
+ return (void *) dnode->dirent;
+}
+
+/* The end+1 of the dir entries */
+
+extern inline struct hpfs_dirent *dnode_end_de (struct dnode *dnode)
+{
+ CHKCOND(dnode->first_free>=0x14 && dnode->first_free<=0xa00,("HPFS: dnode_end_de: dnode->first_free = %d\n",(int)dnode->first_free));
+ return (void *) dnode + dnode->first_free;
+}
+
+/* The dir entry after dir entry de */
+
+extern inline struct hpfs_dirent *de_next_de (struct hpfs_dirent *de)
+{
+ CHKCOND(de->length>=0x20 && de->length<0x800,("HPFS: de_next_de: de->length = %d\n",(int)de->length));
+ return (void *) de + de->length;
+}
+
+extern inline struct extended_attribute *fnode_ea(struct fnode *fnode)
+{
+ return (struct extended_attribute *)((char *)fnode + fnode->ea_offs);
+}
+
+extern inline struct extended_attribute *fnode_end_ea(struct fnode *fnode)
+{
+ return (struct extended_attribute *)((char *)fnode + fnode->ea_offs + fnode->ea_size_s);
+}
+
+extern inline struct extended_attribute *next_ea(struct extended_attribute *ea)
+{
+ return (struct extended_attribute *)((char *)ea + 5 + ea->namelen + ea->valuelen);
+}
+
+extern inline secno ea_sec(struct extended_attribute *ea)
+{
+ return *(secno *)((char *)ea + 9 + ea->namelen);
+}
+
+extern inline secno ea_len(struct extended_attribute *ea)
+{
+ return *(secno *)((char *)ea + 5 + ea->namelen);
+}
+
+extern inline char *ea_data(struct extended_attribute *ea)
+{
+ return (char *)((char *)ea + 5 + ea->namelen);
+}
+
+extern inline unsigned de_size(int namelen, secno down_ptr)
+{
+ return ((0x1f + namelen + 3) & ~3) + (down_ptr ? 4 : 0);
+}
+
+extern inline void copy_de(struct hpfs_dirent *dst, struct hpfs_dirent *src)
+{
+ int a = dst->down;
+ int n = dst->not_8x3;
+ if (!dst || !src) return;
+ memcpy((char *)dst + 2, (char *)src + 2, 28);
+ dst->down = a;
+ dst->not_8x3 = n;
+}
+
+extern inline unsigned tstbits(unsigned *bmp, unsigned b, unsigned n)
+{
+ int i;
+ if ((b >= 0x4000) || (b + n - 1 >= 0x4000)) return n;
+ if (!((bmp[(b & 0x3fff) >> 5] >> (b & 0x1f)) & 1)) return 1;
+ for (i = 1; i < n; i++)
+ if (/*b+i < 0x4000 &&*/ !((bmp[((b+i) & 0x3fff) >> 5] >> ((b+i) & 0x1f)) & 1))
+ return i + 1;
+ return 0;
+}
+
+/* alloc.c */
+
+int hpfs_chk_sectors(struct super_block *, secno, int, char *);
+secno hpfs_alloc_sector(struct super_block *, secno, unsigned, int, int);
+int hpfs_alloc_if_possible_nolock(struct super_block *, secno);
+int hpfs_alloc_if_possible(struct super_block *, secno);
+void hpfs_free_sectors(struct super_block *, secno, unsigned);
+int hpfs_check_free_dnodes(struct super_block *, int);
+void hpfs_free_dnode(struct super_block *, secno);
+struct dnode *hpfs_alloc_dnode(struct super_block *, secno, dnode_secno *, struct quad_buffer_head *, int);
+struct fnode *hpfs_alloc_fnode(struct super_block *, secno, fnode_secno *, struct buffer_head **);
+struct anode *hpfs_alloc_anode(struct super_block *, secno, anode_secno *, struct buffer_head **);
+
+/* anode.c */
+
+secno hpfs_bplus_lookup(struct super_block *, struct inode *, struct bplus_header *, unsigned, struct buffer_head *);
+secno hpfs_add_sector_to_btree(struct super_block *, secno, int, unsigned);
+void hpfs_remove_btree(struct super_block *, struct bplus_header *);
+int hpfs_ea_read(struct super_block *, secno, int, unsigned, unsigned, char *);
+int hpfs_ea_write(struct super_block *, secno, int, unsigned, unsigned, char *);
+void hpfs_ea_remove(struct super_block *, secno, int, unsigned);
+void hpfs_truncate_btree(struct super_block *, secno, int, unsigned);
+void hpfs_remove_fnode(struct super_block *, fnode_secno fno);
+
+/* buffer.c */
+
+void hpfs_lock_creation(struct super_block *);
+void hpfs_unlock_creation(struct super_block *);
+void hpfs_lock_iget(struct super_block *, int);
+void hpfs_unlock_iget(struct super_block *);
+void hpfs_lock_inode(struct inode *);
+void hpfs_unlock_inode(struct inode *);
+void hpfs_lock_2inodes(struct inode *, struct inode *);
+void hpfs_unlock_2inodes(struct inode *, struct inode *);
+void hpfs_lock_3inodes(struct inode *, struct inode *, struct inode *);
+void hpfs_unlock_3inodes(struct inode *, struct inode *, struct inode *);
+void *hpfs_map_sector(struct super_block *, unsigned, struct buffer_head **, int);
+void *hpfs_get_sector(struct super_block *, unsigned, struct buffer_head **);
+void *hpfs_map_4sectors(struct super_block *, unsigned, struct quad_buffer_head *, int);
+void *hpfs_get_4sectors(struct super_block *, unsigned, struct quad_buffer_head *);
+void hpfs_brelse4(struct quad_buffer_head *);
+void hpfs_mark_4buffers_dirty(struct quad_buffer_head *);
+
+/* dentry.c */
+
+void hpfs_set_dentry_operations(struct dentry *);
+
+/* dir.c */
+
+int hpfs_dir_read(struct file *, char *, size_t, loff_t *);
+int hpfs_dir_release(struct inode *, struct file *);
+int hpfs_readdir(struct file *, void *, filldir_t);
+struct dentry *hpfs_lookup(struct inode *, struct dentry *);
+
+/* dnode.c */
+
+void hpfs_add_pos(struct inode *, loff_t *);
+void hpfs_del_pos(struct inode *, loff_t *);
+struct hpfs_dirent *hpfs_add_de(struct super_block *, struct dnode *, unsigned char *, unsigned, secno);
+void hpfs_delete_de(struct super_block *, struct dnode *, struct hpfs_dirent *);
+int hpfs_add_to_dnode(struct inode *, dnode_secno, unsigned char *, unsigned, struct hpfs_dirent *, dnode_secno);
+int hpfs_add_dirent(struct inode *, unsigned char *, unsigned, struct hpfs_dirent *, int);
+int hpfs_remove_dirent(struct inode *, dnode_secno, struct hpfs_dirent *, struct quad_buffer_head *, int);
+void hpfs_count_dnodes(struct super_block *, dnode_secno, int *, int *, int *);
+dnode_secno hpfs_de_as_down_as_possible(struct super_block *, dnode_secno dno);
+struct hpfs_dirent *map_pos_dirent(struct inode *, loff_t *, struct quad_buffer_head *);
+struct hpfs_dirent *map_dirent(struct inode *, dnode_secno, char *, unsigned, dnode_secno *, struct quad_buffer_head *);
+void hpfs_remove_dtree(struct super_block *, dnode_secno);
+struct hpfs_dirent *map_fnode_dirent(struct super_block *, fnode_secno, struct fnode *, struct quad_buffer_head *);
+
+/* ea.c */
+
+void hpfs_ea_ext_remove(struct super_block *, secno, int, unsigned);
+char *hpfs_get_ea(struct super_block *, struct fnode *, char *, int *);
+void hpfs_set_ea(struct inode *, struct fnode *, char *, char *, int);
+
+/* file.c */
+
+int hpfs_file_release(struct inode *, struct file *);
+int hpfs_open(struct inode *, struct file *);
+int hpfs_file_fsync(struct file *, struct dentry *);
+secno hpfs_bmap(struct inode *, unsigned);
+void hpfs_truncate(struct inode *);
+ssize_t hpfs_file_read(struct file *, char *, size_t, loff_t *);
+ssize_t hpfs_file_write(struct file *, const char *, size_t, loff_t *);
+
+/* inode.c */
+
+void hpfs_read_inode(struct inode *);
+void hpfs_write_inode_ea(struct inode *, struct fnode *);
+void hpfs_write_inode(struct inode *);
+void hpfs_write_inode_nolock(struct inode *);
+int hpfs_notify_change(struct dentry *, struct iattr *);
+void hpfs_write_if_changed(struct inode *);
+void hpfs_delete_inode(struct inode *);
+
+/* map.c */
+
+unsigned *hpfs_map_dnode_bitmap(struct super_block *, struct quad_buffer_head *);
+unsigned *hpfs_map_bitmap(struct super_block *, unsigned, struct quad_buffer_head *, char *);
+char *hpfs_load_code_page(struct super_block *, secno);
+secno *hpfs_load_bitmap_directory(struct super_block *, secno bmp);
+struct fnode *hpfs_map_fnode(struct super_block *s, ino_t, struct buffer_head **);
+struct anode *hpfs_map_anode(struct super_block *s, anode_secno, struct buffer_head **);
+struct dnode *hpfs_map_dnode(struct super_block *s, dnode_secno, struct quad_buffer_head *);
+dnode_secno hpfs_fnode_dno(struct super_block *s, ino_t ino);
+
+/* mmap.c */
+
+int hpfs_mmap(struct file *, struct vm_area_struct *);
+
+/* name.c */
+
+unsigned char hpfs_upcase(unsigned char *, unsigned char);
+int hpfs_chk_name(unsigned char *, unsigned *);
+char *hpfs_translate_name(struct super_block *, unsigned char *, unsigned, int, int);
+int hpfs_compare_names(struct super_block *, unsigned char *, unsigned, unsigned char *, unsigned, int);
+int hpfs_is_name_long(unsigned char *, unsigned);
+void hpfs_adjust_length(unsigned char *, unsigned *);
+void hpfs_decide_conv(struct inode *, unsigned char *, unsigned);
+
+/* namei.c */
+
+int hpfs_mkdir(struct inode *, struct dentry *, int);
+int hpfs_create(struct inode *, struct dentry *, int);
+int hpfs_mknod(struct inode *, struct dentry *, int, int);
+int hpfs_symlink(struct inode *, struct dentry *, const char *);
+int hpfs_unlink(struct inode *, struct dentry *);
+int hpfs_rmdir(struct inode *, struct dentry *);
+int hpfs_readlink(struct dentry *, char *, int);
+struct dentry *hpfs_follow_link(struct dentry *, struct dentry *, unsigned int);
+int hpfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *);
+
+/* super.c */
+
+void hpfs_error(struct super_block *, char *, ...);
+int hpfs_stop_cycles(struct super_block *, int, int *, int *, char *);
+int hpfs_remount_fs(struct super_block *, int *, char *);
+void hpfs_put_super(struct super_block *);
+unsigned hpfs_count_one_bitmap(struct super_block *, secno);
+int hpfs_statfs(struct super_block *, struct statfs *, int);
+struct super_block *hpfs_read_super(struct super_block *, void *, int);
diff --git a/fs/hpfs/inode.c b/fs/hpfs/inode.c
new file mode 100644
index 000000000..99bfa1004
--- /dev/null
+++ b/fs/hpfs/inode.c
@@ -0,0 +1,381 @@
+/*
+ * linux/fs/hpfs/inode.c
+ *
+ * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
+ *
+ * inode VFS functions
+ */
+
+#include "hpfs_fn.h"
+
+static const struct file_operations hpfs_file_ops =
+{
+ NULL, /* lseek - default */
+ hpfs_file_read, /* read */
+ hpfs_file_write, /* write */
+ NULL, /* readdir - bad */
+ NULL, /* poll - default */
+ NULL, /* ioctl - default */
+ generic_file_mmap/*hpfs_mmap*/, /* mmap */
+ hpfs_open, /* open */
+ NULL, /* flush */
+ hpfs_file_release, /* release */
+ hpfs_file_fsync, /* fsync */
+ NULL, /* fasync */
+ NULL, /* check_media_change */
+ NULL, /* revalidate */
+ NULL, /* lock */
+};
+
+static const struct inode_operations hpfs_file_iops =
+{
+ (nonconst *) & hpfs_file_ops, /* default file operations */
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ generic_readpage, /* readpage */
+ NULL, /* writepage */
+ (int (*)(struct inode *, int))
+ &hpfs_bmap, /* bmap */
+ &hpfs_truncate, /* truncate */
+ NULL, /* permission */
+ NULL, /* smap */
+ NULL, /* updatepage */
+ NULL, /* revalidate */
+};
+
+static const struct file_operations hpfs_dir_ops =
+{
+ NULL, /* lseek - default */
+ hpfs_dir_read, /* read */
+ NULL, /* write - bad */
+ hpfs_readdir, /* readdir */
+ NULL, /* poll - default */
+ NULL, /* ioctl - default */
+ NULL, /* mmap */
+ hpfs_open, /* open */
+ NULL, /* flush */
+ hpfs_dir_release, /* no special release code */
+ hpfs_file_fsync, /* fsync */
+ NULL, /* fasync */
+ NULL, /* check_media_change */
+ NULL, /* revalidate */
+ NULL, /* lock */
+};
+
+static const struct inode_operations hpfs_dir_iops =
+{
+ (nonconst *) & hpfs_dir_ops, /* default directory file ops */
+ hpfs_create, /* create */
+ hpfs_lookup, /* lookup */
+ NULL, /* link */
+ hpfs_unlink, /* unlink */
+ hpfs_symlink, /* symlink */
+ hpfs_mkdir, /* mkdir */
+ hpfs_rmdir, /* rmdir */
+ hpfs_mknod, /* mknod */
+ hpfs_rename, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL, /* permission */
+ NULL, /* smap */
+ NULL, /* updatepage */
+ NULL, /* revalidate */
+};
+
+const struct inode_operations hpfs_symlink_iops =
+{
+ NULL, /* default file operations */
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ hpfs_readlink, /* readlink */
+ hpfs_follow_link, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL, /* permission */
+ NULL, /* smap */
+ NULL, /* updatepage */
+ NULL, /* revalidate */
+};
+
+
+void hpfs_read_inode(struct inode *i)
+{
+ struct buffer_head *bh;
+ struct fnode *fnode;
+ struct super_block *sb = i->i_sb;
+ unsigned char *ea;
+ int ea_size;
+ i->i_op = 0;
+ /*i->i_hpfs_sem = MUTEX;*/
+ init_MUTEX(&i->i_hpfs_sem);
+ i->i_uid = sb->s_hpfs_uid;
+ i->i_gid = sb->s_hpfs_gid;
+ i->i_mode = sb->s_hpfs_mode;
+ i->i_hpfs_conv = sb->s_hpfs_conv;
+ i->i_blksize = 512;
+ i->i_size = -1;
+ i->i_blocks = -1;
+
+ i->i_hpfs_dno = 0;
+ i->i_hpfs_n_secs = 0;
+ i->i_hpfs_file_sec = 0;
+ i->i_hpfs_disk_sec = 0;
+ i->i_hpfs_dpos = 0;
+ i->i_hpfs_dsubdno = 0;
+ i->i_hpfs_ea_mode = 0;
+ i->i_hpfs_ea_uid = 0;
+ i->i_hpfs_ea_gid = 0;
+ i->i_hpfs_ea_size = 0;
+ i->i_version = ++event;
+
+ i->i_hpfs_rddir_off = NULL;
+ i->i_hpfs_dirty = 0;
+
+ i->i_atime = 0;
+ i->i_mtime = 0;
+ i->i_ctime = 0;
+
+ if (!i->i_sb->s_hpfs_rd_inode)
+ hpfs_error(i->i_sb, "read_inode: s_hpfs_rd_inode == 0");
+ if (i->i_sb->s_hpfs_rd_inode == 2) {
+ i->i_mode |= S_IFREG;
+ i->i_mode &= ~0111;
+ i->i_op = (struct inode_operations *) &hpfs_file_iops;
+ i->i_nlink = 1;
+ return;
+ }
+ if (!(fnode = hpfs_map_fnode(sb, i->i_ino, &bh))) {
+ /*i->i_mode |= S_IFREG;
+ i->i_mode &= ~0111;
+ i->i_op = (struct inode_operations *) &hpfs_file_iops;
+ i->i_nlink = 0;*/
+ make_bad_inode(i);
+ return;
+ }
+ if (i->i_sb->s_hpfs_eas) {
+ if ((ea = hpfs_get_ea(i->i_sb, fnode, "UID", &ea_size))) {
+ if (ea_size == 2) {
+ i->i_uid = ea[0] + (ea[1] << 8);
+ i->i_hpfs_ea_uid = 1;
+ }
+ kfree(ea);
+ }
+ if ((ea = hpfs_get_ea(i->i_sb, fnode, "GID", &ea_size))) {
+ if (ea_size == 2) {
+ i->i_gid = ea[0] + (ea[1] << 8);
+ i->i_hpfs_ea_gid = 1;
+ }
+ kfree(ea);
+ }
+ if ((ea = hpfs_get_ea(i->i_sb, fnode, "SYMLINK", &ea_size))) {
+ kfree(ea);
+ i->i_mode = S_IFLNK | 0777;
+ i->i_op = (struct inode_operations *) &hpfs_symlink_iops;
+ i->i_nlink = 1;
+ i->i_size = ea_size;
+ i->i_blocks = 1;
+ brelse(bh);
+ return;
+ }
+ if ((ea = hpfs_get_ea(i->i_sb, fnode, "MODE", &ea_size))) {
+ int rdev = 0;
+ umode_t mode = sb->s_hpfs_mode;
+ if (ea_size == 2) {
+ mode = ea[0] + (ea[1] << 8);
+ i->i_hpfs_ea_mode = 1;
+ }
+ kfree(ea);
+ i->i_mode = mode;
+ if (S_ISBLK(mode) || S_ISCHR(mode)) {
+ if ((ea = hpfs_get_ea(i->i_sb, fnode, "DEV", &ea_size))) {
+ if (ea_size == 4)
+ rdev = ea[0] + (ea[1] << 8) + (ea[2] << 16) + (ea[3] << 24);
+ kfree(ea);
+ }
+ }
+ if (S_ISBLK(mode) || S_ISCHR(mode) || S_ISFIFO(mode) || S_ISSOCK(mode)) {
+ brelse(bh);
+ i->i_nlink = 1;
+ i->i_size = 0;
+ i->i_blocks = 1;
+ init_special_inode(i, mode, rdev);
+ return;
+ }
+ }
+ }
+ if (fnode->dirflag) {
+ unsigned n_dnodes, n_subdirs;
+ i->i_mode |= S_IFDIR;
+ i->i_op = (struct inode_operations *) &hpfs_dir_iops;
+ i->i_hpfs_parent_dir = fnode->up;
+ i->i_hpfs_dno = fnode->u.external[0].disk_secno;
+ if (sb->s_hpfs_chk >= 2) {
+ struct buffer_head *bh0;
+ if (hpfs_map_fnode(sb, i->i_hpfs_parent_dir, &bh0)) brelse(bh0);
+ }
+ n_dnodes = 0; n_subdirs = 0;
+ hpfs_count_dnodes(i->i_sb, i->i_hpfs_dno, &n_dnodes, &n_subdirs, NULL);
+ i->i_blocks = 4 * n_dnodes;
+ i->i_size = 2048 * n_dnodes;
+ i->i_nlink = 2 + n_subdirs;
+ } else {
+ i->i_mode |= S_IFREG;
+ if (!i->i_hpfs_ea_mode) i->i_mode &= ~0111;
+ i->i_op = (struct inode_operations *) &hpfs_file_iops;
+ i->i_nlink = 1;
+ i->i_size = fnode->file_size;
+ i->i_blocks = ((i->i_size + 511) >> 9) + 1;
+ }
+ brelse(bh);
+}
+
+void hpfs_write_inode_ea(struct inode *i, struct fnode *fnode)
+{
+ if (fnode->acl_size_l || fnode->acl_size_s) {
+ /* Some unknown structures like ACL may be in fnode,
+ we'd better not overwrite them */
+ hpfs_error(i->i_sb, "fnode %08x has some unknown HPFS386 stuctures", i->i_ino);
+ } else if (i->i_sb->s_hpfs_eas >= 2) {
+ unsigned char ea[4];
+ if ((i->i_uid != i->i_sb->s_hpfs_uid) || i->i_hpfs_ea_uid) {
+ ea[0] = i->i_uid & 0xff;
+ ea[1] = i->i_uid >> 8;
+ hpfs_set_ea(i, fnode, "UID", ea, 2);
+ i->i_hpfs_ea_uid = 1;
+ }
+ if ((i->i_gid != i->i_sb->s_hpfs_gid) || i->i_hpfs_ea_gid) {
+ ea[0] = i->i_gid & 0xff;
+ ea[1] = i->i_gid >> 8;
+ hpfs_set_ea(i, fnode, "GID", ea, 2);
+ i->i_hpfs_ea_gid = 1;
+ }
+ if (!S_ISLNK(i->i_mode))
+ if ((i->i_mode != ((i->i_sb->s_hpfs_mode & ~(S_ISDIR(i->i_mode) ? 0 : 0111))
+ | (S_ISDIR(i->i_mode) ? S_IFDIR : S_IFREG))
+ && i->i_mode != ((i->i_sb->s_hpfs_mode & ~(S_ISDIR(i->i_mode) ? 0222 : 0333))
+ | (S_ISDIR(i->i_mode) ? S_IFDIR : S_IFREG))) || i->i_hpfs_ea_mode) {
+ ea[0] = i->i_mode & 0xff;
+ ea[1] = i->i_mode >> 8;
+ hpfs_set_ea(i, fnode, "MODE", ea, 2);
+ i->i_hpfs_ea_mode = 1;
+ }
+ if (S_ISBLK(i->i_mode) || S_ISCHR(i->i_mode)) {
+ int d = kdev_t_to_nr(i->i_rdev);
+ ea[0] = d & 0xff;
+ ea[1] = (d >> 8) & 0xff;
+ ea[2] = (d >> 16) & 0xff;
+ ea[3] = d >> 24;
+ hpfs_set_ea(i, fnode, "DEV", ea, 4);
+ }
+ }
+}
+
+void hpfs_write_inode(struct inode *i)
+{
+ struct inode *parent;
+ if (!i->i_nlink) return;
+ if (i->i_ino == i->i_sb->s_hpfs_root) return;
+ if (i->i_hpfs_rddir_off && !i->i_count) {
+ if (*i->i_hpfs_rddir_off) printk("HPFS: write_inode: some position still there\n");
+ kfree(i->i_hpfs_rddir_off);
+ i->i_hpfs_rddir_off = NULL;
+ }
+ i->i_hpfs_dirty = 0;
+ hpfs_lock_iget(i->i_sb, 1);
+ parent = iget(i->i_sb, i->i_hpfs_parent_dir);
+ hpfs_unlock_iget(i->i_sb);
+ hpfs_lock_inode(parent);
+ hpfs_write_inode_nolock(i);
+ hpfs_unlock_inode(parent);
+ iput(parent);
+}
+
+void hpfs_write_inode_nolock(struct inode *i)
+{
+ struct buffer_head *bh;
+ struct fnode *fnode;
+ struct quad_buffer_head qbh;
+ struct hpfs_dirent *de;
+ if (i->i_ino == i->i_sb->s_hpfs_root) return;
+ if (!(fnode = hpfs_map_fnode(i->i_sb, i->i_ino, &bh))) return;
+ if (i->i_ino != i->i_sb->s_hpfs_root) {
+ if (!(de = map_fnode_dirent(i->i_sb, i->i_ino, fnode, &qbh))) {
+ brelse(bh);
+ return;
+ }
+ } else de = NULL;
+ if (S_ISREG(i->i_mode)) {
+ fnode->file_size = de->file_size = i->i_size;
+ } else if (S_ISDIR(i->i_mode)) {
+ fnode->file_size = de->file_size = 0;
+ }
+ hpfs_write_inode_ea(i, fnode);
+ if (de) {
+ de->write_date = gmt_to_local(i->i_sb, i->i_mtime);
+ de->read_date = gmt_to_local(i->i_sb, i->i_atime);
+ de->creation_date = gmt_to_local(i->i_sb, i->i_ctime);
+ de->read_only = !(i->i_mode & 0222);
+ de->ea_size = i->i_hpfs_ea_size;
+ hpfs_mark_4buffers_dirty(&qbh);
+ hpfs_brelse4(&qbh);
+ }
+ if (S_ISDIR(i->i_mode)) {
+ if ((de = map_dirent(i, i->i_hpfs_dno, "\001\001", 2, NULL, &qbh))) {
+ de->write_date = gmt_to_local(i->i_sb, i->i_mtime);
+ de->read_date = gmt_to_local(i->i_sb, i->i_atime);
+ de->creation_date = gmt_to_local(i->i_sb, i->i_ctime);
+ de->read_only = !(i->i_mode & 0222);
+ de->ea_size = /*i->i_hpfs_ea_size*/0;
+ de->file_size = 0;
+ hpfs_mark_4buffers_dirty(&qbh);
+ hpfs_brelse4(&qbh);
+ } else hpfs_error(i->i_sb, "directory %08x doesn't have '.' entry", i->i_ino);
+ }
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+}
+
+int hpfs_notify_change(struct dentry *dentry, struct iattr *attr)
+{
+ struct inode *inode = dentry->d_inode;
+ int error;
+ if (inode->i_sb->s_hpfs_root == inode->i_ino) return -EINVAL;
+ if ((error = inode_change_ok(inode, attr))) return error;
+ inode_setattr(inode, attr);
+ hpfs_write_inode(inode);
+ return 0;
+}
+
+void hpfs_write_if_changed(struct inode *inode)
+{
+ if (inode->i_hpfs_dirty) {
+ hpfs_write_inode(inode);
+ }
+}
+
+void hpfs_delete_inode(struct inode *inode)
+{
+ hpfs_remove_fnode(inode->i_sb, inode->i_ino);
+}
diff --git a/fs/hpfs/map.c b/fs/hpfs/map.c
new file mode 100644
index 000000000..a0e0feada
--- /dev/null
+++ b/fs/hpfs/map.c
@@ -0,0 +1,268 @@
+/*
+ * linux/fs/hpfs/map.c
+ *
+ * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
+ *
+ * mapping structures to memory with some minimal checks
+ */
+
+#include "hpfs_fn.h"
+
+unsigned *hpfs_map_dnode_bitmap(struct super_block *s, struct quad_buffer_head *qbh)
+{
+ return hpfs_map_4sectors(s, s->s_hpfs_dmap, qbh, 0);
+}
+
+unsigned int *hpfs_map_bitmap(struct super_block *s, unsigned bmp_block,
+ struct quad_buffer_head *qbh, char *id)
+{
+ secno sec;
+ if (s->s_hpfs_chk) if (bmp_block * 16384 > s->s_hpfs_fs_size) {
+ hpfs_error(s, "hpfs_map_bitmap called with bad parameter: %08x at %s", bmp_block, id);
+ return NULL;
+ }
+ sec = s->s_hpfs_bmp_dir[bmp_block];
+ if (!sec || sec > s->s_hpfs_fs_size-4) {
+ hpfs_error(s, "invalid bitmap block pointer %08x -> %08x at %s", bmp_block, sec, id);
+ return NULL;
+ }
+ return hpfs_map_4sectors(s, sec, qbh, 4);
+}
+
+/*
+ * Load first code page into kernel memory, return pointer to 256-byte array,
+ * first 128 bytes are uppercasing table for chars 128-255, next 128 bytes are
+ * lowercasing table
+ */
+
+char *hpfs_load_code_page(struct super_block *s, secno cps)
+{
+ struct buffer_head *bh;
+ secno cpds;
+ unsigned cpi;
+ unsigned char *ptr;
+ unsigned char *cp_table;
+ int i;
+ struct code_page_data *cpd;
+ struct code_page_directory *cp = hpfs_map_sector(s, cps, &bh, 0);
+ if (!cp) return NULL;
+ if (cp->magic != CP_DIR_MAGIC) {
+ printk("HPFS: Code page directory magic doesn't match (magic = %08x)\n", cp->magic);
+ brelse(bh);
+ return NULL;
+ }
+ if (!cp->n_code_pages) {
+ printk("HPFS: n_code_pages == 0\n");
+ brelse(bh);
+ return NULL;
+ }
+ cpds = cp->array[0].code_page_data;
+ cpi = cp->array[0].index;
+ brelse(bh);
+
+ if (!(cpd = hpfs_map_sector(s, cpds, &bh, 0))) return NULL;
+ if ((unsigned)cpd->offs[cpi] > 0x178) {
+ printk("HPFS: Code page index out of sector\n");
+ brelse(bh);
+ return NULL;
+ }
+ ptr = (char *)cpd + cpd->offs[cpi] + 6;
+ if (!(cp_table = kmalloc(256, GFP_KERNEL))) {
+ printk("HPFS: out of memory for code page table\n");
+ brelse(bh);
+ return NULL;
+ }
+ memcpy(cp_table, ptr, 128);
+ brelse(bh);
+
+ /* Try to build lowercasing table from uppercasing one */
+
+ for (i=128; i<256; i++) cp_table[i]=i;
+ for (i=128; i<256; i++) if (cp_table[i-128]!=i && cp_table[i-128]>=128)
+ cp_table[cp_table[i-128]] = i;
+
+ return cp_table;
+}
+
+secno *hpfs_load_bitmap_directory(struct super_block *s, secno bmp)
+{
+ struct buffer_head *bh;
+ int n = (s->s_hpfs_fs_size + 0x200000 - 1) >> 21;
+ int i;
+ secno *b;
+ if (!(b = kmalloc(n * 512, GFP_KERNEL))) {
+ printk("HPFS: can't allocate memory for bitmap directory\n");
+ return NULL;
+ }
+ for (i=0;i<n;i++) {
+ secno *d = hpfs_map_sector(s, bmp+i, &bh, n - i - 1);
+ if (!d) {
+ kfree(b);
+ return NULL;
+ }
+ memcpy((char *)b + 512 * i, d, 512);
+ brelse(bh);
+ }
+ return b;
+}
+
+/*
+ * Load fnode to memory
+ */
+
+struct fnode *hpfs_map_fnode(struct super_block *s, ino_t ino, struct buffer_head **bhp)
+{
+ struct fnode *fnode;
+ if (s->s_hpfs_chk) if (hpfs_chk_sectors(s, ino, 1, "fnode")) {
+ return NULL;
+ }
+ if ((fnode = hpfs_map_sector(s, ino, bhp, FNODE_RD_AHEAD))) {
+ if (s->s_hpfs_chk) {
+ struct extended_attribute *ea;
+ struct extended_attribute *ea_end;
+ if (fnode->magic != FNODE_MAGIC) {
+ hpfs_error(s, "bad magic on fnode %08x", ino);
+ goto bail;
+ }
+ if (!fnode->dirflag) {
+ if ((unsigned)fnode->btree.n_used_nodes + (unsigned)fnode->btree.n_free_nodes !=
+ (fnode->btree.internal ? 12 : 8)) {
+ hpfs_error(s, "bad number of nodes in fnode %08x", ino);
+ goto bail;
+ }
+ if (fnode->btree.first_free !=
+ 8 + fnode->btree.n_used_nodes * (fnode->btree.internal ? 8 : 12)) {
+ hpfs_error(s, "bad first_free pointer in fnode %08x", ino);
+ goto bail;
+ }
+ }
+ if (fnode->ea_size_s && ((signed int)fnode->ea_offs < 0xc4 ||
+ (signed int)fnode->ea_offs + fnode->ea_size_s > 0x200)) {
+ hpfs_error(s, "bad EA info in fnode %08x: ea_offs == %04x ea_size_s == %04x",
+ ino, fnode->ea_offs, fnode->ea_size_s);
+ goto bail;
+ }
+ ea = fnode_ea(fnode);
+ ea_end = fnode_end_ea(fnode);
+ while (ea != ea_end) {
+ if (ea > ea_end) {
+ hpfs_error(s, "bad EA in fnode %08x", ino);
+ goto bail;
+ }
+ ea = next_ea(ea);
+ }
+ }
+ }
+ return fnode;
+ bail:
+ brelse(*bhp);
+ return NULL;
+}
+
+struct anode *hpfs_map_anode(struct super_block *s, anode_secno ano, struct buffer_head **bhp)
+{
+ struct anode *anode;
+ if (s->s_hpfs_chk) if (hpfs_chk_sectors(s, ano, 1, "anode")) return NULL;
+ if ((anode = hpfs_map_sector(s, ano, bhp, ANODE_RD_AHEAD)))
+ if (s->s_hpfs_chk) {
+ if (anode->magic != ANODE_MAGIC || anode->self != ano) {
+ hpfs_error(s, "bad magic on anode %08x", ano);
+ goto bail;
+ }
+ if ((unsigned)anode->btree.n_used_nodes + (unsigned)anode->btree.n_free_nodes !=
+ (anode->btree.internal ? 60 : 40)) {
+ hpfs_error(s, "bad number of nodes in anode %08x", ano);
+ goto bail;
+ }
+ if (anode->btree.first_free !=
+ 8 + anode->btree.n_used_nodes * (anode->btree.internal ? 8 : 12)) {
+ hpfs_error(s, "bad first_free pointer in anode %08x", ano);
+ goto bail;
+ }
+ }
+ return anode;
+ bail:
+ brelse(*bhp);
+ return NULL;
+}
+
+/*
+ * Load dnode to memory and do some checks
+ */
+
+struct dnode *hpfs_map_dnode(struct super_block *s, unsigned secno,
+ struct quad_buffer_head *qbh)
+{
+ struct dnode *dnode;
+ if (s->s_hpfs_chk) {
+ if (hpfs_chk_sectors(s, secno, 4, "dnode")) return NULL;
+ if (secno & 3) {
+ hpfs_error(s, "dnode %08x not byte-aligned", secno);
+ return NULL;
+ }
+ }
+ if ((dnode = hpfs_map_4sectors(s, secno, qbh, DNODE_RD_AHEAD)))
+ if (s->s_hpfs_chk) {
+ unsigned p, pp = 0;
+ unsigned char *d = (char *)dnode;
+ int b = 0;
+ if (dnode->magic != DNODE_MAGIC) {
+ hpfs_error(s, "bad magic on dnode %08x", secno);
+ goto bail;
+ }
+ if (dnode->self != secno)
+ hpfs_error(s, "bad self pointer on dnode %08x self = %08x", secno, dnode->self);
+ /* Check dirents - bad dirents would cause infinite
+ loops or shooting to memory */
+ if (dnode->first_free > 2048/* || dnode->first_free < 84*/) {
+ hpfs_error(s, "dnode %08x has first_free == %08x", secno, dnode->first_free);
+ goto bail;
+ }
+ for (p = 20; p < dnode->first_free; p += d[p] + (d[p+1] << 8)) {
+ struct hpfs_dirent *de = (struct hpfs_dirent *)((char *)dnode + p);
+ if (de->length > 292 || (de->length < 32) || (de->length & 3)) {
+ hpfs_error(s, "bad dirent size in dnode %08x, dirent %03x, last %03x", secno, p, pp);
+ goto bail;
+ }
+ if (((31 + de->namelen + de->down*4 + 3) & ~3) != de->length) {
+ hpfs_error(s, "namelen does not match dirent size in dnode %08x, dirent %03x, last %03x", secno, p, pp);
+ goto bail;
+ }
+ if (s->s_hpfs_chk >= 2) b |= 1 << de->down;
+ if (de->down) if (de_down_pointer(de) < 0x10) {
+ hpfs_error(s, "bad down pointer in dnode %08x, dirent %03x, last %03x", secno, p, pp);
+ goto bail;
+ }
+ pp = p;
+
+ }
+ if (p != dnode->first_free) {
+ hpfs_error(s, "size on last dirent does not match first_free; dnode %08x", secno);
+ goto bail;
+ }
+ if (d[pp + 30] != 1 || d[pp + 31] != 255) {
+ hpfs_error(s, "dnode %08x does not end with \\377 entry", secno);
+ goto bail;
+ }
+ if (b == 3) printk("HPFS: warning: unbalanced dnode tree, dnode %08x; see hpfs.txt 4 more info\n", secno);
+ }
+ return dnode;
+ bail:
+ hpfs_brelse4(qbh);
+ return NULL;
+}
+
+dnode_secno hpfs_fnode_dno(struct super_block *s, ino_t ino)
+{
+ struct buffer_head *bh;
+ struct fnode *fnode;
+ dnode_secno dno;
+
+ fnode = hpfs_map_fnode(s, ino, &bh);
+ if (!fnode)
+ return 0;
+
+ dno = fnode->u.external[0].disk_secno;
+ brelse(bh);
+ return dno;
+}
diff --git a/fs/hpfs/mmap.c b/fs/hpfs/mmap.c
new file mode 100644
index 000000000..3fd544664
--- /dev/null
+++ b/fs/hpfs/mmap.c
@@ -0,0 +1,128 @@
+/*
+ * linux/fs/hpfs/mmap.c
+ *
+ * taken from fat filesystem
+ *
+ * Written by Jacques Gelinas (jacques@solucorp.qc.ca)
+ * Inspired by fs/nfs/mmap.c (Jon Tombs 15 Aug 1993)
+ *
+ * Modified for HPFS by Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz)
+ *
+ * mmap handling for hpfs filesystem
+ * (generic_file_mmap may be used only on filesystems that keep zeros
+ * in last file sector beyond end)
+ */
+
+/*
+ * generic_file_mmap doesn't erase the space beyond file end in last sector. :-(
+ * Hpfs doesn't keep zeros in last sector. This causes problems with kernel
+ * mkdep.c and probably other programs. Additionally this could be a security
+ * hole - some interesting data, like pieces of /etc/shadow could be found
+ * beyond file end.
+ *
+ * So, I can't use generic mmap. mmap from fat filesystem looks good, so I used
+ * it.
+ *
+ * BTW. fat uses generic mmap on normal disks. Doesn't it also have above bugs?
+ * I don't think Msdos erases space in last sector.
+ *
+ * If you fix generic_file_mmap, you can remove this file and use it.
+ */
+
+#include "hpfs_fn.h"
+
+/*
+ * Fill in the supplied page for mmap
+ */
+
+static unsigned long hpfs_file_mmap_nopage(
+ struct vm_area_struct * area,
+ unsigned long address,
+ int error_code)
+{
+ /*struct inode * inode = area->vm_inode;*/
+ struct inode * inode = area->vm_file->f_dentry->d_inode;
+ unsigned long page;
+ unsigned int clear;
+ loff_t pos;
+ long gap; /* distance from eof to pos */
+
+ page = __get_free_page(GFP_KERNEL);
+ if (!page)
+ return page;
+ address &= PAGE_MASK;
+ pos = address - area->vm_start + area->vm_offset;
+
+ clear = 0;
+ gap = inode->i_size - pos;
+ if (gap <= 0){
+ /* mmaping beyond end of file */
+ clear = PAGE_SIZE;
+ }else{
+ int cur_read;
+ int need_read;
+ /*struct file *filp = area->vm_file;*/
+ struct file filp;
+ if (gap < PAGE_SIZE){
+ clear = PAGE_SIZE - gap;
+ }
+ filp.f_reada = 0;
+ filp.f_pos = pos;
+ filp.f_dentry=area->vm_file->f_dentry;
+ need_read = PAGE_SIZE - clear;
+ {
+ mm_segment_t cur_fs = get_fs();
+ set_fs (KERNEL_DS);
+ cur_read = generic_file_read (&filp,(char*)page
+ ,need_read,&pos);
+ set_fs (cur_fs);
+ }
+ if (cur_read != need_read){
+ hpfs_error(inode->i_sb, "Error while reading an mmap file %08x", inode->i_ino);
+ }
+ }
+ if (clear > 0){
+ memset ((char*)page+PAGE_SIZE-clear,0,clear);
+ }
+ return page;
+}
+
+struct vm_operations_struct hpfs_file_mmap = {
+ NULL, /* open */
+ NULL, /* close */
+ NULL, /* unmap */
+ NULL, /* protect */
+ NULL, /* sync */
+ NULL, /* advise */
+ hpfs_file_mmap_nopage, /* nopage */
+ NULL, /* wppage */
+ NULL, /* swapout */
+ NULL, /* swapin */
+};
+
+/*
+ * This is used for a general mmap of an msdos file
+ * Returns 0 if ok, or a negative error code if not.
+ */
+int hpfs_mmap(struct file * file, struct vm_area_struct * vma)
+{
+ struct inode *inode = file->f_dentry->d_inode;
+ /*printk("start mmap\n");*/
+ if (vma->vm_flags & VM_SHARED) /* only PAGE_COW or read-only supported now */
+ return -EINVAL;
+ if (vma->vm_offset & (inode->i_sb->s_blocksize - 1))
+ return -EINVAL;
+ if (!inode->i_sb || !S_ISREG(inode->i_mode))
+ return -EACCES;
+ /*if (!IS_RDONLY(inode)) {
+ inode->i_atime = CURRENT_TIME;
+ mark_inode_dirty(inode);
+ }*/
+
+ vma->vm_file = file;
+ /*inode->i_count++;*/
+ file->f_count++;
+ vma->vm_ops = &hpfs_file_mmap;
+ /*printk("end mmap\n");*/
+ return 0;
+}
diff --git a/fs/hpfs/name.c b/fs/hpfs/name.c
new file mode 100644
index 000000000..f9fa94c7e
--- /dev/null
+++ b/fs/hpfs/name.c
@@ -0,0 +1,144 @@
+/*
+ * linux/fs/hpfs/name.c
+ *
+ * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
+ *
+ * operations with filenames
+ */
+
+#include <linux/string.h>
+#include "hpfs_fn.h"
+
+char *text_postfix[]={
+".ASM", ".BAS", ".BAT", ".C", ".CC", ".CFG", ".CMD", ".CON", ".CPP", ".DEF",
+".DOC", ".DPR", ".ERX", ".H", ".HPP", ".HTM", ".HTML", ".JAVA", ".LOG", ".PAS",
+".RC", ".TEX", ".TXT", ".Y", ""};
+
+char *text_prefix[]={
+"AUTOEXEC.", "CHANGES", "COPYING", "CONFIG.", "CREDITS", "FAQ", "FILE_ID.DIZ",
+"MAKEFILE", "READ.ME", "README", "TERMCAP", ""};
+
+void hpfs_decide_conv(struct inode *inode, unsigned char *name, unsigned len)
+{
+ int i;
+ if (inode->i_hpfs_conv != CONV_AUTO) return;
+ for (i = 0; *text_postfix[i]; i++) {
+ int l = strlen(text_postfix[i]);
+ if (l <= len)
+ if (!hpfs_compare_names(inode->i_sb, text_postfix[i], l, name + len - l, l, 0))
+ goto text;
+ }
+ for (i = 0; *text_prefix[i]; i++) {
+ int l = strlen(text_prefix[i]);
+ if (l <= len)
+ if (!hpfs_compare_names(inode->i_sb, text_prefix[i], l, name, l, 0))
+ goto text;
+ }
+ inode->i_hpfs_conv = CONV_BINARY;
+ return;
+ text:
+ inode->i_hpfs_conv = CONV_TEXT;
+ return;
+}
+
+static inline int not_allowed_char(unsigned char c)
+{
+ return c<' ' || c=='"' || c=='*' || c=='/' || c==':' || c=='<' ||
+ c=='>' || c=='?' || c=='\\' || c=='|';
+}
+
+static inline int no_dos_char(unsigned char c)
+{ /* Characters that are allowed in HPFS but not in DOS */
+ return c=='+' || c==',' || c==';' || c=='=' || c=='[' || c==']';
+}
+
+static inline unsigned char upcase(unsigned char *dir, unsigned char a)
+{
+ if (a<128 || a==255) return a>='a' && a<='z' ? a - 0x20 : a;
+ if (!dir) return a;
+ return dir[a-128];
+}
+
+unsigned char hpfs_upcase(unsigned char *dir, unsigned char a)
+{
+ return upcase(dir, a);
+}
+
+static inline unsigned char locase(unsigned char *dir, unsigned char a)
+{
+ if (a<128 || a==255) return a>='A' && a<='Z' ? a + 0x20 : a;
+ if (!dir) return a;
+ return dir[a];
+}
+
+int hpfs_chk_name(unsigned char *name, unsigned *len)
+{
+ int i;
+ if (*len > 254) return -ENAMETOOLONG;
+ hpfs_adjust_length(name, len);
+ if (!*len) return -EINVAL;
+ for (i = 0; i < *len; i++) if (not_allowed_char(name[i])) return -EINVAL;
+ if (*len == 1) if (name[0] == '.') return -EINVAL;
+ if (*len == 2) if (name[0] == '.' && name[1] == '.') return -EINVAL;
+ return 0;
+}
+
+char *hpfs_translate_name(struct super_block *s, unsigned char *from,
+ unsigned len, int lc, int lng)
+{
+ char *to;
+ int i;
+ if (s->s_hpfs_chk >= 2) if (hpfs_is_name_long(from, len) != lng) {
+ printk("HPFS: Long name flag mismatch - name ");
+ for (i=0; i<len; i++) printk("%c", from[i]);
+ printk(" misidentified as %s.\n", lng ? "short" : "long");
+ printk("HPFS: It's nothing serious. It could happen because of bug in OS/2.\nHPFS: Set checks=normal to disable this message.\n");
+ }
+ if (!lc) return from;
+ if (!(to = kmalloc(len, GFP_KERNEL))) {
+ printk("HPFS: can't allocate memory for name conversion buffer\n");
+ return from;
+ }
+ for (i = 0; i < len; i++) to[i] = locase(s->s_hpfs_cp_table,from[i]);
+ return to;
+}
+
+int hpfs_compare_names(struct super_block *s, unsigned char *n1, unsigned l1,
+ unsigned char *n2, unsigned l2, int last)
+{
+ unsigned l = l1 < l2 ? l1 : l2;
+ unsigned i;
+ if (last) return -1;
+ for (i = 0; i < l; i++) {
+ unsigned char c1 = upcase(s->s_hpfs_cp_table,n1[i]);
+ unsigned char c2 = upcase(s->s_hpfs_cp_table,n2[i]);
+ if (c1 < c2) return -1;
+ if (c1 > c2) return 1;
+ }
+ if (l1 < l2) return -1;
+ if (l1 > l2) return 1;
+ return 0;
+}
+
+int hpfs_is_name_long(unsigned char *name, unsigned len)
+{
+ int i,j;
+ for (i = 0; i < len && name[i] != '.'; i++)
+ if (no_dos_char(name[i])) return 1;
+ if (!i || i > 8) return 1;
+ if (i == len) return 0;
+ for (j = i + 1; j < len; j++)
+ if (name[j] == '.' || no_dos_char(name[i])) return 1;
+ return j - i > 4;
+}
+
+/* OS/2 clears dots and spaces at the end of file name, so we have to */
+
+void hpfs_adjust_length(unsigned char *name, unsigned *len)
+{
+ if (!*len) return;
+ if (*len == 1 && name[0] == '.') return;
+ if (*len == 2 && name[0] == '.' && name[1] == '.') return;
+ while (*len && (name[*len - 1] == '.' || name[*len - 1] == ' '))
+ (*len)--;
+}
diff --git a/fs/hpfs/namei.c b/fs/hpfs/namei.c
new file mode 100644
index 000000000..fe9b66e3d
--- /dev/null
+++ b/fs/hpfs/namei.c
@@ -0,0 +1,549 @@
+/*
+ * linux/fs/hpfs/namei.c
+ *
+ * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
+ *
+ * adding & removing files & directories
+ */
+
+#include <linux/string.h>
+#include "hpfs_fn.h"
+
+int hpfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+ const char *name = dentry->d_name.name;
+ unsigned len = dentry->d_name.len;
+ struct quad_buffer_head qbh0;
+ struct buffer_head *bh;
+ struct hpfs_dirent *de;
+ struct fnode *fnode;
+ struct dnode *dnode;
+ struct inode *result;
+ fnode_secno fno;
+ dnode_secno dno;
+ int r;
+ struct hpfs_dirent dee;
+ int err;
+ if ((err = hpfs_chk_name((char *)name, &len))) return err==-ENOENT ? -EINVAL : err;
+ if (!(fnode = hpfs_alloc_fnode(dir->i_sb, dir->i_hpfs_dno, &fno, &bh))) goto bail;
+ if (!(dnode = hpfs_alloc_dnode(dir->i_sb, fno, &dno, &qbh0, 1))) goto bail1;
+ memset(&dee, 0, sizeof dee);
+ dee.directory = 1;
+ if (!(mode & 0222)) dee.read_only = 1;
+ /*dee.archive = 0;*/
+ dee.hidden = name[0] == '.';
+ dee.fnode = fno;
+ dee.creation_date = dee.write_date = dee.read_date = gmt_to_local(dir->i_sb, CURRENT_TIME);
+ hpfs_lock_inode(dir);
+ r = hpfs_add_dirent(dir, (char *)name, len, &dee, 0);
+ if (r == 1) goto bail2;
+ if (r == -1) {
+ brelse(bh);
+ hpfs_brelse4(&qbh0);
+ hpfs_free_sectors(dir->i_sb, fno, 1);
+ hpfs_free_dnode(dir->i_sb, dno);
+ hpfs_unlock_inode(dir);
+ return -EEXIST;
+ }
+ fnode->len = len;
+ memcpy(fnode->name, name, len > 15 ? 15 : len);
+ fnode->up = dir->i_ino;
+ fnode->dirflag = 1;
+ fnode->btree.n_free_nodes = 7;
+ fnode->btree.n_used_nodes = 1;
+ fnode->btree.first_free = 0x14;
+ fnode->u.external[0].disk_secno = dno;
+ fnode->u.external[0].file_secno = -1;
+ dnode->root_dnode = 1;
+ dnode->up = fno;
+ de = hpfs_add_de(dir->i_sb, dnode, "\001\001", 2, 0);
+ de->creation_date = de->write_date = de->read_date = gmt_to_local(dir->i_sb, CURRENT_TIME);
+ if (!(mode & 0222)) de->read_only = 1;
+ de->first = de->directory = 1;
+ /*de->hidden = de->system = 0;*/
+ de->fnode = fno;
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+ hpfs_mark_4buffers_dirty(&qbh0);
+ hpfs_brelse4(&qbh0);
+ dir->i_nlink++;
+ hpfs_lock_iget(dir->i_sb, 1);
+ if ((result = iget(dir->i_sb, fno))) {
+ result->i_hpfs_parent_dir = dir->i_ino;
+ result->i_ctime = result->i_mtime = result->i_atime = local_to_gmt(dir->i_sb, dee.creation_date);
+ result->i_hpfs_ea_size = 0;
+ if (dee.read_only) result->i_mode &= ~0222;
+ if (result->i_uid != current->fsuid ||
+ result->i_gid != current->fsgid ||
+ result->i_mode != (mode | S_IFDIR)) {
+ result->i_uid = current->fsuid;
+ result->i_gid = current->fsgid;
+ result->i_mode = mode | S_IFDIR;
+ hpfs_write_inode_nolock(result);
+ }
+ d_instantiate(dentry, result);
+ }
+ hpfs_unlock_iget(dir->i_sb);
+ hpfs_unlock_inode(dir);
+ return 0;
+ bail2:
+ hpfs_brelse4(&qbh0);
+ hpfs_free_dnode(dir->i_sb, dno);
+ hpfs_unlock_inode(dir);
+ bail1:
+ brelse(bh);
+ hpfs_free_sectors(dir->i_sb, fno, 1);
+ bail:
+ return -ENOSPC;
+}
+
+int hpfs_create(struct inode *dir, struct dentry *dentry, int mode)
+{
+ const char *name = dentry->d_name.name;
+ unsigned len = dentry->d_name.len;
+ struct inode *result = NULL;
+ struct buffer_head *bh;
+ struct fnode *fnode;
+ fnode_secno fno;
+ int r;
+ struct hpfs_dirent dee;
+ int err;
+ if ((err = hpfs_chk_name((char *)name, &len))) return err==-ENOENT ? -EINVAL : err;
+ if (!(fnode = hpfs_alloc_fnode(dir->i_sb, dir->i_hpfs_dno, &fno, &bh))) goto bail;
+ memset(&dee, 0, sizeof dee);
+ if (!(mode & 0222)) dee.read_only = 1;
+ dee.archive = 1;
+ dee.hidden = name[0] == '.';
+ dee.fnode = fno;
+ dee.creation_date = dee.write_date = dee.read_date = gmt_to_local(dir->i_sb, CURRENT_TIME);
+ hpfs_lock_inode(dir);
+ r = hpfs_add_dirent(dir, (char *)name, len, &dee, 0);
+ if (r == 1) goto bail1;
+ if (r == -1) {
+ brelse(bh);
+ hpfs_free_sectors(dir->i_sb, fno, 1);
+ hpfs_unlock_inode(dir);
+ return -EEXIST;
+ }
+ fnode->len = len;
+ memcpy(fnode->name, name, len > 15 ? 15 : len);
+ fnode->up = dir->i_ino;
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+ hpfs_lock_iget(dir->i_sb, 2);
+ if ((result = iget(dir->i_sb, fno))) {
+ hpfs_decide_conv(result, (char *)name, len);
+ result->i_hpfs_parent_dir = dir->i_ino;
+ result->i_ctime = result->i_mtime = result->i_atime = local_to_gmt(dir->i_sb, dee.creation_date);
+ result->i_hpfs_ea_size = 0;
+ if (dee.read_only) result->i_mode &= ~0222;
+ if (result->i_blocks == -1) result->i_blocks = 1;
+ if (result->i_size == -1) result->i_size = 0;
+ if (result->i_uid != current->fsuid ||
+ result->i_gid != current->fsgid ||
+ result->i_mode != (mode | S_IFREG)) {
+ result->i_uid = current->fsuid;
+ result->i_gid = current->fsgid;
+ result->i_mode = mode | S_IFREG;
+ hpfs_write_inode_nolock(result);
+ }
+ d_instantiate(dentry, result);
+ }
+ hpfs_unlock_iget(dir->i_sb);
+ hpfs_unlock_inode(dir);
+ return 0;
+ bail1:
+ brelse(bh);
+ hpfs_free_sectors(dir->i_sb, fno, 1);
+ hpfs_unlock_inode(dir);
+ bail:
+ return -ENOSPC;
+}
+
+int hpfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rdev)
+{
+ const char *name = dentry->d_name.name;
+ unsigned len = dentry->d_name.len;
+ struct buffer_head *bh;
+ struct fnode *fnode;
+ fnode_secno fno;
+ int r;
+ struct hpfs_dirent dee;
+ struct inode *result = NULL;
+ int err;
+ if ((err = hpfs_chk_name((char *)name, &len))) return err==-ENOENT ? -EINVAL : err;
+ if (dir->i_sb->s_hpfs_eas < 2) return -EPERM;
+ if (!(fnode = hpfs_alloc_fnode(dir->i_sb, dir->i_hpfs_dno, &fno, &bh))) goto bail;
+ memset(&dee, 0, sizeof dee);
+ if (!(mode & 0222)) dee.read_only = 1;
+ dee.archive = 1;
+ dee.hidden = name[0] == '.';
+ dee.fnode = fno;
+ dee.creation_date = dee.write_date = dee.read_date = gmt_to_local(dir->i_sb, CURRENT_TIME);
+ hpfs_lock_inode(dir);
+ r = hpfs_add_dirent(dir, (char *)name, len, &dee, 0);
+ if (r == 1) goto bail1;
+ if (r == -1) {
+ brelse(bh);
+ hpfs_free_sectors(dir->i_sb, fno, 1);
+ hpfs_unlock_inode(dir);
+ return -EEXIST;
+ }
+ fnode->len = len;
+ memcpy(fnode->name, name, len > 15 ? 15 : len);
+ fnode->up = dir->i_ino;
+ mark_buffer_dirty(bh, 1);
+ hpfs_lock_iget(dir->i_sb, 2);
+ if ((result = iget(dir->i_sb, fno))) {
+ result->i_hpfs_parent_dir = dir->i_ino;
+ result->i_ctime = result->i_mtime = result->i_atime = local_to_gmt(dir->i_sb, dee.creation_date);
+ result->i_hpfs_ea_size = 0;
+ /*if (result->i_blocks == -1) result->i_blocks = 1;
+ if (result->i_size == -1) result->i_size = 0;*/
+ result->i_uid = current->fsuid;
+ result->i_gid = current->fsgid;
+ result->i_nlink = 1;
+ result->i_size = 0;
+ result->i_blocks = 1;
+ init_special_inode(result, mode, rdev);
+ hpfs_write_inode_nolock(result);
+ d_instantiate(dentry, result);
+ }
+ hpfs_unlock_iget(dir->i_sb);
+ hpfs_unlock_inode(dir);
+ brelse(bh);
+ return 0;
+ bail1:
+ brelse(bh);
+ hpfs_free_sectors(dir->i_sb, fno, 1);
+ hpfs_unlock_inode(dir);
+ bail:
+ return -ENOSPC;
+}
+
+extern const struct inode_operations hpfs_symlink_iops;
+
+int hpfs_symlink(struct inode *dir, struct dentry *dentry, const char *symlink)
+{
+ const char *name = dentry->d_name.name;
+ unsigned len = dentry->d_name.len;
+ struct buffer_head *bh;
+ struct fnode *fnode;
+ fnode_secno fno;
+ int r;
+ struct hpfs_dirent dee;
+ struct inode *result;
+ int err;
+ if ((err = hpfs_chk_name((char *)name, &len))) return err==-ENOENT ? -EINVAL : err;
+ if (dir->i_sb->s_hpfs_eas < 2) return -EPERM;
+ if (!(fnode = hpfs_alloc_fnode(dir->i_sb, dir->i_hpfs_dno, &fno, &bh))) goto bail;
+ memset(&dee, 0, sizeof dee);
+ dee.archive = 1;
+ dee.hidden = name[0] == '.';
+ dee.fnode = fno;
+ dee.creation_date = dee.write_date = dee.read_date = gmt_to_local(dir->i_sb, CURRENT_TIME);
+ hpfs_lock_inode(dir);
+ r = hpfs_add_dirent(dir, (char *)name, len, &dee, 0);
+ if (r == 1) goto bail1;
+ if (r == -1) {
+ brelse(bh);
+ hpfs_free_sectors(dir->i_sb, fno, 1);
+ hpfs_unlock_inode(dir);
+ return -EEXIST;
+ }
+ fnode->len = len;
+ memcpy(fnode->name, name, len > 15 ? 15 : len);
+ fnode->up = dir->i_ino;
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+ hpfs_lock_iget(dir->i_sb, 2);
+ if ((result = iget(dir->i_sb, fno))) {
+ result->i_hpfs_parent_dir = dir->i_ino;
+ result->i_ctime = result->i_mtime = result->i_atime = local_to_gmt(dir->i_sb, dee.creation_date);
+ result->i_hpfs_ea_size = 0;
+ /*if (result->i_blocks == -1) result->i_blocks = 1;
+ if (result->i_size == -1) result->i_size = 0;*/
+ result->i_mode = S_IFLNK | 0777;
+ result->i_uid = current->fsuid;
+ result->i_gid = current->fsgid;
+ result->i_blocks = 1;
+ result->i_size = strlen(symlink);
+ result->i_op = (struct inode_operations *) &hpfs_symlink_iops;
+ if ((fnode = hpfs_map_fnode(dir->i_sb, fno, &bh))) {
+ hpfs_set_ea(result, fnode, "SYMLINK", (char *)symlink, strlen(symlink));
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+ }
+ hpfs_write_inode_nolock(result);
+ d_instantiate(dentry, result);
+ }
+ hpfs_unlock_iget(dir->i_sb);
+ hpfs_unlock_inode(dir);
+ return 0;
+ bail1:
+ brelse(bh);
+ hpfs_free_sectors(dir->i_sb, fno, 1);
+ hpfs_unlock_inode(dir);
+ bail:
+ return -ENOSPC;
+}
+
+int hpfs_unlink(struct inode *dir, struct dentry *dentry)
+{
+ const char *name = dentry->d_name.name;
+ unsigned len = dentry->d_name.len;
+ struct quad_buffer_head qbh;
+ struct hpfs_dirent *de;
+ struct inode *inode = dentry->d_inode;
+ dnode_secno dno;
+ fnode_secno fno;
+ int r;
+ int rep = 0;
+ hpfs_adjust_length((char *)name, &len);
+ again:
+ hpfs_lock_2inodes(dir, inode);
+ if (!(de = map_dirent(dir, dir->i_hpfs_dno, (char *)name, len, &dno, &qbh))) {
+ hpfs_unlock_2inodes(dir, inode);
+ return -ENOENT;
+ }
+ if (de->first) {
+ hpfs_brelse4(&qbh);
+ hpfs_unlock_2inodes(dir, inode);
+ return -EPERM;
+ }
+ if (de->directory) {
+ hpfs_brelse4(&qbh);
+ hpfs_unlock_2inodes(dir, inode);
+ return -EISDIR;
+ }
+ fno = de->fnode;
+ if ((r = hpfs_remove_dirent(dir, dno, de, &qbh, 1)) == 1) hpfs_error(dir->i_sb, "there was error when removing dirent");
+ if (r != 2) {
+ inode->i_nlink--;
+ hpfs_unlock_2inodes(dir, inode);
+ d_delete(dentry);
+ } else { /* no space for deleting, try to truncate file */
+ struct iattr newattrs;
+ hpfs_unlock_2inodes(dir, inode);
+ if (rep || dentry->d_count > 1 || permission(inode, MAY_WRITE) || get_write_access(inode)) goto ret;
+ /*printk("HPFS: truncating file before delete.\n");*/
+ down(&inode->i_sem); /* do_truncate should be called here, but it's */
+ newattrs.ia_size = 0; /* not exported */
+ newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME;
+ if (notify_change(dentry, &newattrs)) {
+ up(&inode->i_sem);
+ put_write_access(inode);
+ goto ret;
+ }
+ vmtruncate(inode, 0);
+ if (inode->i_op && inode->i_op->truncate) inode->i_op->truncate(inode);
+ up(&inode->i_sem);
+ put_write_access(inode);
+ rep = 1;
+ goto again;
+ }
+ ret:
+ return r == 2 ? -ENOSPC : r == 1 ? -EFSERROR : 0;
+}
+
+int hpfs_rmdir(struct inode *dir, struct dentry *dentry)
+{
+ const char *name = dentry->d_name.name;
+ unsigned len = dentry->d_name.len;
+ struct quad_buffer_head qbh;
+ struct hpfs_dirent *de;
+ struct inode *inode = dentry->d_inode;
+ dnode_secno dno;
+ fnode_secno fno;
+ int n_items = 0;
+ int r;
+ hpfs_adjust_length((char *)name, &len);
+ hpfs_lock_2inodes(dir, inode);
+ if (!(de = map_dirent(dir, dir->i_hpfs_dno, (char *)name, len, &dno, &qbh))) {
+ hpfs_unlock_2inodes(dir, inode);
+ return -ENOENT;
+ }
+ if (de->first) {
+ hpfs_brelse4(&qbh);
+ hpfs_unlock_2inodes(dir, inode);
+ return -EPERM;
+ }
+ if (!de->directory) {
+ hpfs_brelse4(&qbh);
+ hpfs_unlock_2inodes(dir, inode);
+ return -ENOTDIR;
+ }
+ if (!list_empty(&dentry->d_hash)) {
+ hpfs_brelse4(&qbh);
+ hpfs_unlock_2inodes(dir, inode);
+ return -EBUSY;
+ }
+ hpfs_count_dnodes(dir->i_sb, inode->i_hpfs_dno, NULL, NULL, &n_items);
+ if (n_items) {
+ hpfs_brelse4(&qbh);
+ hpfs_unlock_2inodes(dir, inode);
+ return -ENOTEMPTY;
+ }
+ fno = de->fnode;
+ if ((r = hpfs_remove_dirent(dir, dno, de, &qbh, 1)) == 1)
+ hpfs_error(dir->i_sb, "there was error when removing dirent");
+ if (r != 2) {
+ dir->i_nlink--;
+ inode->i_nlink = 0;
+ hpfs_unlock_2inodes(dir, inode);
+ d_delete(dentry);
+ } else hpfs_unlock_2inodes(dir, inode);
+ return r == 2 ? -ENOSPC : r == 1 ? -EFSERROR : 0;
+}
+
+int hpfs_readlink(struct dentry *dentry, char *buf, int len)
+{
+ struct inode *i = dentry->d_inode;
+ struct fnode *fnode;
+ struct buffer_head *bh;
+ char *symlink;
+ int slen;
+ if (!S_ISLNK(i->i_mode)) {
+ return -EINVAL;
+ }
+ if (!(fnode = hpfs_map_fnode(i->i_sb, i->i_ino, &bh))) {
+ return -EIO;
+ }
+ if (!(symlink = hpfs_get_ea(i->i_sb, fnode, "SYMLINK", &slen))) {
+ brelse(bh);
+ return -EFSERROR;
+ }
+ brelse(bh);
+ if (slen > len) slen = len;
+ memcpy_tofs(buf, symlink, slen);
+ kfree(symlink);
+ return slen;
+}
+
+struct dentry *hpfs_follow_link(struct dentry *dinode, struct dentry *ddir,
+ unsigned int follow)
+{
+ struct inode *inode = dinode->d_inode;
+ char *link;
+ unsigned len;
+ struct buffer_head *bh;
+ struct fnode *fnode;
+ if (!(fnode = hpfs_map_fnode(inode->i_sb, inode->i_ino, &bh))) {
+ dput(dinode);
+ return ERR_PTR(-EIO);
+ }
+ if (!(link = hpfs_get_ea(inode->i_sb, fnode, "SYMLINK", &len))) {
+ brelse(bh);
+ dput(dinode);
+ return ERR_PTR(-EIO);
+ }
+ brelse(bh);
+ UPDATE_ATIME(inode);
+ ddir = lookup_dentry(link, ddir, follow);
+ kfree(link);
+ return ddir;
+}
+
+int hpfs_rename(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry)
+{
+ char *old_name = (char *)old_dentry->d_name.name;
+ int old_len = old_dentry->d_name.len;
+ char *new_name = (char *)new_dentry->d_name.name;
+ int new_len = new_dentry->d_name.len;
+ struct inode *i = old_dentry->d_inode;
+ struct inode *new_inode = new_dentry->d_inode;
+ struct quad_buffer_head qbh, qbh1;
+ struct hpfs_dirent *dep, *nde;
+ struct hpfs_dirent de;
+ dnode_secno dno;
+ int r;
+ struct buffer_head *bh;
+ struct fnode *fnode;
+ int err;
+ if ((err = hpfs_chk_name((char *)new_name, &new_len))) return err;
+ err = 0;
+ hpfs_adjust_length((char *)old_name, &old_len);
+
+ hpfs_lock_3inodes(old_dir, new_dir, i);
+
+ /* Erm? Moving over the empty non-busy directory is perfectly legal */
+ if (new_inode && S_ISDIR(new_inode->i_mode)) {
+ err = -EINVAL;
+ goto end1;
+ }
+
+ if (!(dep = map_dirent(old_dir, old_dir->i_hpfs_dno, (char *)old_name, old_len, &dno, &qbh))) {
+ hpfs_error(i->i_sb, "lookup succeeded but map dirent failed");
+ err = -ENOENT;
+ goto end1;
+ }
+ copy_de(&de, dep);
+ de.hidden = new_name[0] == '.';
+
+ if (new_inode) {
+ int r;
+ if ((r = hpfs_remove_dirent(old_dir, dno, dep, &qbh, 1)) != 2) {
+ if ((nde = map_dirent(new_dir, new_dir->i_hpfs_dno, (char *)new_name, new_len, NULL, &qbh1))) {
+ new_inode->i_nlink = 0;
+ copy_de(nde, &de);
+ memcpy(nde->name, new_name, new_len);
+ hpfs_mark_4buffers_dirty(&qbh1);
+ hpfs_brelse4(&qbh1);
+ goto end;
+ }
+ hpfs_error(new_dir->i_sb, "hpfs_rename: could not find dirent");
+ err = -EFSERROR;
+ goto end1;
+ }
+ err = r == 2 ? -ENOSPC : r == 1 ? -EFSERROR : 0;
+ goto end1;
+ }
+
+ if (new_dir == old_dir) hpfs_brelse4(&qbh);
+
+ hpfs_lock_creation(i->i_sb);
+ if ((r = hpfs_add_dirent(new_dir, new_name, new_len, &de, 1))) {
+ hpfs_unlock_creation(i->i_sb);
+ if (r == -1) hpfs_error(new_dir->i_sb, "hpfs_rename: dirent already exists!");
+ err = r == 1 ? -ENOSPC : -EFSERROR;
+ if (new_dir != old_dir) hpfs_brelse4(&qbh);
+ goto end1;
+ }
+
+ if (new_dir == old_dir)
+ if (!(dep = map_dirent(old_dir, old_dir->i_hpfs_dno, (char *)old_name, old_len, &dno, &qbh))) {
+ hpfs_unlock_creation(i->i_sb);
+ hpfs_error(i->i_sb, "lookup succeeded but map dirent failed at #2");
+ err = -ENOENT;
+ goto end1;
+ }
+
+ if ((r = hpfs_remove_dirent(old_dir, dno, dep, &qbh, 0))) {
+ hpfs_unlock_creation(i->i_sb);
+ hpfs_error(i->i_sb, "hpfs_rename: could not remove dirent");
+ err = r == 2 ? -ENOSPC : -EFSERROR;
+ goto end1;
+ }
+ hpfs_unlock_creation(i->i_sb);
+
+ end:
+ i->i_hpfs_parent_dir = new_dir->i_ino;
+ if (S_ISDIR(i->i_mode)) {
+ new_dir->i_nlink++;
+ old_dir->i_nlink--;
+ }
+ if ((fnode = hpfs_map_fnode(i->i_sb, i->i_ino, &bh))) {
+ fnode->up = new_dir->i_ino;
+ fnode->len = new_len;
+ memcpy(fnode->name, new_name, new_len>15?15:new_len);
+ if (new_len < 15) memset(&fnode->name[new_len], 0, 15 - new_len);
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+ }
+ i->i_hpfs_conv = i->i_sb->s_hpfs_conv;
+ hpfs_decide_conv(i, (char *)new_name, new_len);
+ end1:
+ hpfs_unlock_3inodes(old_dir, new_dir, i);
+ return err;
+}
diff --git a/fs/hpfs/super.c b/fs/hpfs/super.c
new file mode 100644
index 000000000..a50f2a49b
--- /dev/null
+++ b/fs/hpfs/super.c
@@ -0,0 +1,598 @@
+/*
+ * linux/fs/hpfs/super.c
+ *
+ * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
+ *
+ * mouning, unmounting, error handling
+ */
+
+#include <linux/string.h>
+#include "hpfs_fn.h"
+#include <linux/module.h>
+
+/* Mark the filesystem dirty, so that chkdsk checks it when os/2 booted */
+
+static void mark_dirty(struct super_block *s)
+{
+ if (s->s_hpfs_chkdsk && !(s->s_flags & MS_RDONLY)) {
+ struct buffer_head *bh;
+ struct hpfs_spare_block *sb;
+ if ((sb = hpfs_map_sector(s, 17, &bh, 0))) {
+ sb->dirty = 1;
+ sb->old_wrote = 0;
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+ }
+ }
+}
+
+/* Mark the filesystem clean (mark it dirty for chkdsk if chkdsk==2 or if there
+ were errors) */
+
+static void unmark_dirty(struct super_block *s)
+{
+ struct buffer_head *bh;
+ struct hpfs_spare_block *sb;
+ if (s->s_flags & MS_RDONLY) return;
+ if ((sb = hpfs_map_sector(s, 17, &bh, 0))) {
+ sb->dirty = s->s_hpfs_chkdsk > 1 - s->s_hpfs_was_error;
+ sb->old_wrote = s->s_hpfs_chkdsk >= 2 && !s->s_hpfs_was_error;
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+ }
+}
+
+/* Filesystem error... */
+
+#define ERR_BUF_SIZE 1024
+
+void hpfs_error(struct super_block *s, char *m,...)
+{
+ char *buf;
+ va_list l;
+ va_start(l, m);
+ if (!(buf = kmalloc(ERR_BUF_SIZE, GFP_KERNEL)))
+ printk("HPFS: No memory for error message '%s'\n",m);
+ else if (vsprintf(buf, m, l) >= ERR_BUF_SIZE)
+ printk("HPFS: Grrrr... Kernel memory corrupted ... going on, but it'll crash very soon :-(\n");
+ printk("HPFS: filesystem error: ");
+ if (buf) printk("%s", buf);
+ else printk("%s\n",m);
+ if (!s->s_hpfs_was_error) {
+ if (s->s_hpfs_err == 2) {
+ printk("; crashing the system because you wanted it\n");
+ mark_dirty(s);
+ panic("HPFS panic");
+ } else if (s->s_hpfs_err == 1) {
+ if (s->s_flags & MS_RDONLY) printk("; already mounted read-only\n");
+ else {
+ printk("; remounting read-only\n");
+ mark_dirty(s);
+ s->s_flags |= MS_RDONLY;
+ }
+ } else if (s->s_flags & MS_RDONLY) printk("; going on - but anything won't be destroyed because it's read-only\n");
+ else printk("; corrupted filesystem mounted read/write - your computer will explode within 20 seconds ... but you wanted it so!\n");
+ } else printk("\n");
+ if (buf) kfree(buf);
+ s->s_hpfs_was_error = 1;
+}
+
+/*
+ * A little trick to detect cycles in many hpfs structures and don't let the
+ * kernel crash on corrupted filesystem. When first called, set c2 to 0.
+ *
+ * BTW. chkdsk doesn't detect cycles correctly. When I had 2 lost directories
+ * nested each in other, chkdsk locked up happilly.
+ */
+
+int hpfs_stop_cycles(struct super_block *s, int key, int *c1, int *c2,
+ char *msg)
+{
+ if (*c2 && *c1 == key) {
+ hpfs_error(s, "cycle detected on key %08x in %s", key, msg);
+ return 1;
+ }
+ (*c2)++;
+ if (!((*c2 - 1) & *c2)) *c1 = key;
+ return 0;
+}
+
+void hpfs_put_super(struct super_block *s)
+{
+ if (s->s_hpfs_cp_table) kfree(s->s_hpfs_cp_table);
+ if (s->s_hpfs_bmp_dir) kfree(s->s_hpfs_bmp_dir);
+ unmark_dirty(s);
+ s->s_dev = 0;
+ MOD_DEC_USE_COUNT;
+}
+
+unsigned hpfs_count_one_bitmap(struct super_block *s, secno secno)
+{
+ struct quad_buffer_head qbh;
+ unsigned *bits;
+ unsigned i, count;
+ if (!(bits = hpfs_map_4sectors(s, secno, &qbh, 4))) return 0;
+ count = 0;
+ for (i = 0; i < 2048 / sizeof(unsigned); i++) {
+ unsigned b;
+ if (!bits[i]) continue;
+ for (b = bits[i]; b; b>>=1) count += b & 1;
+ }
+ hpfs_brelse4(&qbh);
+ return count;
+}
+
+static unsigned count_bitmaps(struct super_block *s)
+{
+ unsigned n, count, n_bands;
+ n_bands = (s->s_hpfs_fs_size + 0x3fff) >> 14;
+ count = 0;
+ for (n = 0; n < n_bands; n++)
+ count += hpfs_count_one_bitmap(s, s->s_hpfs_bmp_dir[n]);
+ return count;
+}
+
+int hpfs_statfs(struct super_block *s, struct statfs *buf, int bufsiz)
+{
+ struct statfs tmp;
+ /*if (s->s_hpfs_n_free == -1) {*/
+ s->s_hpfs_n_free = count_bitmaps(s);
+ s->s_hpfs_n_free_dnodes = hpfs_count_one_bitmap(s, s->s_hpfs_dmap);
+ /*}*/
+ tmp.f_type = s->s_magic;
+ tmp.f_bsize = 512;
+ tmp.f_blocks = s->s_hpfs_fs_size;
+ tmp.f_bfree = s->s_hpfs_n_free;
+ tmp.f_bavail = s->s_hpfs_n_free;
+ tmp.f_files = s->s_hpfs_dirband_size / 4;
+ tmp.f_ffree = s->s_hpfs_n_free_dnodes;
+ tmp.f_namelen = 254;
+ return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0;
+}
+
+/* Super operations */
+
+static const struct super_operations hpfs_sops =
+{
+ hpfs_read_inode, /* read_inode */
+ NULL, /* write_inode */
+ NULL, /* put_inode */
+ hpfs_delete_inode, /* delete inode */
+ hpfs_notify_change, /* notify_change */
+ hpfs_put_super, /* put_super */
+ NULL, /* write_super */
+ hpfs_statfs, /* statfs */
+ hpfs_remount_fs, /* remount_fs */
+ NULL, /* clear inode */
+ NULL, /* umount_begin */
+};
+
+/*
+ * A tiny parser for option strings, stolen from dosfs.
+ *
+ * Stolen again from read-only hpfs.
+ */
+
+int parse_opts(char *opts, uid_t *uid, gid_t *gid, umode_t *umask,
+ int *lowercase, int *conv, int *eas, int *chk, int *errs,
+ int *chkdsk, int *timeshift)
+{
+ char *p, *rhs;
+
+ if (!opts)
+ return 1;
+
+ /*printk("Parsing opts: '%s'\n",opts);*/
+
+ for (p = strtok(opts, ","); p != 0; p = strtok(0, ",")) {
+ if ((rhs = strchr(p, '=')) != 0)
+ *rhs++ = '\0';
+ if (!strcmp(p, "help")) return 2;
+ if (!strcmp(p, "uid")) {
+ if (!rhs || !*rhs)
+ return 0;
+ *uid = simple_strtoul(rhs, &rhs, 0);
+ if (*rhs)
+ return 0;
+ }
+ else if (!strcmp(p, "gid")) {
+ if (!rhs || !*rhs)
+ return 0;
+ *gid = simple_strtoul(rhs, &rhs, 0);
+ if (*rhs)
+ return 0;
+ }
+ else if (!strcmp(p, "umask")) {
+ if (!rhs || !*rhs)
+ return 0;
+ *umask = simple_strtoul(rhs, &rhs, 8);
+ if (*rhs)
+ return 0;
+ }
+ else if (!strcmp(p, "timeshift")) {
+ int m = 1;
+ if (!rhs || !*rhs)
+ return 0;
+ if (*rhs == '-') m = -1;
+ if (*rhs == '+' || *rhs == '-') rhs++;
+ *timeshift = simple_strtoul(rhs, &rhs, 0) * m;
+ if (*rhs)
+ return 0;
+ }
+ else if (!strcmp(p, "case")) {
+ if (!strcmp(rhs, "lower"))
+ *lowercase = 1;
+ else if (!strcmp(rhs, "asis"))
+ *lowercase = 0;
+ else
+ return 0;
+ }
+ else if (!strcmp(p, "conv")) {
+ if (!strcmp(rhs, "binary"))
+ *conv = CONV_BINARY;
+ else if (!strcmp(rhs, "text"))
+ *conv = CONV_TEXT;
+ else if (!strcmp(rhs, "auto"))
+ *conv = CONV_AUTO;
+ else
+ return 0;
+ }
+ else if (!strcmp(p, "check")) {
+ if (!strcmp(rhs, "none"))
+ *chk = 0;
+ else if (!strcmp(rhs, "normal"))
+ *chk = 1;
+ else if (!strcmp(rhs, "strict"))
+ *chk = 2;
+ else
+ return 0;
+ }
+ else if (!strcmp(p, "errors")) {
+ if (!strcmp(rhs, "continue"))
+ *errs = 0;
+ else if (!strcmp(rhs, "remount-ro"))
+ *errs = 1;
+ else if (!strcmp(rhs, "panic"))
+ *errs = 2;
+ else
+ return 0;
+ }
+ else if (!strcmp(p, "eas")) {
+ if (!strcmp(rhs, "no"))
+ *eas = 0;
+ else if (!strcmp(rhs, "ro"))
+ *eas = 1;
+ else if (!strcmp(rhs, "rw"))
+ *eas = 2;
+ else
+ return 0;
+ }
+ else if (!strcmp(p, "chkdsk")) {
+ if (!strcmp(rhs, "no"))
+ *chkdsk = 0;
+ else if (!strcmp(rhs, "errors"))
+ *chkdsk = 1;
+ else if (!strcmp(rhs, "always"))
+ *chkdsk = 2;
+ else
+ return 0;
+ }
+ else
+ return 0;
+ }
+ return 1;
+}
+
+static inline void hpfs_help()
+{
+ printk("\n\
+HPFS filesystem options:\n\
+ help do not mount and display this text\n\
+ uid=xxx set uid of files that don't have uid specified in eas\n\
+ gid=xxx set gid of files that don't have gid specified in eas\n\
+ umask=xxx set mode of files that don't have mode specified in eas\n\
+ case=lower lowercase all files\n\
+ case=asis do not lowercase files (default)\n\
+ conv=binary do not convert CR/LF -> LF (default)\n\
+ conv=auto convert only files with known text extensions\n\
+ conv=text convert all files\n\
+ check=none no fs checks - kernel may crash on corrupted filesystem\n\
+ check=normal do some checks - it should not crash (default)\n\
+ check=strict do extra time-consuming checks, used for debugging\n\
+ errors=continue continue on errors\n\
+ errors=remount-ro remount read-only if errors found (default)\n\
+ errors=panic panic on errors\n\
+ chkdsk=no do not mark fs for chkdsking even if there were errors\n\
+ chkdsk=errors mark fs dirty if errors found (default)\n\
+ chkdsk=always always mark fs dirty - used for debugging\n\
+ eas=no ignore extended attributes\n\
+ eas=ro read but do not write extended attributes\n\
+ eas=rw r/w eas => enables chmod, chown, mknod, ln -s (default)\n\
+ timeshift=nnn add nnn seconds to file times\n\
+\n");
+}
+
+int hpfs_remount_fs(struct super_block *s, int *flags, char *data)
+{
+ uid_t uid;
+ gid_t gid;
+ umode_t umask;
+ int lowercase, conv, eas, chk, errs, chkdsk, timeshift;
+ int o;
+
+ *flags |= MS_NOATIME;
+
+ uid = s->s_hpfs_uid; gid = s->s_hpfs_gid;
+ umask = 0777 & ~s->s_hpfs_mode;
+ lowercase = s->s_hpfs_lowercase; conv = s->s_hpfs_conv;
+ eas = s->s_hpfs_eas; chk = s->s_hpfs_chk; chkdsk = s->s_hpfs_chkdsk;
+ errs = s->s_hpfs_err; timeshift = s->s_hpfs_timeshift;
+
+ if (!(o = parse_opts(data, &uid, &gid, &umask, &lowercase, &conv,
+ &eas, &chk, &errs, &chkdsk, &timeshift))) {
+ printk("HPFS: bad mount options.\n");
+ return 1;
+ }
+ if (o == 2) {
+ hpfs_help();
+ return 1;
+ }
+ if (timeshift != s->s_hpfs_timeshift) {
+ printk("HPFS: timeshift can't be changed using remount.\n");
+ return 1;
+ }
+
+ unmark_dirty(s);
+
+ s->s_hpfs_uid = uid; s->s_hpfs_gid = gid;
+ s->s_hpfs_mode = 0777 & ~umask;
+ s->s_hpfs_lowercase = lowercase; s->s_hpfs_conv = conv;
+ s->s_hpfs_eas = eas; s->s_hpfs_chk = chk; s->s_hpfs_chkdsk = chkdsk;
+ s->s_hpfs_err = errs; s->s_hpfs_timeshift = timeshift;
+
+ if (!(*flags & MS_RDONLY)) mark_dirty(s);
+
+ return 0;
+}
+
+struct super_block *hpfs_read_super(struct super_block *s, void *options,
+ int silent)
+{
+ kdev_t dev;
+ struct buffer_head *bh0, *bh1, *bh2;
+ struct hpfs_boot_block *bootblock;
+ struct hpfs_super_block *superblock;
+ struct hpfs_spare_block *spareblock;
+
+ uid_t uid;
+ gid_t gid;
+ umode_t umask;
+ int lowercase, conv, eas, chk, errs, chkdsk, timeshift;
+
+ dnode_secno root_dno;
+ struct hpfs_dirent *de = NULL;
+ struct quad_buffer_head qbh;
+
+ int o;
+
+ MOD_INC_USE_COUNT;
+
+ s->s_hpfs_bmp_dir = NULL;
+ s->s_hpfs_cp_table = NULL;
+
+ s->s_hpfs_creation_de_lock = s->s_hpfs_rd_inode = 0;
+ init_waitqueue_head(&s->s_hpfs_creation_de);
+ init_waitqueue_head(&s->s_hpfs_iget_q);
+
+ uid = current->uid;
+ gid = current->gid;
+ umask = current->fs->umask;
+ lowercase = 0;
+ conv = CONV_BINARY;
+ eas = 2;
+ chk = 1;
+ errs = 1;
+ chkdsk = 1;
+ timeshift = 0;
+
+ if (!(o = parse_opts(options, &uid, &gid, &umask, &lowercase, &conv,
+ &eas, &chk, &errs, &chkdsk, &timeshift))) {
+ printk("HPFS: bad mount options.\n");
+ goto bail0;
+ }
+ if (o==2) {
+ hpfs_help();
+ goto bail0;
+ }
+
+ /*s->s_hpfs_mounting = 1;*/
+ lock_super(s);
+ dev = s->s_dev;
+ set_blocksize(dev, 512);
+ s->s_hpfs_fs_size = -1;
+ if (!(bootblock = hpfs_map_sector(s, 0, &bh0, 0))) goto bail1;
+ if (!(superblock = hpfs_map_sector(s, 16, &bh1, 1))) goto bail2;
+ if (!(spareblock = hpfs_map_sector(s, 17, &bh2, 0))) goto bail3;
+
+ /* Check magics */
+ if (/*bootblock->magic != BB_MAGIC
+ ||*/ superblock->magic != SB_MAGIC
+ || spareblock->magic != SP_MAGIC) {
+ if (!silent) printk("HPFS: Bad magic ... probably not HPFS\n");
+ goto bail4;
+ }
+
+ /* Check version */
+ if (!(s->s_flags & MS_RDONLY) &&
+ superblock->funcversion != 2 && superblock->funcversion != 3) {
+ printk("HPFS: Bad version %d,%d. Mount readonly to go around\n",
+ (int)superblock->version, (int)superblock->funcversion);
+ printk("HPFS: please try recent version of HPFS driver at http://artax.karlin.mff.cuni.cz/~mikulas/vyplody/hpfs/index-e.cgi and if it still can't understand this format, contact author - mikulas@artax.karlin.mff.cuni.cz\n");
+ goto bail4;
+ }
+
+ s->s_flags |= MS_NOATIME;
+
+ /* Fill superblock stuff */
+ s->s_magic = HPFS_SUPER_MAGIC;
+ s->s_blocksize = 512;
+ s->s_blocksize_bits = 9;
+ s->s_op = (struct super_operations *) &hpfs_sops;
+
+ s->s_hpfs_root = superblock->root;
+ s->s_hpfs_fs_size = superblock->n_sectors;
+ s->s_hpfs_bitmaps = superblock->bitmaps;
+ s->s_hpfs_dirband_start = superblock->dir_band_start;
+ s->s_hpfs_dirband_size = superblock->n_dir_band;
+ s->s_hpfs_dmap = superblock->dir_band_bitmap;
+ s->s_hpfs_uid = uid;
+ s->s_hpfs_gid = gid;
+ s->s_hpfs_mode = 0777 & ~umask;
+ s->s_hpfs_n_free = -1;
+ s->s_hpfs_n_free_dnodes = -1;
+ s->s_hpfs_lowercase = lowercase;
+ s->s_hpfs_conv = conv;
+ s->s_hpfs_eas = eas;
+ s->s_hpfs_chk = chk;
+ s->s_hpfs_chkdsk = chkdsk;
+ s->s_hpfs_err = errs;
+ s->s_hpfs_timeshift = timeshift;
+ s->s_hpfs_was_error = 0;
+ s->s_hpfs_cp_table = NULL;
+ s->s_hpfs_c_bitmap = -1;
+
+ /* Load bitmap directory */
+ if (!(s->s_hpfs_bmp_dir = hpfs_load_bitmap_directory(s, superblock->bitmaps)))
+ goto bail4;
+
+ /* Check for general fs errors*/
+ if (spareblock->dirty && !spareblock->old_wrote) {
+ if (errs == 2) {
+ printk("HPFS: Improperly stopped, not mounted\n");
+ goto bail4;
+ }
+ hpfs_error(s, "improperly stopped");
+ }
+
+ if (!(s->s_flags & MS_RDONLY)) {
+ spareblock->dirty = 1;
+ spareblock->old_wrote = 0;
+ mark_buffer_dirty(bh2, 1);
+ }
+
+ if (spareblock->hotfixes_used || spareblock->n_spares_used) {
+ if (errs >= 2) {
+ printk("HPFS: Hotfixes not supported here, try chkdsk\n");
+ mark_dirty(s);
+ goto bail4;
+ }
+ hpfs_error(s, "hotfixes not supported here, try chkdsk");
+ if (errs == 0) printk("HPFS: Proceeding, but your filesystem will be probably corrupted by this driver...\n");
+ else printk("HPFS: This driver may read bad files or crash when operating on disk with hotfixes.\n");
+ }
+ if (spareblock->n_dnode_spares != spareblock->n_dnode_spares_free) {
+ if (errs >= 2) {
+ printk("HPFS: Spare dnodes used, try chkdsk\n");
+ mark_dirty(s);
+ goto bail4;
+ }
+ hpfs_error(s, "warning: spare dnodes used, try chkdsk");
+ if (errs == 0) printk("HPFS: Proceeding, but your filesystem could be corrupted if you delete files or directories\n");
+ }
+ if (chk) {
+ unsigned a;
+ if (superblock->dir_band_end - superblock->dir_band_start + 1 != superblock->n_dir_band ||
+ superblock->dir_band_end < superblock->dir_band_start || superblock->n_dir_band > 0x4000) {
+ hpfs_error(s, "dir band size mismatch: dir_band_start==%08x, dir_band_end==%08x, n_dir_band==%08x",
+ superblock->dir_band_start, superblock->dir_band_end, superblock->n_dir_band);
+ goto bail4;
+ }
+ a = s->s_hpfs_dirband_size;
+ s->s_hpfs_dirband_size = 0;
+ if (hpfs_chk_sectors(s, superblock->dir_band_start, superblock->n_dir_band, "dir_band") ||
+ hpfs_chk_sectors(s, superblock->dir_band_bitmap, 4, "dir_band_bitmap") ||
+ hpfs_chk_sectors(s, superblock->bitmaps, 4, "bitmaps")) {
+ mark_dirty(s);
+ goto bail4;
+ }
+ s->s_hpfs_dirband_size = a;
+ } else printk("HPFS: You really don't want any checks? You are crazy...\n");
+
+ /* Load code page table */
+ if (spareblock->n_code_pages)
+ if (!(s->s_hpfs_cp_table = hpfs_load_code_page(s, spareblock->code_page_dir)))
+ printk("HPFS: Warning: code page support is disabled\n");
+
+ brelse(bh2);
+ brelse(bh1);
+ brelse(bh0);
+
+ hpfs_lock_iget(s, 1);
+ s->s_root = d_alloc_root(iget(s, s->s_hpfs_root));
+ hpfs_unlock_iget(s);
+ unlock_super(s);
+ if (!s->s_root || !s->s_root->d_inode) {
+ printk("HPFS: iget failed. Why???\n");
+ goto bail0;
+ }
+ hpfs_set_dentry_operations(s->s_root);
+
+ /*
+ * find the root directory's . pointer & finish filling in the inode
+ */
+
+ root_dno = hpfs_fnode_dno(s, s->s_hpfs_root);
+ if (root_dno)
+ de = map_dirent(s->s_root->d_inode, root_dno, "\001\001", 2, NULL, &qbh);
+ if (!root_dno || !de) hpfs_error(s, "unable to find root dir");
+ else {
+ s->s_root->d_inode->i_atime = local_to_gmt(s, de->read_date);
+ s->s_root->d_inode->i_mtime = local_to_gmt(s, de->write_date);
+ s->s_root->d_inode->i_ctime = local_to_gmt(s, de->creation_date);
+ s->s_root->d_inode->i_hpfs_ea_size = de->ea_size;
+ s->s_root->d_inode->i_hpfs_parent_dir = s->s_root->d_inode->i_ino;
+ if (s->s_root->d_inode->i_size == -1) s->s_root->d_inode->i_size = 2048;
+ if (s->s_root->d_inode->i_blocks == -1) s->s_root->d_inode->i_blocks = 5;
+ }
+ if (de) hpfs_brelse4(&qbh);
+
+ return s;
+
+bail4: brelse(bh2);
+bail3: brelse(bh1);
+bail2: brelse(bh0);
+bail1: unlock_super(s);
+bail0: s->s_dev = 0;
+ if (s->s_hpfs_bmp_dir) kfree(s->s_hpfs_bmp_dir);
+ if (s->s_hpfs_cp_table) kfree(s->s_hpfs_cp_table);
+ MOD_DEC_USE_COUNT;
+ return NULL;
+}
+
+struct file_system_type hpfs_fs_type = {
+ "hpfs", FS_REQUIRES_DEV, hpfs_read_super, NULL
+};
+
+int init_hpfs_fs(void)
+{
+ return register_filesystem(&hpfs_fs_type);
+}
+
+#ifdef MODULE
+
+/*int register_symtab_from(struct symbol_table *, long *);*/
+
+int init_module(void)
+{
+ /*int status;
+ if (!(status = init_hpfs_fs())) register_symtab(NULL);
+ return status;*/
+ return init_hpfs_fs();
+}
+
+void cleanup_module(void)
+{
+ unregister_filesystem(&hpfs_fs_type);
+}
+
+#endif
diff --git a/fs/proc/sysvipc.c b/fs/proc/sysvipc.c
new file mode 100644
index 000000000..eab3e3186
--- /dev/null
+++ b/fs/proc/sysvipc.c
@@ -0,0 +1,138 @@
+/*
+ * linux/fs/proc/sysvipc.c
+ *
+ * Copyright (c) 1999 Dragos Acostachioaie
+ *
+ * This code is derived from linux/fs/proc/generic.c,
+ * which is Copyright (C) 1991, 1992 Linus Torvalds.
+ *
+ * /proc/sysvipc directory handling functions
+ */
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/mm.h>
+
+#include <asm/uaccess.h>
+
+#ifndef MIN
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
+/* 4K page size but our output routines use some slack for overruns */
+#define PROC_BLOCK_SIZE (3*1024)
+
+static ssize_t
+proc_sysvipc_read(struct file * file, char * buf, size_t nbytes, loff_t *ppos)
+{
+ struct inode * inode = file->f_dentry->d_inode;
+ char *page;
+ ssize_t retval=0;
+ int eof=0;
+ ssize_t n, count;
+ char *start;
+ struct proc_dir_entry * dp;
+
+ dp = (struct proc_dir_entry *) inode->u.generic_ip;
+ if (!(page = (char*) __get_free_page(GFP_KERNEL)))
+ return -ENOMEM;
+
+ while ((nbytes > 0) && !eof)
+ {
+ count = MIN(PROC_BLOCK_SIZE, nbytes);
+
+ start = NULL;
+ if (dp->get_info) {
+ /*
+ * Handle backwards compatibility with the old net
+ * routines.
+ *
+ * XXX What gives with the file->f_flags & O_ACCMODE
+ * test? Seems stupid to me....
+ */
+ n = dp->get_info(page, &start, *ppos, count,
+ (file->f_flags & O_ACCMODE) == O_RDWR);
+ if (n < count)
+ eof = 1;
+ } else if (dp->read_proc) {
+ n = dp->read_proc(page, &start, *ppos,
+ count, &eof, dp->data);
+ } else
+ break;
+
+ if (!start) {
+ /*
+ * For proc files that are less than 4k
+ */
+ start = page + *ppos;
+ n -= *ppos;
+ if (n <= 0)
+ break;
+ if (n > count)
+ n = count;
+ }
+ if (n == 0)
+ break; /* End of file */
+ if (n < 0) {
+ if (retval == 0)
+ retval = n;
+ break;
+ }
+
+ /* This is a hack to allow mangling of file pos independent
+ * of actual bytes read. Simply place the data at page,
+ * return the bytes, and set `start' to the desired offset
+ * as an unsigned int. - Paul.Russell@rustcorp.com.au
+ */
+ n -= copy_to_user(buf, start < page ? page : start, n);
+ if (n == 0) {
+ if (retval == 0)
+ retval = -EFAULT;
+ break;
+ }
+
+ *ppos += start < page ? (long)start : n; /* Move down the file */
+ nbytes -= n;
+ buf += n;
+ retval += n;
+ }
+ free_page((unsigned long) page);
+ return retval;
+}
+
+static struct file_operations proc_sysvipc_operations = {
+ NULL, /* lseek */
+ proc_sysvipc_read, /* read */
+ NULL, /* write */
+ NULL, /* readdir */
+ NULL, /* poll */
+ NULL, /* ioctl */
+ NULL, /* mmap */
+ NULL, /* no special open code */
+ NULL, /* no special release code */
+ NULL /* can't fsync */
+};
+
+/*
+ * proc directories can do almost nothing..
+ */
+struct inode_operations proc_sysvipc_inode_operations = {
+ &proc_sysvipc_operations, /* default net file-ops */
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL /* permission */
+};