summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRussell King <rmk@flint.arm.linux.org.uk>2002-05-13 02:14:57 +0100
committerRussell King <rmk@flint.arm.linux.org.uk>2002-05-13 02:14:57 +0100
commitb918ced48922920d562ff17935bc1363ca020035 (patch)
treeff88df9f8b3d6ab4341462c55cc18c836b06fe4c
parent34dc307ad5fd2af8ee41469f587c34885d554c83 (diff)
[ARM] ADFS updates/fixes.
Fixes lockup on SMP boxes, and fixes buggy map scanning code.
-rw-r--r--fs/adfs/adfs.h2
-rw-r--r--fs/adfs/dir.c2
-rw-r--r--fs/adfs/map.c100
-rw-r--r--fs/adfs/super.c34
4 files changed, 73 insertions, 65 deletions
diff --git a/fs/adfs/adfs.h b/fs/adfs/adfs.h
index 720b707a34e0..1d6792fbcb89 100644
--- a/fs/adfs/adfs.h
+++ b/fs/adfs/adfs.h
@@ -77,7 +77,7 @@ void adfs_write_inode(struct inode *inode,int unused);
int adfs_notify_change(struct dentry *dentry, struct iattr *attr);
/* map.c */
-extern int adfs_map_lookup(struct super_block *sb, int frag_id, int offset);
+extern int adfs_map_lookup(struct super_block *sb, unsigned int frag_id, unsigned int offset);
extern unsigned int adfs_map_free(struct super_block *sb);
/* Misc */
diff --git a/fs/adfs/dir.c b/fs/adfs/dir.c
index e50456ba1e69..fe1b273ef8a5 100644
--- a/fs/adfs/dir.c
+++ b/fs/adfs/dir.c
@@ -24,7 +24,7 @@
/*
* For future. This should probably be per-directory.
*/
-static rwlock_t adfs_dir_lock;
+static rwlock_t adfs_dir_lock = RW_LOCK_UNLOCKED;
static int
adfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
diff --git a/fs/adfs/map.c b/fs/adfs/map.c
index ce36fcd25124..666a2678edde 100644
--- a/fs/adfs/map.c
+++ b/fs/adfs/map.c
@@ -1,7 +1,7 @@
/*
* linux/fs/adfs/map.c
*
- * Copyright (C) 1997-1999 Russell King
+ * Copyright (C) 1997-2002 Russell King
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -13,30 +13,64 @@
#include <linux/adfs_fs.h>
#include <linux/spinlock.h>
+#include <asm/unaligned.h>
+
#include "adfs.h"
/*
+ * The ADFS map is basically a set of sectors. Each sector is called a
+ * zone which contains a bitstream made up of variable sized fragments.
+ * Each bit refers to a set of bytes in the filesystem, defined by
+ * log2bpmb. This may be larger or smaller than the sector size, but
+ * the overall size it describes will always be a round number of
+ * sectors. A fragment id is always idlen bits long.
+ *
+ * < idlen > < n > <1>
+ * +---------+-------//---------+---+
+ * | frag id | 0000....000000 | 1 |
+ * +---------+-------//---------+---+
+ *
+ * The physical disk space used by a fragment is taken from the start of
+ * the fragment id up to and including the '1' bit - ie, idlen + n + 1
+ * bits.
+ *
+ * A fragment id can be repeated multiple times in the whole map for
+ * large or fragmented files. The first map zone a fragment starts in
+ * is given by fragment id / ids_per_zone - this allows objects to start
+ * from any zone on the disk.
+ *
+ * Free space is described by a linked list of fragments. Each free
+ * fragment describes free space in the same way as the other fragments,
+ * however, the frag id specifies an offset (in map bits) from the end
+ * of this fragment to the start of the next free fragment.
+ *
+ * Objects stored on the disk are allocated object ids (we use these as
+ * our inode numbers.) Object ids contain a fragment id and an optional
+ * offset. This allows a directory fragment to contain small files
+ * associated with that directory.
+ */
+
+/*
* For the future...
*/
-static rwlock_t adfs_map_lock;
+static rwlock_t adfs_map_lock = RW_LOCK_UNLOCKED;
+/*
+ * This is fun. We need to load up to 19 bits from the map at an
+ * arbitary bit alignment. (We're limited to 19 bits by F+ version 2).
+ */
#define GET_FRAG_ID(_map,_start,_idmask) \
({ \
- unsigned long _v2, _frag; \
- unsigned int _tmp; \
- _tmp = _start >> 5; \
- _frag = le32_to_cpu(_map[_tmp]); \
- _v2 = le32_to_cpu(_map[_tmp + 1]); \
- _tmp = start & 31; \
- _frag = (_frag >> _tmp) | (_v2 << (32 - _tmp)); \
+ unsigned char *_m = _map + (_start >> 3); \
+ u32 _frag = get_unaligned((u32 *)_m); \
+ _frag >>= (_start & 7); \
_frag & _idmask; \
})
/*
- * return the map bit offset of the fragment frag_id in
- * the zone dm.
- * Note that the loop is optimised for best asm code -
- * look at the output of:
+ * return the map bit offset of the fragment frag_id in the zone dm.
+ * Note that the loop is optimised for best asm code - look at the
+ * output of:
* gcc -D__KERNEL__ -O2 -I../../include -o - -S map.c
*/
static int
@@ -44,14 +78,13 @@ lookup_zone(const struct adfs_discmap *dm, const unsigned int idlen,
const unsigned int frag_id, unsigned int *offset)
{
const unsigned int mapsize = dm->dm_endbit;
- const unsigned int idmask = (1 << idlen) - 1;
- unsigned long *map = ((unsigned long *)dm->dm_bh->b_data) + 1;
+ const u32 idmask = (1 << idlen) - 1;
+ unsigned char *map = dm->dm_bh->b_data + 4;
unsigned int start = dm->dm_startbit;
unsigned int mapptr;
+ u32 frag;
do {
- unsigned long frag;
-
frag = GET_FRAG_ID(map, start, idmask);
mapptr = start + idlen;
@@ -59,15 +92,17 @@ lookup_zone(const struct adfs_discmap *dm, const unsigned int idlen,
* find end of fragment
*/
{
- unsigned long v2;
+ u32 v, *_map = (u32 *)map;
- while ((v2 = map[mapptr >> 5] >> (mapptr & 31)) == 0) {
+ v = le32_to_cpu(_map[mapptr >> 5]) >> (mapptr & 31);
+ while (v == 0) {
mapptr = (mapptr & ~31) + 32;
if (mapptr >= mapsize)
goto error;
+ v = le32_to_cpu(_map[mapptr >> 5]);
}
- mapptr += 1 + ffz(~v2);
+ mapptr += 1 + ffz(~v);
}
if (frag == frag_id)
@@ -75,8 +110,11 @@ lookup_zone(const struct adfs_discmap *dm, const unsigned int idlen,
again:
start = mapptr;
} while (mapptr < mapsize);
+ return -1;
error:
+ printk(KERN_ERR "adfs: oversized fragment 0x%x at 0x%x-0x%x\n",
+ frag, start, mapptr);
return -1;
found:
@@ -102,10 +140,10 @@ scan_free_map(struct adfs_sb_info *asb, struct adfs_discmap *dm)
const unsigned int mapsize = dm->dm_endbit + 32;
const unsigned int idlen = asb->s_idlen;
const unsigned int frag_idlen = idlen <= 15 ? idlen : 15;
- const unsigned int idmask = (1 << frag_idlen) - 1;
- unsigned long *map = (unsigned long *)dm->dm_bh->b_data;
+ const u32 idmask = (1 << frag_idlen) - 1;
+ unsigned char *map = dm->dm_bh->b_data;
unsigned int start = 8, mapptr;
- unsigned long frag;
+ u32 frag;
unsigned long total = 0;
/*
@@ -133,15 +171,17 @@ scan_free_map(struct adfs_sb_info *asb, struct adfs_discmap *dm)
* find end of fragment
*/
{
- unsigned long v2;
+ u32 v, *_map = (u32 *)map;
- while ((v2 = map[mapptr >> 5] >> (mapptr & 31)) == 0) {
+ v = le32_to_cpu(_map[mapptr >> 5]) >> (mapptr & 31);
+ while (v == 0) {
mapptr = (mapptr & ~31) + 32;
if (mapptr >= mapsize)
goto error;
+ v = le32_to_cpu(_map[mapptr >> 5]);
}
- mapptr += 1 + ffz(~v2);
+ mapptr += 1 + ffz(~v);
}
total += mapptr - start;
@@ -212,7 +252,9 @@ adfs_map_free(struct super_block *sb)
return signed_asl(total, asb->s_map2blk);
}
-int adfs_map_lookup (struct super_block *sb, int frag_id, int offset)
+int
+adfs_map_lookup(struct super_block *sb, unsigned int frag_id,
+ unsigned int offset)
{
struct adfs_sb_info *asb = &sb->u.adfs_sb;
unsigned int zone, mapoff;
@@ -245,12 +287,12 @@ int adfs_map_lookup (struct super_block *sb, int frag_id, int offset)
return secoff + signed_asl(result, asb->s_map2blk);
}
- adfs_error(sb, "fragment %04X at offset %d not found in map",
+ adfs_error(sb, "fragment 0x%04x at offset %d not found in map",
frag_id, offset);
return 0;
bad_fragment:
- adfs_error(sb, "fragment %X is invalid (zone = %d, max = %d)",
+ adfs_error(sb, "invalid fragment 0x%04x (zone = %d, max = %d)",
frag_id, zone, asb->s_map_size);
return 0;
}
diff --git a/fs/adfs/super.c b/fs/adfs/super.c
index 1164ce6d11d0..55309e14720c 100644
--- a/fs/adfs/super.c
+++ b/fs/adfs/super.c
@@ -64,43 +64,9 @@ static int adfs_checkdiscrecord(struct adfs_discrecord *dr)
if (dr->disc_size_high >> dr->log2secsize)
return 1;
- /*
- * The following checks are not required for F+
- * stage 1.
- */
-#if 0
- /* idlen must be smaller be no greater than 15 */
- if (dr->idlen > 15)
- return 1;
-
- /* nzones must be less than 128 for the root
- * directory to be addressable
- */
- if (dr->nzones >= 128 && dr->nzones_high == 0)
- return 1;
-
- /* root must be of the form 0x2.. */
- if ((le32_to_cpu(dr->root) & 0xffffff00) != 0x00000200)
- return 1;
-#else
- /*
- * Stage 2 F+ does not require the following check
- */
-#if 0
- /* idlen must be no greater than 16 v2 [1.0] */
- if (dr->idlen > 16)
- return 1;
-
- /* we can't handle F+ discs yet */
- if (dr->format_version || dr->root_size)
- return 1;
-
-#else
/* idlen must be no greater than 19 v2 [1.0] */
if (dr->idlen > 19)
return 1;
-#endif
-#endif
/* reserved bytes should be zero */
for (i = 0; i < sizeof(dr->unused52); i++)