blob: 5f2d9d77530559778aa326b90dcc2f3b248328a9 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * linux/fs/adfs/map.c
3 *
4 * Copyright (C) 1997-2002 Russell King
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070010#include <asm/unaligned.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070011#include "adfs.h"
12
13/*
14 * The ADFS map is basically a set of sectors. Each sector is called a
15 * zone which contains a bitstream made up of variable sized fragments.
16 * Each bit refers to a set of bytes in the filesystem, defined by
17 * log2bpmb. This may be larger or smaller than the sector size, but
18 * the overall size it describes will always be a round number of
19 * sectors. A fragment id is always idlen bits long.
20 *
21 * < idlen > < n > <1>
22 * +---------+-------//---------+---+
23 * | frag id | 0000....000000 | 1 |
24 * +---------+-------//---------+---+
25 *
26 * The physical disk space used by a fragment is taken from the start of
27 * the fragment id up to and including the '1' bit - ie, idlen + n + 1
28 * bits.
29 *
30 * A fragment id can be repeated multiple times in the whole map for
31 * large or fragmented files. The first map zone a fragment starts in
32 * is given by fragment id / ids_per_zone - this allows objects to start
33 * from any zone on the disk.
34 *
35 * Free space is described by a linked list of fragments. Each free
36 * fragment describes free space in the same way as the other fragments,
37 * however, the frag id specifies an offset (in map bits) from the end
38 * of this fragment to the start of the next free fragment.
39 *
40 * Objects stored on the disk are allocated object ids (we use these as
41 * our inode numbers.) Object ids contain a fragment id and an optional
42 * offset. This allows a directory fragment to contain small files
43 * associated with that directory.
44 */
45
46/*
47 * For the future...
48 */
49static DEFINE_RWLOCK(adfs_map_lock);
50
51/*
52 * This is fun. We need to load up to 19 bits from the map at an
Lucas De Marchi25985ed2011-03-30 22:57:33 -030053 * arbitrary bit alignment. (We're limited to 19 bits by F+ version 2).
Linus Torvalds1da177e2005-04-16 15:20:36 -070054 */
55#define GET_FRAG_ID(_map,_start,_idmask) \
56 ({ \
57 unsigned char *_m = _map + (_start >> 3); \
Al Viro224c8862009-06-08 00:46:40 -040058 u32 _frag = get_unaligned_le32(_m); \
Linus Torvalds1da177e2005-04-16 15:20:36 -070059 _frag >>= (_start & 7); \
60 _frag & _idmask; \
61 })
62
63/*
64 * return the map bit offset of the fragment frag_id in the zone dm.
65 * Note that the loop is optimised for best asm code - look at the
66 * output of:
67 * gcc -D__KERNEL__ -O2 -I../../include -o - -S map.c
68 */
69static int
70lookup_zone(const struct adfs_discmap *dm, const unsigned int idlen,
71 const unsigned int frag_id, unsigned int *offset)
72{
73 const unsigned int mapsize = dm->dm_endbit;
74 const u32 idmask = (1 << idlen) - 1;
75 unsigned char *map = dm->dm_bh->b_data + 4;
76 unsigned int start = dm->dm_startbit;
77 unsigned int mapptr;
78 u32 frag;
79
80 do {
81 frag = GET_FRAG_ID(map, start, idmask);
82 mapptr = start + idlen;
83
84 /*
85 * find end of fragment
86 */
87 {
88 __le32 *_map = (__le32 *)map;
89 u32 v = le32_to_cpu(_map[mapptr >> 5]) >> (mapptr & 31);
90 while (v == 0) {
91 mapptr = (mapptr & ~31) + 32;
92 if (mapptr >= mapsize)
93 goto error;
94 v = le32_to_cpu(_map[mapptr >> 5]);
95 }
96
97 mapptr += 1 + ffz(~v);
98 }
99
100 if (frag == frag_id)
101 goto found;
102again:
103 start = mapptr;
104 } while (mapptr < mapsize);
105 return -1;
106
107error:
108 printk(KERN_ERR "adfs: oversized fragment 0x%x at 0x%x-0x%x\n",
109 frag, start, mapptr);
110 return -1;
111
112found:
113 {
114 int length = mapptr - start;
115 if (*offset >= length) {
116 *offset -= length;
117 goto again;
118 }
119 }
120 return start + *offset;
121}
122
123/*
124 * Scan the free space map, for this zone, calculating the total
125 * number of map bits in each free space fragment.
126 *
127 * Note: idmask is limited to 15 bits [3.2]
128 */
129static unsigned int
130scan_free_map(struct adfs_sb_info *asb, struct adfs_discmap *dm)
131{
132 const unsigned int mapsize = dm->dm_endbit + 32;
133 const unsigned int idlen = asb->s_idlen;
134 const unsigned int frag_idlen = idlen <= 15 ? idlen : 15;
135 const u32 idmask = (1 << frag_idlen) - 1;
136 unsigned char *map = dm->dm_bh->b_data;
137 unsigned int start = 8, mapptr;
138 u32 frag;
139 unsigned long total = 0;
140
141 /*
142 * get fragment id
143 */
144 frag = GET_FRAG_ID(map, start, idmask);
145
146 /*
147 * If the freelink is null, then no free fragments
148 * exist in this zone.
149 */
150 if (frag == 0)
151 return 0;
152
153 do {
154 start += frag;
155
156 /*
157 * get fragment id
158 */
159 frag = GET_FRAG_ID(map, start, idmask);
160 mapptr = start + idlen;
161
162 /*
163 * find end of fragment
164 */
165 {
166 __le32 *_map = (__le32 *)map;
167 u32 v = le32_to_cpu(_map[mapptr >> 5]) >> (mapptr & 31);
168 while (v == 0) {
169 mapptr = (mapptr & ~31) + 32;
170 if (mapptr >= mapsize)
171 goto error;
172 v = le32_to_cpu(_map[mapptr >> 5]);
173 }
174
175 mapptr += 1 + ffz(~v);
176 }
177
178 total += mapptr - start;
179 } while (frag >= idlen + 1);
180
181 if (frag != 0)
182 printk(KERN_ERR "adfs: undersized free fragment\n");
183
184 return total;
185error:
186 printk(KERN_ERR "adfs: oversized free fragment\n");
187 return 0;
188}
189
190static int
191scan_map(struct adfs_sb_info *asb, unsigned int zone,
192 const unsigned int frag_id, unsigned int mapoff)
193{
194 const unsigned int idlen = asb->s_idlen;
195 struct adfs_discmap *dm, *dm_end;
196 int result;
197
198 dm = asb->s_map + zone;
199 zone = asb->s_map_size;
200 dm_end = asb->s_map + zone;
201
202 do {
203 result = lookup_zone(dm, idlen, frag_id, &mapoff);
204
205 if (result != -1)
206 goto found;
207
208 dm ++;
209 if (dm == dm_end)
210 dm = asb->s_map;
211 } while (--zone > 0);
212
213 return -1;
214found:
215 result -= dm->dm_startbit;
216 result += dm->dm_startblk;
217
218 return result;
219}
220
221/*
222 * calculate the amount of free blocks in the map.
223 *
224 * n=1
225 * total_free = E(free_in_zone_n)
226 * nzones
227 */
228unsigned int
229adfs_map_free(struct super_block *sb)
230{
231 struct adfs_sb_info *asb = ADFS_SB(sb);
232 struct adfs_discmap *dm;
233 unsigned int total = 0;
234 unsigned int zone;
235
236 dm = asb->s_map;
237 zone = asb->s_map_size;
238
239 do {
240 total += scan_free_map(asb, dm++);
241 } while (--zone > 0);
242
243 return signed_asl(total, asb->s_map2blk);
244}
245
246int
247adfs_map_lookup(struct super_block *sb, unsigned int frag_id,
248 unsigned int offset)
249{
250 struct adfs_sb_info *asb = ADFS_SB(sb);
251 unsigned int zone, mapoff;
252 int result;
253
254 /*
255 * map & root fragment is special - it starts in the center of the
256 * disk. The other fragments start at zone (frag / ids_per_zone)
257 */
258 if (frag_id == ADFS_ROOT_FRAG)
259 zone = asb->s_map_size >> 1;
260 else
261 zone = frag_id / asb->s_ids_per_zone;
262
263 if (zone >= asb->s_map_size)
264 goto bad_fragment;
265
266 /* Convert sector offset to map offset */
267 mapoff = signed_asl(offset, -asb->s_map2blk);
268
269 read_lock(&adfs_map_lock);
270 result = scan_map(asb, zone, frag_id, mapoff);
271 read_unlock(&adfs_map_lock);
272
273 if (result > 0) {
274 unsigned int secoff;
275
276 /* Calculate sector offset into map block */
277 secoff = offset - signed_asl(mapoff, asb->s_map2blk);
278 return secoff + signed_asl(result, asb->s_map2blk);
279 }
280
281 adfs_error(sb, "fragment 0x%04x at offset %d not found in map",
282 frag_id, offset);
283 return 0;
284
285bad_fragment:
286 adfs_error(sb, "invalid fragment 0x%04x (zone = %d, max = %d)",
287 frag_id, zone, asb->s_map_size);
288 return 0;
289}