summaryrefslogtreecommitdiffstats
path: root/fs/adfs/map.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1998-03-17 22:05:47 +0000
committerRalf Baechle <ralf@linux-mips.org>1998-03-17 22:05:47 +0000
commit27cfca1ec98e91261b1a5355d10a8996464b63af (patch)
tree8e895a53e372fa682b4c0a585b9377d67ed70d0e /fs/adfs/map.c
parent6a76fb7214c477ccf6582bd79c5b4ccc4f9c41b1 (diff)
Look Ma' what I found on my harddisk ...
o New faster syscalls for 2.1.x, too o Upgrade to 2.1.89. Don't try to run this. It's flaky as hell. But feel free to debug ...
Diffstat (limited to 'fs/adfs/map.c')
-rw-r--r--fs/adfs/map.c108
1 files changed, 108 insertions, 0 deletions
diff --git a/fs/adfs/map.c b/fs/adfs/map.c
new file mode 100644
index 000000000..31d560143
--- /dev/null
+++ b/fs/adfs/map.c
@@ -0,0 +1,108 @@
+/*
+ * linux/fs/adfs/map.c
+ *
+ * Copyright (C) 1997 Russell King
+ */
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/adfs_fs.h>
+
+static inline unsigned int
+adfs_convert_map_to_sector (const struct super_block *sb, unsigned int mapoff)
+{
+ if (sb->u.adfs_sb.s_map2blk >= 0)
+ mapoff <<= sb->u.adfs_sb.s_map2blk;
+ else
+ mapoff >>= -sb->u.adfs_sb.s_map2blk;
+ return mapoff;
+}
+
+static inline unsigned int
+adfs_convert_sector_to_map (const struct super_block *sb, unsigned int secoff)
+{
+ if (sb->u.adfs_sb.s_map2blk >= 0)
+ secoff >>= sb->u.adfs_sb.s_map2blk;
+ else
+ secoff <<= -sb->u.adfs_sb.s_map2blk;
+ return secoff;
+}
+
+static int lookup_zone (struct super_block *sb, int zone, int frag_id, int *offset)
+{
+ unsigned int mapptr, idlen, mapsize;
+ unsigned long *map;
+
+ map = ((unsigned long *)sb->u.adfs_sb.s_map[zone]->b_data) + 1;
+ zone =
+ mapptr = zone == 0 ? (ADFS_DR_SIZE << 3) : 0;
+ idlen = sb->u.adfs_sb.s_idlen;
+ mapsize = sb->u.adfs_sb.s_zonesize;
+
+ do {
+ unsigned long v1, v2;
+ unsigned int start;
+
+ v1 = map[mapptr>>5];
+ v2 = map[(mapptr>>5)+1];
+
+ v1 = (v1 >> (mapptr & 31)) | (v2 << (32 - (mapptr & 31)));
+ start = mapptr;
+ mapptr += idlen;
+
+ v2 = map[mapptr >> 5] >> (mapptr & 31);
+ if (!v2) {
+ mapptr = (mapptr + 32) & ~31;
+ for (; (v2 = map[mapptr >> 5]) == 0 && mapptr < mapsize; mapptr += 32);
+ }
+ for (; (v2 & 255) == 0; v2 >>= 8, mapptr += 8);
+ for (; (v2 & 1) == 0; v2 >>= 1, mapptr += 1);
+ mapptr += 1;
+
+ if ((v1 & ((1 << idlen) - 1)) == frag_id) {
+ int length = mapptr - start;
+ if (*offset >= length)
+ *offset -= length;
+ else
+ return start + *offset - zone;
+ }
+ } while (mapptr < mapsize);
+ return -1;
+}
+
+int adfs_map_lookup (struct super_block *sb, int frag_id, int offset)
+{
+ unsigned int start_zone, zone, max_zone, mapoff, secoff;
+
+ zone = start_zone = frag_id / sb->u.adfs_sb.s_ids_per_zone;
+ max_zone = sb->u.adfs_sb.s_map_size;
+
+ if (start_zone >= max_zone) {
+ adfs_error (sb, "adfs_map_lookup", "fragment %X is invalid (zone = %d, max = %d)",
+ frag_id, start_zone, max_zone);
+ return 0;
+ }
+
+ /* Convert sector offset to map offset */
+ mapoff = adfs_convert_sector_to_map (sb, offset);
+ /* Calculate sector offset into map block */
+ secoff = offset - adfs_convert_map_to_sector (sb, mapoff);
+
+ do {
+ int result = lookup_zone (sb, zone, frag_id, &mapoff);
+
+ if (result != -1) {
+ result += zone ? (zone * sb->u.adfs_sb.s_zonesize) - (ADFS_DR_SIZE << 3): 0;
+ return adfs_convert_map_to_sector (sb, result) + secoff;
+ }
+
+ zone ++;
+ if (zone >= max_zone)
+ zone = 0;
+
+ } while (zone != start_zone);
+
+ adfs_error (sb, "adfs_map_lookup", "fragment %X at offset %d not found in map (start zone %d)",
+ frag_id, offset, start_zone);
+ return 0;
+}