0001
0002
0003
0004
0005
0006
0007 #include <linux/slab.h>
0008 #include <linux/statfs.h>
0009 #include <asm/unaligned.h>
0010 #include "adfs.h"
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048 static DEFINE_RWLOCK(adfs_map_lock);
0049
0050
0051
0052
0053
0054 #define GET_FRAG_ID(_map,_start,_idmask) \
0055 ({ \
0056 unsigned char *_m = _map + (_start >> 3); \
0057 u32 _frag = get_unaligned_le32(_m); \
0058 _frag >>= (_start & 7); \
0059 _frag & _idmask; \
0060 })
0061
0062
0063
0064
0065
0066
0067
0068 static int lookup_zone(const struct adfs_discmap *dm, const unsigned int idlen,
0069 const u32 frag_id, unsigned int *offset)
0070 {
0071 const unsigned int endbit = dm->dm_endbit;
0072 const u32 idmask = (1 << idlen) - 1;
0073 unsigned char *map = dm->dm_bh->b_data;
0074 unsigned int start = dm->dm_startbit;
0075 unsigned int freelink, fragend;
0076 u32 frag;
0077
0078 frag = GET_FRAG_ID(map, 8, idmask & 0x7fff);
0079 freelink = frag ? 8 + frag : 0;
0080
0081 do {
0082 frag = GET_FRAG_ID(map, start, idmask);
0083
0084 fragend = find_next_bit_le(map, endbit, start + idlen);
0085 if (fragend >= endbit)
0086 goto error;
0087
0088 if (start == freelink) {
0089 freelink += frag & 0x7fff;
0090 } else if (frag == frag_id) {
0091 unsigned int length = fragend + 1 - start;
0092
0093 if (*offset < length)
0094 return start + *offset;
0095 *offset -= length;
0096 }
0097
0098 start = fragend + 1;
0099 } while (start < endbit);
0100 return -1;
0101
0102 error:
0103 printk(KERN_ERR "adfs: oversized fragment 0x%x at 0x%x-0x%x\n",
0104 frag, start, fragend);
0105 return -1;
0106 }
0107
0108
0109
0110
0111
0112
0113
0114 static unsigned int
0115 scan_free_map(struct adfs_sb_info *asb, struct adfs_discmap *dm)
0116 {
0117 const unsigned int endbit = dm->dm_endbit;
0118 const unsigned int idlen = asb->s_idlen;
0119 const unsigned int frag_idlen = idlen <= 15 ? idlen : 15;
0120 const u32 idmask = (1 << frag_idlen) - 1;
0121 unsigned char *map = dm->dm_bh->b_data;
0122 unsigned int start = 8, fragend;
0123 u32 frag;
0124 unsigned long total = 0;
0125
0126
0127
0128
0129 frag = GET_FRAG_ID(map, start, idmask);
0130
0131
0132
0133
0134
0135 if (frag == 0)
0136 return 0;
0137
0138 do {
0139 start += frag;
0140
0141 frag = GET_FRAG_ID(map, start, idmask);
0142
0143 fragend = find_next_bit_le(map, endbit, start + idlen);
0144 if (fragend >= endbit)
0145 goto error;
0146
0147 total += fragend + 1 - start;
0148 } while (frag >= idlen + 1);
0149
0150 if (frag != 0)
0151 printk(KERN_ERR "adfs: undersized free fragment\n");
0152
0153 return total;
0154 error:
0155 printk(KERN_ERR "adfs: oversized free fragment\n");
0156 return 0;
0157 }
0158
0159 static int scan_map(struct adfs_sb_info *asb, unsigned int zone,
0160 const u32 frag_id, unsigned int mapoff)
0161 {
0162 const unsigned int idlen = asb->s_idlen;
0163 struct adfs_discmap *dm, *dm_end;
0164 int result;
0165
0166 dm = asb->s_map + zone;
0167 zone = asb->s_map_size;
0168 dm_end = asb->s_map + zone;
0169
0170 do {
0171 result = lookup_zone(dm, idlen, frag_id, &mapoff);
0172
0173 if (result != -1)
0174 goto found;
0175
0176 dm ++;
0177 if (dm == dm_end)
0178 dm = asb->s_map;
0179 } while (--zone > 0);
0180
0181 return -1;
0182 found:
0183 result -= dm->dm_startbit;
0184 result += dm->dm_startblk;
0185
0186 return result;
0187 }
0188
0189
0190
0191
0192
0193
0194
0195
0196 void adfs_map_statfs(struct super_block *sb, struct kstatfs *buf)
0197 {
0198 struct adfs_sb_info *asb = ADFS_SB(sb);
0199 struct adfs_discrecord *dr = adfs_map_discrecord(asb->s_map);
0200 struct adfs_discmap *dm;
0201 unsigned int total = 0;
0202 unsigned int zone;
0203
0204 dm = asb->s_map;
0205 zone = asb->s_map_size;
0206
0207 do {
0208 total += scan_free_map(asb, dm++);
0209 } while (--zone > 0);
0210
0211 buf->f_blocks = adfs_disc_size(dr) >> sb->s_blocksize_bits;
0212 buf->f_files = asb->s_ids_per_zone * asb->s_map_size;
0213 buf->f_bavail =
0214 buf->f_bfree = signed_asl(total, asb->s_map2blk);
0215 }
0216
0217 int adfs_map_lookup(struct super_block *sb, u32 frag_id, unsigned int offset)
0218 {
0219 struct adfs_sb_info *asb = ADFS_SB(sb);
0220 unsigned int zone, mapoff;
0221 int result;
0222
0223
0224
0225
0226
0227 if (frag_id == ADFS_ROOT_FRAG)
0228 zone = asb->s_map_size >> 1;
0229 else
0230 zone = frag_id / asb->s_ids_per_zone;
0231
0232 if (zone >= asb->s_map_size)
0233 goto bad_fragment;
0234
0235
0236 mapoff = signed_asl(offset, -asb->s_map2blk);
0237
0238 read_lock(&adfs_map_lock);
0239 result = scan_map(asb, zone, frag_id, mapoff);
0240 read_unlock(&adfs_map_lock);
0241
0242 if (result > 0) {
0243 unsigned int secoff;
0244
0245
0246 secoff = offset - signed_asl(mapoff, asb->s_map2blk);
0247 return secoff + signed_asl(result, asb->s_map2blk);
0248 }
0249
0250 adfs_error(sb, "fragment 0x%04x at offset %d not found in map",
0251 frag_id, offset);
0252 return 0;
0253
0254 bad_fragment:
0255 adfs_error(sb, "invalid fragment 0x%04x (zone = %d, max = %d)",
0256 frag_id, zone, asb->s_map_size);
0257 return 0;
0258 }
0259
0260 static unsigned char adfs_calczonecheck(struct super_block *sb, unsigned char *map)
0261 {
0262 unsigned int v0, v1, v2, v3;
0263 int i;
0264
0265 v0 = v1 = v2 = v3 = 0;
0266 for (i = sb->s_blocksize - 4; i; i -= 4) {
0267 v0 += map[i] + (v3 >> 8);
0268 v3 &= 0xff;
0269 v1 += map[i + 1] + (v0 >> 8);
0270 v0 &= 0xff;
0271 v2 += map[i + 2] + (v1 >> 8);
0272 v1 &= 0xff;
0273 v3 += map[i + 3] + (v2 >> 8);
0274 v2 &= 0xff;
0275 }
0276 v0 += v3 >> 8;
0277 v1 += map[1] + (v0 >> 8);
0278 v2 += map[2] + (v1 >> 8);
0279 v3 += map[3] + (v2 >> 8);
0280
0281 return v0 ^ v1 ^ v2 ^ v3;
0282 }
0283
0284 static int adfs_checkmap(struct super_block *sb, struct adfs_discmap *dm)
0285 {
0286 unsigned char crosscheck = 0, zonecheck = 1;
0287 int i;
0288
0289 for (i = 0; i < ADFS_SB(sb)->s_map_size; i++) {
0290 unsigned char *map;
0291
0292 map = dm[i].dm_bh->b_data;
0293
0294 if (adfs_calczonecheck(sb, map) != map[0]) {
0295 adfs_error(sb, "zone %d fails zonecheck", i);
0296 zonecheck = 0;
0297 }
0298 crosscheck ^= map[3];
0299 }
0300 if (crosscheck != 0xff)
0301 adfs_error(sb, "crosscheck != 0xff");
0302 return crosscheck == 0xff && zonecheck;
0303 }
0304
0305
0306
0307
0308
0309 static void adfs_map_layout(struct adfs_discmap *dm, unsigned int nzones,
0310 struct adfs_discrecord *dr)
0311 {
0312 unsigned int zone, zone_size;
0313 u64 size;
0314
0315 zone_size = (8 << dr->log2secsize) - le16_to_cpu(dr->zone_spare);
0316
0317 dm[0].dm_bh = NULL;
0318 dm[0].dm_startblk = 0;
0319 dm[0].dm_startbit = 32 + ADFS_DR_SIZE_BITS;
0320 dm[0].dm_endbit = 32 + zone_size;
0321
0322 for (zone = 1; zone < nzones; zone++) {
0323 dm[zone].dm_bh = NULL;
0324 dm[zone].dm_startblk = zone * zone_size - ADFS_DR_SIZE_BITS;
0325 dm[zone].dm_startbit = 32;
0326 dm[zone].dm_endbit = 32 + zone_size;
0327 }
0328
0329 size = adfs_disc_size(dr) >> dr->log2bpmb;
0330 size -= (nzones - 1) * zone_size - ADFS_DR_SIZE_BITS;
0331 dm[nzones - 1].dm_endbit = 32 + size;
0332 }
0333
0334 static int adfs_map_read(struct adfs_discmap *dm, struct super_block *sb,
0335 unsigned int map_addr, unsigned int nzones)
0336 {
0337 unsigned int zone;
0338
0339 for (zone = 0; zone < nzones; zone++) {
0340 dm[zone].dm_bh = sb_bread(sb, map_addr + zone);
0341 if (!dm[zone].dm_bh)
0342 return -EIO;
0343 }
0344
0345 return 0;
0346 }
0347
0348 static void adfs_map_relse(struct adfs_discmap *dm, unsigned int nzones)
0349 {
0350 unsigned int zone;
0351
0352 for (zone = 0; zone < nzones; zone++)
0353 brelse(dm[zone].dm_bh);
0354 }
0355
0356 struct adfs_discmap *adfs_read_map(struct super_block *sb, struct adfs_discrecord *dr)
0357 {
0358 struct adfs_sb_info *asb = ADFS_SB(sb);
0359 struct adfs_discmap *dm;
0360 unsigned int map_addr, zone_size, nzones;
0361 int ret;
0362
0363 nzones = dr->nzones | dr->nzones_high << 8;
0364 zone_size = (8 << dr->log2secsize) - le16_to_cpu(dr->zone_spare);
0365
0366 asb->s_idlen = dr->idlen;
0367 asb->s_map_size = nzones;
0368 asb->s_map2blk = dr->log2bpmb - dr->log2secsize;
0369 asb->s_log2sharesize = dr->log2sharesize;
0370 asb->s_ids_per_zone = zone_size / (asb->s_idlen + 1);
0371
0372 map_addr = (nzones >> 1) * zone_size -
0373 ((nzones > 1) ? ADFS_DR_SIZE_BITS : 0);
0374 map_addr = signed_asl(map_addr, asb->s_map2blk);
0375
0376 dm = kmalloc_array(nzones, sizeof(*dm), GFP_KERNEL);
0377 if (dm == NULL) {
0378 adfs_error(sb, "not enough memory");
0379 return ERR_PTR(-ENOMEM);
0380 }
0381
0382 adfs_map_layout(dm, nzones, dr);
0383
0384 ret = adfs_map_read(dm, sb, map_addr, nzones);
0385 if (ret) {
0386 adfs_error(sb, "unable to read map");
0387 goto error_free;
0388 }
0389
0390 if (adfs_checkmap(sb, dm))
0391 return dm;
0392
0393 adfs_error(sb, "map corrupted");
0394
0395 error_free:
0396 adfs_map_relse(dm, nzones);
0397 kfree(dm);
0398 return ERR_PTR(-EIO);
0399 }
0400
0401 void adfs_free_map(struct super_block *sb)
0402 {
0403 struct adfs_sb_info *asb = ADFS_SB(sb);
0404
0405 adfs_map_relse(asb->s_map, asb->s_map_size);
0406 kfree(asb->s_map);
0407 }