xref: /openbmc/linux/fs/adfs/map.c (revision f93793fd)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  *  linux/fs/adfs/map.c
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  *  Copyright (C) 1997-2002 Russell King
61da177e4SLinus Torvalds  */
7f75d398dSRussell King #include <linux/slab.h>
8e6160e46SRussell King #include <linux/statfs.h>
91da177e4SLinus Torvalds #include <asm/unaligned.h>
101da177e4SLinus Torvalds #include "adfs.h"
111da177e4SLinus Torvalds 
121da177e4SLinus Torvalds /*
131da177e4SLinus Torvalds  * The ADFS map is basically a set of sectors.  Each sector is called a
141da177e4SLinus Torvalds  * zone which contains a bitstream made up of variable sized fragments.
151da177e4SLinus Torvalds  * Each bit refers to a set of bytes in the filesystem, defined by
161da177e4SLinus Torvalds  * log2bpmb.  This may be larger or smaller than the sector size, but
171da177e4SLinus Torvalds  * the overall size it describes will always be a round number of
181da177e4SLinus Torvalds  * sectors.  A fragment id is always idlen bits long.
191da177e4SLinus Torvalds  *
201da177e4SLinus Torvalds  *  < idlen > <       n        > <1>
211da177e4SLinus Torvalds  * +---------+-------//---------+---+
221da177e4SLinus Torvalds  * | frag id |  0000....000000  | 1 |
231da177e4SLinus Torvalds  * +---------+-------//---------+---+
241da177e4SLinus Torvalds  *
251da177e4SLinus Torvalds  * The physical disk space used by a fragment is taken from the start of
261da177e4SLinus Torvalds  * the fragment id up to and including the '1' bit - ie, idlen + n + 1
271da177e4SLinus Torvalds  * bits.
281da177e4SLinus Torvalds  *
291da177e4SLinus Torvalds  * A fragment id can be repeated multiple times in the whole map for
301da177e4SLinus Torvalds  * large or fragmented files.  The first map zone a fragment starts in
311da177e4SLinus Torvalds  * is given by fragment id / ids_per_zone - this allows objects to start
321da177e4SLinus Torvalds  * from any zone on the disk.
331da177e4SLinus Torvalds  *
341da177e4SLinus Torvalds  * Free space is described by a linked list of fragments.  Each free
351da177e4SLinus Torvalds  * fragment describes free space in the same way as the other fragments,
361da177e4SLinus Torvalds  * however, the frag id specifies an offset (in map bits) from the end
371da177e4SLinus Torvalds  * of this fragment to the start of the next free fragment.
381da177e4SLinus Torvalds  *
391da177e4SLinus Torvalds  * Objects stored on the disk are allocated object ids (we use these as
401da177e4SLinus Torvalds  * our inode numbers.)  Object ids contain a fragment id and an optional
411da177e4SLinus Torvalds  * offset.  This allows a directory fragment to contain small files
421da177e4SLinus Torvalds  * associated with that directory.
431da177e4SLinus Torvalds  */
441da177e4SLinus Torvalds 
451da177e4SLinus Torvalds /*
461da177e4SLinus Torvalds  * For the future...
471da177e4SLinus Torvalds  */
481da177e4SLinus Torvalds static DEFINE_RWLOCK(adfs_map_lock);
491da177e4SLinus Torvalds 
501da177e4SLinus Torvalds /*
511da177e4SLinus Torvalds  * This is fun.  We need to load up to 19 bits from the map at an
5225985edcSLucas De Marchi  * arbitrary bit alignment.  (We're limited to 19 bits by F+ version 2).
531da177e4SLinus Torvalds  */
541da177e4SLinus Torvalds #define GET_FRAG_ID(_map,_start,_idmask)				\
551da177e4SLinus Torvalds 	({								\
561da177e4SLinus Torvalds 		unsigned char *_m = _map + (_start >> 3);		\
57224c8866SAl Viro 		u32 _frag = get_unaligned_le32(_m);			\
581da177e4SLinus Torvalds 		_frag >>= (_start & 7);					\
591da177e4SLinus Torvalds 		_frag & _idmask;					\
601da177e4SLinus Torvalds 	})
611da177e4SLinus Torvalds 
621da177e4SLinus Torvalds /*
631da177e4SLinus Torvalds  * return the map bit offset of the fragment frag_id in the zone dm.
641da177e4SLinus Torvalds  * Note that the loop is optimised for best asm code - look at the
651da177e4SLinus Torvalds  * output of:
661da177e4SLinus Torvalds  *  gcc -D__KERNEL__ -O2 -I../../include -o - -S map.c
671da177e4SLinus Torvalds  */
lookup_zone(const struct adfs_discmap * dm,const unsigned int idlen,const u32 frag_id,unsigned int * offset)685ed70bb4SRussell King static int lookup_zone(const struct adfs_discmap *dm, const unsigned int idlen,
695ed70bb4SRussell King 		       const u32 frag_id, unsigned int *offset)
701da177e4SLinus Torvalds {
71197ba3c5SRussell King 	const unsigned int endbit = dm->dm_endbit;
721da177e4SLinus Torvalds 	const u32 idmask = (1 << idlen) - 1;
73197ba3c5SRussell King 	unsigned char *map = dm->dm_bh->b_data;
741da177e4SLinus Torvalds 	unsigned int start = dm->dm_startbit;
75f93793fdSRussell King 	unsigned int freelink, fragend;
761da177e4SLinus Torvalds 	u32 frag;
771da177e4SLinus Torvalds 
78f93793fdSRussell King 	frag = GET_FRAG_ID(map, 8, idmask & 0x7fff);
79f93793fdSRussell King 	freelink = frag ? 8 + frag : 0;
80f93793fdSRussell King 
811da177e4SLinus Torvalds 	do {
821da177e4SLinus Torvalds 		frag = GET_FRAG_ID(map, start, idmask);
831da177e4SLinus Torvalds 
84792314f8SRussell King 		fragend = find_next_bit_le(map, endbit, start + idlen);
85792314f8SRussell King 		if (fragend >= endbit)
861da177e4SLinus Torvalds 			goto error;
87792314f8SRussell King 
88f93793fdSRussell King 		if (start == freelink) {
89f93793fdSRussell King 			freelink += frag & 0x7fff;
90f93793fdSRussell King 		} else if (frag == frag_id) {
91792314f8SRussell King 			unsigned int length = fragend + 1 - start;
92792314f8SRussell King 
93792314f8SRussell King 			if (*offset < length)
94792314f8SRussell King 				return start + *offset;
95792314f8SRussell King 			*offset -= length;
961da177e4SLinus Torvalds 		}
971da177e4SLinus Torvalds 
98792314f8SRussell King 		start = fragend + 1;
99792314f8SRussell King 	} while (start < endbit);
1001da177e4SLinus Torvalds 	return -1;
1011da177e4SLinus Torvalds 
1021da177e4SLinus Torvalds error:
1031da177e4SLinus Torvalds 	printk(KERN_ERR "adfs: oversized fragment 0x%x at 0x%x-0x%x\n",
104792314f8SRussell King 		frag, start, fragend);
1051da177e4SLinus Torvalds 	return -1;
1061da177e4SLinus Torvalds }
1071da177e4SLinus Torvalds 
1081da177e4SLinus Torvalds /*
1091da177e4SLinus Torvalds  * Scan the free space map, for this zone, calculating the total
1101da177e4SLinus Torvalds  * number of map bits in each free space fragment.
1111da177e4SLinus Torvalds  *
1121da177e4SLinus Torvalds  * Note: idmask is limited to 15 bits [3.2]
1131da177e4SLinus Torvalds  */
1141da177e4SLinus Torvalds static unsigned int
scan_free_map(struct adfs_sb_info * asb,struct adfs_discmap * dm)1151da177e4SLinus Torvalds scan_free_map(struct adfs_sb_info *asb, struct adfs_discmap *dm)
1161da177e4SLinus Torvalds {
117197ba3c5SRussell King 	const unsigned int endbit = dm->dm_endbit;
1181da177e4SLinus Torvalds 	const unsigned int idlen  = asb->s_idlen;
1191da177e4SLinus Torvalds 	const unsigned int frag_idlen = idlen <= 15 ? idlen : 15;
1201da177e4SLinus Torvalds 	const u32 idmask = (1 << frag_idlen) - 1;
1211da177e4SLinus Torvalds 	unsigned char *map = dm->dm_bh->b_data;
122792314f8SRussell King 	unsigned int start = 8, fragend;
1231da177e4SLinus Torvalds 	u32 frag;
1241da177e4SLinus Torvalds 	unsigned long total = 0;
1251da177e4SLinus Torvalds 
1261da177e4SLinus Torvalds 	/*
1271da177e4SLinus Torvalds 	 * get fragment id
1281da177e4SLinus Torvalds 	 */
1291da177e4SLinus Torvalds 	frag = GET_FRAG_ID(map, start, idmask);
1301da177e4SLinus Torvalds 
1311da177e4SLinus Torvalds 	/*
1321da177e4SLinus Torvalds 	 * If the freelink is null, then no free fragments
1331da177e4SLinus Torvalds 	 * exist in this zone.
1341da177e4SLinus Torvalds 	 */
1351da177e4SLinus Torvalds 	if (frag == 0)
1361da177e4SLinus Torvalds 		return 0;
1371da177e4SLinus Torvalds 
1381da177e4SLinus Torvalds 	do {
1391da177e4SLinus Torvalds 		start += frag;
1401da177e4SLinus Torvalds 
1411da177e4SLinus Torvalds 		frag = GET_FRAG_ID(map, start, idmask);
1421da177e4SLinus Torvalds 
143792314f8SRussell King 		fragend = find_next_bit_le(map, endbit, start + idlen);
144792314f8SRussell King 		if (fragend >= endbit)
1451da177e4SLinus Torvalds 			goto error;
1461da177e4SLinus Torvalds 
147792314f8SRussell King 		total += fragend + 1 - start;
1481da177e4SLinus Torvalds 	} while (frag >= idlen + 1);
1491da177e4SLinus Torvalds 
1501da177e4SLinus Torvalds 	if (frag != 0)
1511da177e4SLinus Torvalds 		printk(KERN_ERR "adfs: undersized free fragment\n");
1521da177e4SLinus Torvalds 
1531da177e4SLinus Torvalds 	return total;
1541da177e4SLinus Torvalds error:
1551da177e4SLinus Torvalds 	printk(KERN_ERR "adfs: oversized free fragment\n");
1561da177e4SLinus Torvalds 	return 0;
1571da177e4SLinus Torvalds }
1581da177e4SLinus Torvalds 
scan_map(struct adfs_sb_info * asb,unsigned int zone,const u32 frag_id,unsigned int mapoff)1595ed70bb4SRussell King static int scan_map(struct adfs_sb_info *asb, unsigned int zone,
1605ed70bb4SRussell King 		    const u32 frag_id, unsigned int mapoff)
1611da177e4SLinus Torvalds {
1621da177e4SLinus Torvalds 	const unsigned int idlen = asb->s_idlen;
1631da177e4SLinus Torvalds 	struct adfs_discmap *dm, *dm_end;
1641da177e4SLinus Torvalds 	int result;
1651da177e4SLinus Torvalds 
1661da177e4SLinus Torvalds 	dm	= asb->s_map + zone;
1671da177e4SLinus Torvalds 	zone	= asb->s_map_size;
1681da177e4SLinus Torvalds 	dm_end	= asb->s_map + zone;
1691da177e4SLinus Torvalds 
1701da177e4SLinus Torvalds 	do {
1711da177e4SLinus Torvalds 		result = lookup_zone(dm, idlen, frag_id, &mapoff);
1721da177e4SLinus Torvalds 
1731da177e4SLinus Torvalds 		if (result != -1)
1741da177e4SLinus Torvalds 			goto found;
1751da177e4SLinus Torvalds 
1761da177e4SLinus Torvalds 		dm ++;
1771da177e4SLinus Torvalds 		if (dm == dm_end)
1781da177e4SLinus Torvalds 			dm = asb->s_map;
1791da177e4SLinus Torvalds 	} while (--zone > 0);
1801da177e4SLinus Torvalds 
1811da177e4SLinus Torvalds 	return -1;
1821da177e4SLinus Torvalds found:
1831da177e4SLinus Torvalds 	result -= dm->dm_startbit;
1841da177e4SLinus Torvalds 	result += dm->dm_startblk;
1851da177e4SLinus Torvalds 
1861da177e4SLinus Torvalds 	return result;
1871da177e4SLinus Torvalds }
1881da177e4SLinus Torvalds 
1891da177e4SLinus Torvalds /*
1901da177e4SLinus Torvalds  * calculate the amount of free blocks in the map.
1911da177e4SLinus Torvalds  *
1921da177e4SLinus Torvalds  *              n=1
1931da177e4SLinus Torvalds  *  total_free = E(free_in_zone_n)
1941da177e4SLinus Torvalds  *              nzones
1951da177e4SLinus Torvalds  */
adfs_map_statfs(struct super_block * sb,struct kstatfs * buf)196e6160e46SRussell King void adfs_map_statfs(struct super_block *sb, struct kstatfs *buf)
1971da177e4SLinus Torvalds {
1981da177e4SLinus Torvalds 	struct adfs_sb_info *asb = ADFS_SB(sb);
199e6160e46SRussell King 	struct adfs_discrecord *dr = adfs_map_discrecord(asb->s_map);
2001da177e4SLinus Torvalds 	struct adfs_discmap *dm;
2011da177e4SLinus Torvalds 	unsigned int total = 0;
2021da177e4SLinus Torvalds 	unsigned int zone;
2031da177e4SLinus Torvalds 
2041da177e4SLinus Torvalds 	dm   = asb->s_map;
2051da177e4SLinus Torvalds 	zone = asb->s_map_size;
2061da177e4SLinus Torvalds 
2071da177e4SLinus Torvalds 	do {
2081da177e4SLinus Torvalds 		total += scan_free_map(asb, dm++);
2091da177e4SLinus Torvalds 	} while (--zone > 0);
2101da177e4SLinus Torvalds 
211e6160e46SRussell King 	buf->f_blocks  = adfs_disc_size(dr) >> sb->s_blocksize_bits;
212e6160e46SRussell King 	buf->f_files   = asb->s_ids_per_zone * asb->s_map_size;
213e6160e46SRussell King 	buf->f_bavail  =
214e6160e46SRussell King 	buf->f_bfree   = signed_asl(total, asb->s_map2blk);
2151da177e4SLinus Torvalds }
2161da177e4SLinus Torvalds 
adfs_map_lookup(struct super_block * sb,u32 frag_id,unsigned int offset)2175ed70bb4SRussell King int adfs_map_lookup(struct super_block *sb, u32 frag_id, unsigned int offset)
2181da177e4SLinus Torvalds {
2191da177e4SLinus Torvalds 	struct adfs_sb_info *asb = ADFS_SB(sb);
2201da177e4SLinus Torvalds 	unsigned int zone, mapoff;
2211da177e4SLinus Torvalds 	int result;
2221da177e4SLinus Torvalds 
2231da177e4SLinus Torvalds 	/*
2241da177e4SLinus Torvalds 	 * map & root fragment is special - it starts in the center of the
2251da177e4SLinus Torvalds 	 * disk.  The other fragments start at zone (frag / ids_per_zone)
2261da177e4SLinus Torvalds 	 */
2271da177e4SLinus Torvalds 	if (frag_id == ADFS_ROOT_FRAG)
2281da177e4SLinus Torvalds 		zone = asb->s_map_size >> 1;
2291da177e4SLinus Torvalds 	else
2301da177e4SLinus Torvalds 		zone = frag_id / asb->s_ids_per_zone;
2311da177e4SLinus Torvalds 
2321da177e4SLinus Torvalds 	if (zone >= asb->s_map_size)
2331da177e4SLinus Torvalds 		goto bad_fragment;
2341da177e4SLinus Torvalds 
2351da177e4SLinus Torvalds 	/* Convert sector offset to map offset */
2361da177e4SLinus Torvalds 	mapoff = signed_asl(offset, -asb->s_map2blk);
2371da177e4SLinus Torvalds 
2381da177e4SLinus Torvalds 	read_lock(&adfs_map_lock);
2391da177e4SLinus Torvalds 	result = scan_map(asb, zone, frag_id, mapoff);
2401da177e4SLinus Torvalds 	read_unlock(&adfs_map_lock);
2411da177e4SLinus Torvalds 
2421da177e4SLinus Torvalds 	if (result > 0) {
2431da177e4SLinus Torvalds 		unsigned int secoff;
2441da177e4SLinus Torvalds 
2451da177e4SLinus Torvalds 		/* Calculate sector offset into map block */
2461da177e4SLinus Torvalds 		secoff = offset - signed_asl(mapoff, asb->s_map2blk);
2471da177e4SLinus Torvalds 		return secoff + signed_asl(result, asb->s_map2blk);
2481da177e4SLinus Torvalds 	}
2491da177e4SLinus Torvalds 
2501da177e4SLinus Torvalds 	adfs_error(sb, "fragment 0x%04x at offset %d not found in map",
2511da177e4SLinus Torvalds 		   frag_id, offset);
2521da177e4SLinus Torvalds 	return 0;
2531da177e4SLinus Torvalds 
2541da177e4SLinus Torvalds bad_fragment:
2551da177e4SLinus Torvalds 	adfs_error(sb, "invalid fragment 0x%04x (zone = %d, max = %d)",
2561da177e4SLinus Torvalds 		   frag_id, zone, asb->s_map_size);
2571da177e4SLinus Torvalds 	return 0;
2581da177e4SLinus Torvalds }
259f75d398dSRussell King 
adfs_calczonecheck(struct super_block * sb,unsigned char * map)260f75d398dSRussell King static unsigned char adfs_calczonecheck(struct super_block *sb, unsigned char *map)
261f75d398dSRussell King {
262f75d398dSRussell King 	unsigned int v0, v1, v2, v3;
263f75d398dSRussell King 	int i;
264f75d398dSRussell King 
265f75d398dSRussell King 	v0 = v1 = v2 = v3 = 0;
266f75d398dSRussell King 	for (i = sb->s_blocksize - 4; i; i -= 4) {
267f75d398dSRussell King 		v0 += map[i]     + (v3 >> 8);
268f75d398dSRussell King 		v3 &= 0xff;
269f75d398dSRussell King 		v1 += map[i + 1] + (v0 >> 8);
270f75d398dSRussell King 		v0 &= 0xff;
271f75d398dSRussell King 		v2 += map[i + 2] + (v1 >> 8);
272f75d398dSRussell King 		v1 &= 0xff;
273f75d398dSRussell King 		v3 += map[i + 3] + (v2 >> 8);
274f75d398dSRussell King 		v2 &= 0xff;
275f75d398dSRussell King 	}
276f75d398dSRussell King 	v0 +=           v3 >> 8;
277f75d398dSRussell King 	v1 += map[1] + (v0 >> 8);
278f75d398dSRussell King 	v2 += map[2] + (v1 >> 8);
279f75d398dSRussell King 	v3 += map[3] + (v2 >> 8);
280f75d398dSRussell King 
281f75d398dSRussell King 	return v0 ^ v1 ^ v2 ^ v3;
282f75d398dSRussell King }
283f75d398dSRussell King 
adfs_checkmap(struct super_block * sb,struct adfs_discmap * dm)284f75d398dSRussell King static int adfs_checkmap(struct super_block *sb, struct adfs_discmap *dm)
285f75d398dSRussell King {
286f75d398dSRussell King 	unsigned char crosscheck = 0, zonecheck = 1;
287f75d398dSRussell King 	int i;
288f75d398dSRussell King 
289f75d398dSRussell King 	for (i = 0; i < ADFS_SB(sb)->s_map_size; i++) {
290f75d398dSRussell King 		unsigned char *map;
291f75d398dSRussell King 
292f75d398dSRussell King 		map = dm[i].dm_bh->b_data;
293f75d398dSRussell King 
294f75d398dSRussell King 		if (adfs_calczonecheck(sb, map) != map[0]) {
295f75d398dSRussell King 			adfs_error(sb, "zone %d fails zonecheck", i);
296f75d398dSRussell King 			zonecheck = 0;
297f75d398dSRussell King 		}
298f75d398dSRussell King 		crosscheck ^= map[3];
299f75d398dSRussell King 	}
300f75d398dSRussell King 	if (crosscheck != 0xff)
301f75d398dSRussell King 		adfs_error(sb, "crosscheck != 0xff");
302f75d398dSRussell King 	return crosscheck == 0xff && zonecheck;
303f75d398dSRussell King }
304f75d398dSRussell King 
3056092b6beSRussell King /*
3066092b6beSRussell King  * Layout the map - the first zone contains a copy of the disc record,
3076092b6beSRussell King  * and the last zone must be limited to the size of the filesystem.
3086092b6beSRussell King  */
adfs_map_layout(struct adfs_discmap * dm,unsigned int nzones,struct adfs_discrecord * dr)3096092b6beSRussell King static void adfs_map_layout(struct adfs_discmap *dm, unsigned int nzones,
3106092b6beSRussell King 			    struct adfs_discrecord *dr)
3116092b6beSRussell King {
3126092b6beSRussell King 	unsigned int zone, zone_size;
3136092b6beSRussell King 	u64 size;
3146092b6beSRussell King 
3156092b6beSRussell King 	zone_size = (8 << dr->log2secsize) - le16_to_cpu(dr->zone_spare);
3166092b6beSRussell King 
3176092b6beSRussell King 	dm[0].dm_bh       = NULL;
3186092b6beSRussell King 	dm[0].dm_startblk = 0;
319197ba3c5SRussell King 	dm[0].dm_startbit = 32 + ADFS_DR_SIZE_BITS;
320197ba3c5SRussell King 	dm[0].dm_endbit   = 32 + zone_size;
3216092b6beSRussell King 
3226092b6beSRussell King 	for (zone = 1; zone < nzones; zone++) {
3236092b6beSRussell King 		dm[zone].dm_bh       = NULL;
3246092b6beSRussell King 		dm[zone].dm_startblk = zone * zone_size - ADFS_DR_SIZE_BITS;
325197ba3c5SRussell King 		dm[zone].dm_startbit = 32;
326197ba3c5SRussell King 		dm[zone].dm_endbit   = 32 + zone_size;
3276092b6beSRussell King 	}
3286092b6beSRussell King 
3296092b6beSRussell King 	size = adfs_disc_size(dr) >> dr->log2bpmb;
3306092b6beSRussell King 	size -= (nzones - 1) * zone_size - ADFS_DR_SIZE_BITS;
331197ba3c5SRussell King 	dm[nzones - 1].dm_endbit = 32 + size;
3326092b6beSRussell King }
3336092b6beSRussell King 
adfs_map_read(struct adfs_discmap * dm,struct super_block * sb,unsigned int map_addr,unsigned int nzones)3346092b6beSRussell King static int adfs_map_read(struct adfs_discmap *dm, struct super_block *sb,
3356092b6beSRussell King 			 unsigned int map_addr, unsigned int nzones)
3366092b6beSRussell King {
3376092b6beSRussell King 	unsigned int zone;
3386092b6beSRussell King 
3396092b6beSRussell King 	for (zone = 0; zone < nzones; zone++) {
3406092b6beSRussell King 		dm[zone].dm_bh = sb_bread(sb, map_addr + zone);
3416092b6beSRussell King 		if (!dm[zone].dm_bh)
3426092b6beSRussell King 			return -EIO;
3436092b6beSRussell King 	}
3446092b6beSRussell King 
3456092b6beSRussell King 	return 0;
3466092b6beSRussell King }
3476092b6beSRussell King 
adfs_map_relse(struct adfs_discmap * dm,unsigned int nzones)3486092b6beSRussell King static void adfs_map_relse(struct adfs_discmap *dm, unsigned int nzones)
3496092b6beSRussell King {
3506092b6beSRussell King 	unsigned int zone;
3516092b6beSRussell King 
3526092b6beSRussell King 	for (zone = 0; zone < nzones; zone++)
3536092b6beSRussell King 		brelse(dm[zone].dm_bh);
3546092b6beSRussell King }
3556092b6beSRussell King 
adfs_read_map(struct super_block * sb,struct adfs_discrecord * dr)356f75d398dSRussell King struct adfs_discmap *adfs_read_map(struct super_block *sb, struct adfs_discrecord *dr)
357f75d398dSRussell King {
3586092b6beSRussell King 	struct adfs_sb_info *asb = ADFS_SB(sb);
359f75d398dSRussell King 	struct adfs_discmap *dm;
360f75d398dSRussell King 	unsigned int map_addr, zone_size, nzones;
3616092b6beSRussell King 	int ret;
362f75d398dSRussell King 
363f6f14a0dSRussell King 	nzones    = dr->nzones | dr->nzones_high << 8;
364f75d398dSRussell King 	zone_size = (8 << dr->log2secsize) - le16_to_cpu(dr->zone_spare);
365f6f14a0dSRussell King 
366f6f14a0dSRussell King 	asb->s_idlen = dr->idlen;
367f6f14a0dSRussell King 	asb->s_map_size = nzones;
368f6f14a0dSRussell King 	asb->s_map2blk = dr->log2bpmb - dr->log2secsize;
369f6f14a0dSRussell King 	asb->s_log2sharesize = dr->log2sharesize;
370f6f14a0dSRussell King 	asb->s_ids_per_zone = zone_size / (asb->s_idlen + 1);
371f6f14a0dSRussell King 
372f75d398dSRussell King 	map_addr = (nzones >> 1) * zone_size -
373f75d398dSRussell King 		     ((nzones > 1) ? ADFS_DR_SIZE_BITS : 0);
374f75d398dSRussell King 	map_addr = signed_asl(map_addr, asb->s_map2blk);
375f75d398dSRussell King 
376f75d398dSRussell King 	dm = kmalloc_array(nzones, sizeof(*dm), GFP_KERNEL);
377f75d398dSRussell King 	if (dm == NULL) {
378f75d398dSRussell King 		adfs_error(sb, "not enough memory");
379f75d398dSRussell King 		return ERR_PTR(-ENOMEM);
380f75d398dSRussell King 	}
381f75d398dSRussell King 
3826092b6beSRussell King 	adfs_map_layout(dm, nzones, dr);
383f75d398dSRussell King 
3846092b6beSRussell King 	ret = adfs_map_read(dm, sb, map_addr, nzones);
3856092b6beSRussell King 	if (ret) {
386f75d398dSRussell King 		adfs_error(sb, "unable to read map");
387f75d398dSRussell King 		goto error_free;
388f75d398dSRussell King 	}
389f75d398dSRussell King 
390f75d398dSRussell King 	if (adfs_checkmap(sb, dm))
391f75d398dSRussell King 		return dm;
392f75d398dSRussell King 
393f75d398dSRussell King 	adfs_error(sb, "map corrupted");
394f75d398dSRussell King 
395f75d398dSRussell King error_free:
3966092b6beSRussell King 	adfs_map_relse(dm, nzones);
397f75d398dSRussell King 	kfree(dm);
398f75d398dSRussell King 	return ERR_PTR(-EIO);
399f75d398dSRussell King }
4007b195267SRussell King 
adfs_free_map(struct super_block * sb)4017b195267SRussell King void adfs_free_map(struct super_block *sb)
4027b195267SRussell King {
4037b195267SRussell King 	struct adfs_sb_info *asb = ADFS_SB(sb);
4047b195267SRussell King 
4057b195267SRussell King 	adfs_map_relse(asb->s_map, asb->s_map_size);
4067b195267SRussell King 	kfree(asb->s_map);
4077b195267SRussell King }
408