162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  linux/fs/adfs/map.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Copyright (C) 1997-2002 Russell King
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci#include <linux/slab.h>
862306a36Sopenharmony_ci#include <linux/statfs.h>
962306a36Sopenharmony_ci#include <asm/unaligned.h>
1062306a36Sopenharmony_ci#include "adfs.h"
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci/*
1362306a36Sopenharmony_ci * The ADFS map is basically a set of sectors.  Each sector is called a
1462306a36Sopenharmony_ci * zone which contains a bitstream made up of variable sized fragments.
1562306a36Sopenharmony_ci * Each bit refers to a set of bytes in the filesystem, defined by
1662306a36Sopenharmony_ci * log2bpmb.  This may be larger or smaller than the sector size, but
1762306a36Sopenharmony_ci * the overall size it describes will always be a round number of
1862306a36Sopenharmony_ci * sectors.  A fragment id is always idlen bits long.
1962306a36Sopenharmony_ci *
2062306a36Sopenharmony_ci *  < idlen > <       n        > <1>
2162306a36Sopenharmony_ci * +---------+-------//---------+---+
2262306a36Sopenharmony_ci * | frag id |  0000....000000  | 1 |
2362306a36Sopenharmony_ci * +---------+-------//---------+---+
2462306a36Sopenharmony_ci *
2562306a36Sopenharmony_ci * The physical disk space used by a fragment is taken from the start of
2662306a36Sopenharmony_ci * the fragment id up to and including the '1' bit - ie, idlen + n + 1
2762306a36Sopenharmony_ci * bits.
2862306a36Sopenharmony_ci *
2962306a36Sopenharmony_ci * A fragment id can be repeated multiple times in the whole map for
3062306a36Sopenharmony_ci * large or fragmented files.  The first map zone a fragment starts in
3162306a36Sopenharmony_ci * is given by fragment id / ids_per_zone - this allows objects to start
3262306a36Sopenharmony_ci * from any zone on the disk.
3362306a36Sopenharmony_ci *
3462306a36Sopenharmony_ci * Free space is described by a linked list of fragments.  Each free
3562306a36Sopenharmony_ci * fragment describes free space in the same way as the other fragments,
3662306a36Sopenharmony_ci * however, the frag id specifies an offset (in map bits) from the end
3762306a36Sopenharmony_ci * of this fragment to the start of the next free fragment.
3862306a36Sopenharmony_ci *
3962306a36Sopenharmony_ci * Objects stored on the disk are allocated object ids (we use these as
4062306a36Sopenharmony_ci * our inode numbers.)  Object ids contain a fragment id and an optional
4162306a36Sopenharmony_ci * offset.  This allows a directory fragment to contain small files
4262306a36Sopenharmony_ci * associated with that directory.
4362306a36Sopenharmony_ci */
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci/*
4662306a36Sopenharmony_ci * For the future...
4762306a36Sopenharmony_ci */
4862306a36Sopenharmony_cistatic DEFINE_RWLOCK(adfs_map_lock);
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci/*
5162306a36Sopenharmony_ci * This is fun.  We need to load up to 19 bits from the map at an
5262306a36Sopenharmony_ci * arbitrary bit alignment.  (We're limited to 19 bits by F+ version 2).
5362306a36Sopenharmony_ci */
5462306a36Sopenharmony_ci#define GET_FRAG_ID(_map,_start,_idmask)				\
5562306a36Sopenharmony_ci	({								\
5662306a36Sopenharmony_ci		unsigned char *_m = _map + (_start >> 3);		\
5762306a36Sopenharmony_ci		u32 _frag = get_unaligned_le32(_m);			\
5862306a36Sopenharmony_ci		_frag >>= (_start & 7);					\
5962306a36Sopenharmony_ci		_frag & _idmask;					\
6062306a36Sopenharmony_ci	})
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci/*
6362306a36Sopenharmony_ci * return the map bit offset of the fragment frag_id in the zone dm.
6462306a36Sopenharmony_ci * Note that the loop is optimised for best asm code - look at the
6562306a36Sopenharmony_ci * output of:
6662306a36Sopenharmony_ci *  gcc -D__KERNEL__ -O2 -I../../include -o - -S map.c
6762306a36Sopenharmony_ci */
6862306a36Sopenharmony_cistatic int lookup_zone(const struct adfs_discmap *dm, const unsigned int idlen,
6962306a36Sopenharmony_ci		       const u32 frag_id, unsigned int *offset)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	const unsigned int endbit = dm->dm_endbit;
7262306a36Sopenharmony_ci	const u32 idmask = (1 << idlen) - 1;
7362306a36Sopenharmony_ci	unsigned char *map = dm->dm_bh->b_data;
7462306a36Sopenharmony_ci	unsigned int start = dm->dm_startbit;
7562306a36Sopenharmony_ci	unsigned int freelink, fragend;
7662306a36Sopenharmony_ci	u32 frag;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	frag = GET_FRAG_ID(map, 8, idmask & 0x7fff);
7962306a36Sopenharmony_ci	freelink = frag ? 8 + frag : 0;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	do {
8262306a36Sopenharmony_ci		frag = GET_FRAG_ID(map, start, idmask);
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci		fragend = find_next_bit_le(map, endbit, start + idlen);
8562306a36Sopenharmony_ci		if (fragend >= endbit)
8662306a36Sopenharmony_ci			goto error;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci		if (start == freelink) {
8962306a36Sopenharmony_ci			freelink += frag & 0x7fff;
9062306a36Sopenharmony_ci		} else if (frag == frag_id) {
9162306a36Sopenharmony_ci			unsigned int length = fragend + 1 - start;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci			if (*offset < length)
9462306a36Sopenharmony_ci				return start + *offset;
9562306a36Sopenharmony_ci			*offset -= length;
9662306a36Sopenharmony_ci		}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci		start = fragend + 1;
9962306a36Sopenharmony_ci	} while (start < endbit);
10062306a36Sopenharmony_ci	return -1;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cierror:
10362306a36Sopenharmony_ci	printk(KERN_ERR "adfs: oversized fragment 0x%x at 0x%x-0x%x\n",
10462306a36Sopenharmony_ci		frag, start, fragend);
10562306a36Sopenharmony_ci	return -1;
10662306a36Sopenharmony_ci}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci/*
10962306a36Sopenharmony_ci * Scan the free space map, for this zone, calculating the total
11062306a36Sopenharmony_ci * number of map bits in each free space fragment.
11162306a36Sopenharmony_ci *
11262306a36Sopenharmony_ci * Note: idmask is limited to 15 bits [3.2]
11362306a36Sopenharmony_ci */
11462306a36Sopenharmony_cistatic unsigned int
11562306a36Sopenharmony_ciscan_free_map(struct adfs_sb_info *asb, struct adfs_discmap *dm)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	const unsigned int endbit = dm->dm_endbit;
11862306a36Sopenharmony_ci	const unsigned int idlen  = asb->s_idlen;
11962306a36Sopenharmony_ci	const unsigned int frag_idlen = idlen <= 15 ? idlen : 15;
12062306a36Sopenharmony_ci	const u32 idmask = (1 << frag_idlen) - 1;
12162306a36Sopenharmony_ci	unsigned char *map = dm->dm_bh->b_data;
12262306a36Sopenharmony_ci	unsigned int start = 8, fragend;
12362306a36Sopenharmony_ci	u32 frag;
12462306a36Sopenharmony_ci	unsigned long total = 0;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	/*
12762306a36Sopenharmony_ci	 * get fragment id
12862306a36Sopenharmony_ci	 */
12962306a36Sopenharmony_ci	frag = GET_FRAG_ID(map, start, idmask);
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	/*
13262306a36Sopenharmony_ci	 * If the freelink is null, then no free fragments
13362306a36Sopenharmony_ci	 * exist in this zone.
13462306a36Sopenharmony_ci	 */
13562306a36Sopenharmony_ci	if (frag == 0)
13662306a36Sopenharmony_ci		return 0;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	do {
13962306a36Sopenharmony_ci		start += frag;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci		frag = GET_FRAG_ID(map, start, idmask);
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci		fragend = find_next_bit_le(map, endbit, start + idlen);
14462306a36Sopenharmony_ci		if (fragend >= endbit)
14562306a36Sopenharmony_ci			goto error;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci		total += fragend + 1 - start;
14862306a36Sopenharmony_ci	} while (frag >= idlen + 1);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	if (frag != 0)
15162306a36Sopenharmony_ci		printk(KERN_ERR "adfs: undersized free fragment\n");
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	return total;
15462306a36Sopenharmony_cierror:
15562306a36Sopenharmony_ci	printk(KERN_ERR "adfs: oversized free fragment\n");
15662306a36Sopenharmony_ci	return 0;
15762306a36Sopenharmony_ci}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_cistatic int scan_map(struct adfs_sb_info *asb, unsigned int zone,
16062306a36Sopenharmony_ci		    const u32 frag_id, unsigned int mapoff)
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci	const unsigned int idlen = asb->s_idlen;
16362306a36Sopenharmony_ci	struct adfs_discmap *dm, *dm_end;
16462306a36Sopenharmony_ci	int result;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	dm	= asb->s_map + zone;
16762306a36Sopenharmony_ci	zone	= asb->s_map_size;
16862306a36Sopenharmony_ci	dm_end	= asb->s_map + zone;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	do {
17162306a36Sopenharmony_ci		result = lookup_zone(dm, idlen, frag_id, &mapoff);
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci		if (result != -1)
17462306a36Sopenharmony_ci			goto found;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci		dm ++;
17762306a36Sopenharmony_ci		if (dm == dm_end)
17862306a36Sopenharmony_ci			dm = asb->s_map;
17962306a36Sopenharmony_ci	} while (--zone > 0);
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	return -1;
18262306a36Sopenharmony_cifound:
18362306a36Sopenharmony_ci	result -= dm->dm_startbit;
18462306a36Sopenharmony_ci	result += dm->dm_startblk;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	return result;
18762306a36Sopenharmony_ci}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci/*
19062306a36Sopenharmony_ci * calculate the amount of free blocks in the map.
19162306a36Sopenharmony_ci *
19262306a36Sopenharmony_ci *              n=1
19362306a36Sopenharmony_ci *  total_free = E(free_in_zone_n)
19462306a36Sopenharmony_ci *              nzones
19562306a36Sopenharmony_ci */
19662306a36Sopenharmony_civoid adfs_map_statfs(struct super_block *sb, struct kstatfs *buf)
19762306a36Sopenharmony_ci{
19862306a36Sopenharmony_ci	struct adfs_sb_info *asb = ADFS_SB(sb);
19962306a36Sopenharmony_ci	struct adfs_discrecord *dr = adfs_map_discrecord(asb->s_map);
20062306a36Sopenharmony_ci	struct adfs_discmap *dm;
20162306a36Sopenharmony_ci	unsigned int total = 0;
20262306a36Sopenharmony_ci	unsigned int zone;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	dm   = asb->s_map;
20562306a36Sopenharmony_ci	zone = asb->s_map_size;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	do {
20862306a36Sopenharmony_ci		total += scan_free_map(asb, dm++);
20962306a36Sopenharmony_ci	} while (--zone > 0);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	buf->f_blocks  = adfs_disc_size(dr) >> sb->s_blocksize_bits;
21262306a36Sopenharmony_ci	buf->f_files   = asb->s_ids_per_zone * asb->s_map_size;
21362306a36Sopenharmony_ci	buf->f_bavail  =
21462306a36Sopenharmony_ci	buf->f_bfree   = signed_asl(total, asb->s_map2blk);
21562306a36Sopenharmony_ci}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ciint adfs_map_lookup(struct super_block *sb, u32 frag_id, unsigned int offset)
21862306a36Sopenharmony_ci{
21962306a36Sopenharmony_ci	struct adfs_sb_info *asb = ADFS_SB(sb);
22062306a36Sopenharmony_ci	unsigned int zone, mapoff;
22162306a36Sopenharmony_ci	int result;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	/*
22462306a36Sopenharmony_ci	 * map & root fragment is special - it starts in the center of the
22562306a36Sopenharmony_ci	 * disk.  The other fragments start at zone (frag / ids_per_zone)
22662306a36Sopenharmony_ci	 */
22762306a36Sopenharmony_ci	if (frag_id == ADFS_ROOT_FRAG)
22862306a36Sopenharmony_ci		zone = asb->s_map_size >> 1;
22962306a36Sopenharmony_ci	else
23062306a36Sopenharmony_ci		zone = frag_id / asb->s_ids_per_zone;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	if (zone >= asb->s_map_size)
23362306a36Sopenharmony_ci		goto bad_fragment;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	/* Convert sector offset to map offset */
23662306a36Sopenharmony_ci	mapoff = signed_asl(offset, -asb->s_map2blk);
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	read_lock(&adfs_map_lock);
23962306a36Sopenharmony_ci	result = scan_map(asb, zone, frag_id, mapoff);
24062306a36Sopenharmony_ci	read_unlock(&adfs_map_lock);
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	if (result > 0) {
24362306a36Sopenharmony_ci		unsigned int secoff;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci		/* Calculate sector offset into map block */
24662306a36Sopenharmony_ci		secoff = offset - signed_asl(mapoff, asb->s_map2blk);
24762306a36Sopenharmony_ci		return secoff + signed_asl(result, asb->s_map2blk);
24862306a36Sopenharmony_ci	}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	adfs_error(sb, "fragment 0x%04x at offset %d not found in map",
25162306a36Sopenharmony_ci		   frag_id, offset);
25262306a36Sopenharmony_ci	return 0;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_cibad_fragment:
25562306a36Sopenharmony_ci	adfs_error(sb, "invalid fragment 0x%04x (zone = %d, max = %d)",
25662306a36Sopenharmony_ci		   frag_id, zone, asb->s_map_size);
25762306a36Sopenharmony_ci	return 0;
25862306a36Sopenharmony_ci}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_cistatic unsigned char adfs_calczonecheck(struct super_block *sb, unsigned char *map)
26162306a36Sopenharmony_ci{
26262306a36Sopenharmony_ci	unsigned int v0, v1, v2, v3;
26362306a36Sopenharmony_ci	int i;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	v0 = v1 = v2 = v3 = 0;
26662306a36Sopenharmony_ci	for (i = sb->s_blocksize - 4; i; i -= 4) {
26762306a36Sopenharmony_ci		v0 += map[i]     + (v3 >> 8);
26862306a36Sopenharmony_ci		v3 &= 0xff;
26962306a36Sopenharmony_ci		v1 += map[i + 1] + (v0 >> 8);
27062306a36Sopenharmony_ci		v0 &= 0xff;
27162306a36Sopenharmony_ci		v2 += map[i + 2] + (v1 >> 8);
27262306a36Sopenharmony_ci		v1 &= 0xff;
27362306a36Sopenharmony_ci		v3 += map[i + 3] + (v2 >> 8);
27462306a36Sopenharmony_ci		v2 &= 0xff;
27562306a36Sopenharmony_ci	}
27662306a36Sopenharmony_ci	v0 +=           v3 >> 8;
27762306a36Sopenharmony_ci	v1 += map[1] + (v0 >> 8);
27862306a36Sopenharmony_ci	v2 += map[2] + (v1 >> 8);
27962306a36Sopenharmony_ci	v3 += map[3] + (v2 >> 8);
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	return v0 ^ v1 ^ v2 ^ v3;
28262306a36Sopenharmony_ci}
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_cistatic int adfs_checkmap(struct super_block *sb, struct adfs_discmap *dm)
28562306a36Sopenharmony_ci{
28662306a36Sopenharmony_ci	unsigned char crosscheck = 0, zonecheck = 1;
28762306a36Sopenharmony_ci	int i;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	for (i = 0; i < ADFS_SB(sb)->s_map_size; i++) {
29062306a36Sopenharmony_ci		unsigned char *map;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci		map = dm[i].dm_bh->b_data;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci		if (adfs_calczonecheck(sb, map) != map[0]) {
29562306a36Sopenharmony_ci			adfs_error(sb, "zone %d fails zonecheck", i);
29662306a36Sopenharmony_ci			zonecheck = 0;
29762306a36Sopenharmony_ci		}
29862306a36Sopenharmony_ci		crosscheck ^= map[3];
29962306a36Sopenharmony_ci	}
30062306a36Sopenharmony_ci	if (crosscheck != 0xff)
30162306a36Sopenharmony_ci		adfs_error(sb, "crosscheck != 0xff");
30262306a36Sopenharmony_ci	return crosscheck == 0xff && zonecheck;
30362306a36Sopenharmony_ci}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci/*
30662306a36Sopenharmony_ci * Layout the map - the first zone contains a copy of the disc record,
30762306a36Sopenharmony_ci * and the last zone must be limited to the size of the filesystem.
30862306a36Sopenharmony_ci */
30962306a36Sopenharmony_cistatic void adfs_map_layout(struct adfs_discmap *dm, unsigned int nzones,
31062306a36Sopenharmony_ci			    struct adfs_discrecord *dr)
31162306a36Sopenharmony_ci{
31262306a36Sopenharmony_ci	unsigned int zone, zone_size;
31362306a36Sopenharmony_ci	u64 size;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	zone_size = (8 << dr->log2secsize) - le16_to_cpu(dr->zone_spare);
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	dm[0].dm_bh       = NULL;
31862306a36Sopenharmony_ci	dm[0].dm_startblk = 0;
31962306a36Sopenharmony_ci	dm[0].dm_startbit = 32 + ADFS_DR_SIZE_BITS;
32062306a36Sopenharmony_ci	dm[0].dm_endbit   = 32 + zone_size;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	for (zone = 1; zone < nzones; zone++) {
32362306a36Sopenharmony_ci		dm[zone].dm_bh       = NULL;
32462306a36Sopenharmony_ci		dm[zone].dm_startblk = zone * zone_size - ADFS_DR_SIZE_BITS;
32562306a36Sopenharmony_ci		dm[zone].dm_startbit = 32;
32662306a36Sopenharmony_ci		dm[zone].dm_endbit   = 32 + zone_size;
32762306a36Sopenharmony_ci	}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	size = adfs_disc_size(dr) >> dr->log2bpmb;
33062306a36Sopenharmony_ci	size -= (nzones - 1) * zone_size - ADFS_DR_SIZE_BITS;
33162306a36Sopenharmony_ci	dm[nzones - 1].dm_endbit = 32 + size;
33262306a36Sopenharmony_ci}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_cistatic int adfs_map_read(struct adfs_discmap *dm, struct super_block *sb,
33562306a36Sopenharmony_ci			 unsigned int map_addr, unsigned int nzones)
33662306a36Sopenharmony_ci{
33762306a36Sopenharmony_ci	unsigned int zone;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	for (zone = 0; zone < nzones; zone++) {
34062306a36Sopenharmony_ci		dm[zone].dm_bh = sb_bread(sb, map_addr + zone);
34162306a36Sopenharmony_ci		if (!dm[zone].dm_bh)
34262306a36Sopenharmony_ci			return -EIO;
34362306a36Sopenharmony_ci	}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	return 0;
34662306a36Sopenharmony_ci}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_cistatic void adfs_map_relse(struct adfs_discmap *dm, unsigned int nzones)
34962306a36Sopenharmony_ci{
35062306a36Sopenharmony_ci	unsigned int zone;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	for (zone = 0; zone < nzones; zone++)
35362306a36Sopenharmony_ci		brelse(dm[zone].dm_bh);
35462306a36Sopenharmony_ci}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_cistruct adfs_discmap *adfs_read_map(struct super_block *sb, struct adfs_discrecord *dr)
35762306a36Sopenharmony_ci{
35862306a36Sopenharmony_ci	struct adfs_sb_info *asb = ADFS_SB(sb);
35962306a36Sopenharmony_ci	struct adfs_discmap *dm;
36062306a36Sopenharmony_ci	unsigned int map_addr, zone_size, nzones;
36162306a36Sopenharmony_ci	int ret;
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	nzones    = dr->nzones | dr->nzones_high << 8;
36462306a36Sopenharmony_ci	zone_size = (8 << dr->log2secsize) - le16_to_cpu(dr->zone_spare);
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	asb->s_idlen = dr->idlen;
36762306a36Sopenharmony_ci	asb->s_map_size = nzones;
36862306a36Sopenharmony_ci	asb->s_map2blk = dr->log2bpmb - dr->log2secsize;
36962306a36Sopenharmony_ci	asb->s_log2sharesize = dr->log2sharesize;
37062306a36Sopenharmony_ci	asb->s_ids_per_zone = zone_size / (asb->s_idlen + 1);
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	map_addr = (nzones >> 1) * zone_size -
37362306a36Sopenharmony_ci		     ((nzones > 1) ? ADFS_DR_SIZE_BITS : 0);
37462306a36Sopenharmony_ci	map_addr = signed_asl(map_addr, asb->s_map2blk);
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	dm = kmalloc_array(nzones, sizeof(*dm), GFP_KERNEL);
37762306a36Sopenharmony_ci	if (dm == NULL) {
37862306a36Sopenharmony_ci		adfs_error(sb, "not enough memory");
37962306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
38062306a36Sopenharmony_ci	}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	adfs_map_layout(dm, nzones, dr);
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	ret = adfs_map_read(dm, sb, map_addr, nzones);
38562306a36Sopenharmony_ci	if (ret) {
38662306a36Sopenharmony_ci		adfs_error(sb, "unable to read map");
38762306a36Sopenharmony_ci		goto error_free;
38862306a36Sopenharmony_ci	}
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	if (adfs_checkmap(sb, dm))
39162306a36Sopenharmony_ci		return dm;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	adfs_error(sb, "map corrupted");
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_cierror_free:
39662306a36Sopenharmony_ci	adfs_map_relse(dm, nzones);
39762306a36Sopenharmony_ci	kfree(dm);
39862306a36Sopenharmony_ci	return ERR_PTR(-EIO);
39962306a36Sopenharmony_ci}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_civoid adfs_free_map(struct super_block *sb)
40262306a36Sopenharmony_ci{
40362306a36Sopenharmony_ci	struct adfs_sb_info *asb = ADFS_SB(sb);
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	adfs_map_relse(asb->s_map, asb->s_map_size);
40662306a36Sopenharmony_ci	kfree(asb->s_map);
40762306a36Sopenharmony_ci}
408