hc
2024-05-10 748e4f3d702def1a4bff191e0cf93b6a05340f01
kernel/fs/adfs/map.c
....@@ -1,13 +1,11 @@
1
+// SPDX-License-Identifier: GPL-2.0-only
12 /*
23 * linux/fs/adfs/map.c
34 *
45 * 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.
96 */
10
-#include <linux/buffer_head.h>
7
+#include <linux/slab.h>
8
+#include <linux/statfs.h>
119 #include <asm/unaligned.h>
1210 #include "adfs.h"
1311
....@@ -67,58 +65,44 @@
6765 * output of:
6866 * gcc -D__KERNEL__ -O2 -I../../include -o - -S map.c
6967 */
70
-static int
71
-lookup_zone(const struct adfs_discmap *dm, const unsigned int idlen,
72
- const unsigned int frag_id, unsigned int *offset)
68
+static int lookup_zone(const struct adfs_discmap *dm, const unsigned int idlen,
69
+ const u32 frag_id, unsigned int *offset)
7370 {
74
- const unsigned int mapsize = dm->dm_endbit;
71
+ const unsigned int endbit = dm->dm_endbit;
7572 const u32 idmask = (1 << idlen) - 1;
76
- unsigned char *map = dm->dm_bh->b_data + 4;
73
+ unsigned char *map = dm->dm_bh->b_data;
7774 unsigned int start = dm->dm_startbit;
78
- unsigned int mapptr;
75
+ unsigned int freelink, fragend;
7976 u32 frag;
77
+
78
+ frag = GET_FRAG_ID(map, 8, idmask & 0x7fff);
79
+ freelink = frag ? 8 + frag : 0;
8080
8181 do {
8282 frag = GET_FRAG_ID(map, start, idmask);
83
- mapptr = start + idlen;
8483
85
- /*
86
- * find end of fragment
87
- */
88
- {
89
- __le32 *_map = (__le32 *)map;
90
- u32 v = le32_to_cpu(_map[mapptr >> 5]) >> (mapptr & 31);
91
- while (v == 0) {
92
- mapptr = (mapptr & ~31) + 32;
93
- if (mapptr >= mapsize)
94
- goto error;
95
- v = le32_to_cpu(_map[mapptr >> 5]);
96
- }
84
+ fragend = find_next_bit_le(map, endbit, start + idlen);
85
+ if (fragend >= endbit)
86
+ goto error;
9787
98
- mapptr += 1 + ffz(~v);
88
+ if (start == freelink) {
89
+ freelink += frag & 0x7fff;
90
+ } else if (frag == frag_id) {
91
+ unsigned int length = fragend + 1 - start;
92
+
93
+ if (*offset < length)
94
+ return start + *offset;
95
+ *offset -= length;
9996 }
10097
101
- if (frag == frag_id)
102
- goto found;
103
-again:
104
- start = mapptr;
105
- } while (mapptr < mapsize);
98
+ start = fragend + 1;
99
+ } while (start < endbit);
106100 return -1;
107101
108102 error:
109103 printk(KERN_ERR "adfs: oversized fragment 0x%x at 0x%x-0x%x\n",
110
- frag, start, mapptr);
104
+ frag, start, fragend);
111105 return -1;
112
-
113
-found:
114
- {
115
- int length = mapptr - start;
116
- if (*offset >= length) {
117
- *offset -= length;
118
- goto again;
119
- }
120
- }
121
- return start + *offset;
122106 }
123107
124108 /*
....@@ -130,12 +114,12 @@
130114 static unsigned int
131115 scan_free_map(struct adfs_sb_info *asb, struct adfs_discmap *dm)
132116 {
133
- const unsigned int mapsize = dm->dm_endbit + 32;
117
+ const unsigned int endbit = dm->dm_endbit;
134118 const unsigned int idlen = asb->s_idlen;
135119 const unsigned int frag_idlen = idlen <= 15 ? idlen : 15;
136120 const u32 idmask = (1 << frag_idlen) - 1;
137121 unsigned char *map = dm->dm_bh->b_data;
138
- unsigned int start = 8, mapptr;
122
+ unsigned int start = 8, fragend;
139123 u32 frag;
140124 unsigned long total = 0;
141125
....@@ -154,29 +138,13 @@
154138 do {
155139 start += frag;
156140
157
- /*
158
- * get fragment id
159
- */
160141 frag = GET_FRAG_ID(map, start, idmask);
161
- mapptr = start + idlen;
162142
163
- /*
164
- * find end of fragment
165
- */
166
- {
167
- __le32 *_map = (__le32 *)map;
168
- u32 v = le32_to_cpu(_map[mapptr >> 5]) >> (mapptr & 31);
169
- while (v == 0) {
170
- mapptr = (mapptr & ~31) + 32;
171
- if (mapptr >= mapsize)
172
- goto error;
173
- v = le32_to_cpu(_map[mapptr >> 5]);
174
- }
143
+ fragend = find_next_bit_le(map, endbit, start + idlen);
144
+ if (fragend >= endbit)
145
+ goto error;
175146
176
- mapptr += 1 + ffz(~v);
177
- }
178
-
179
- total += mapptr - start;
147
+ total += fragend + 1 - start;
180148 } while (frag >= idlen + 1);
181149
182150 if (frag != 0)
....@@ -188,9 +156,8 @@
188156 return 0;
189157 }
190158
191
-static int
192
-scan_map(struct adfs_sb_info *asb, unsigned int zone,
193
- const unsigned int frag_id, unsigned int mapoff)
159
+static int scan_map(struct adfs_sb_info *asb, unsigned int zone,
160
+ const u32 frag_id, unsigned int mapoff)
194161 {
195162 const unsigned int idlen = asb->s_idlen;
196163 struct adfs_discmap *dm, *dm_end;
....@@ -226,10 +193,10 @@
226193 * total_free = E(free_in_zone_n)
227194 * nzones
228195 */
229
-unsigned int
230
-adfs_map_free(struct super_block *sb)
196
+void adfs_map_statfs(struct super_block *sb, struct kstatfs *buf)
231197 {
232198 struct adfs_sb_info *asb = ADFS_SB(sb);
199
+ struct adfs_discrecord *dr = adfs_map_discrecord(asb->s_map);
233200 struct adfs_discmap *dm;
234201 unsigned int total = 0;
235202 unsigned int zone;
....@@ -241,12 +208,13 @@
241208 total += scan_free_map(asb, dm++);
242209 } while (--zone > 0);
243210
244
- return signed_asl(total, asb->s_map2blk);
211
+ buf->f_blocks = adfs_disc_size(dr) >> sb->s_blocksize_bits;
212
+ buf->f_files = asb->s_ids_per_zone * asb->s_map_size;
213
+ buf->f_bavail =
214
+ buf->f_bfree = signed_asl(total, asb->s_map2blk);
245215 }
246216
247
-int
248
-adfs_map_lookup(struct super_block *sb, unsigned int frag_id,
249
- unsigned int offset)
217
+int adfs_map_lookup(struct super_block *sb, u32 frag_id, unsigned int offset)
250218 {
251219 struct adfs_sb_info *asb = ADFS_SB(sb);
252220 unsigned int zone, mapoff;
....@@ -288,3 +256,152 @@
288256 frag_id, zone, asb->s_map_size);
289257 return 0;
290258 }
259
+
260
+static unsigned char adfs_calczonecheck(struct super_block *sb, unsigned char *map)
261
+{
262
+ unsigned int v0, v1, v2, v3;
263
+ int i;
264
+
265
+ v0 = v1 = v2 = v3 = 0;
266
+ for (i = sb->s_blocksize - 4; i; i -= 4) {
267
+ v0 += map[i] + (v3 >> 8);
268
+ v3 &= 0xff;
269
+ v1 += map[i + 1] + (v0 >> 8);
270
+ v0 &= 0xff;
271
+ v2 += map[i + 2] + (v1 >> 8);
272
+ v1 &= 0xff;
273
+ v3 += map[i + 3] + (v2 >> 8);
274
+ v2 &= 0xff;
275
+ }
276
+ v0 += v3 >> 8;
277
+ v1 += map[1] + (v0 >> 8);
278
+ v2 += map[2] + (v1 >> 8);
279
+ v3 += map[3] + (v2 >> 8);
280
+
281
+ return v0 ^ v1 ^ v2 ^ v3;
282
+}
283
+
284
+static int adfs_checkmap(struct super_block *sb, struct adfs_discmap *dm)
285
+{
286
+ unsigned char crosscheck = 0, zonecheck = 1;
287
+ int i;
288
+
289
+ for (i = 0; i < ADFS_SB(sb)->s_map_size; i++) {
290
+ unsigned char *map;
291
+
292
+ map = dm[i].dm_bh->b_data;
293
+
294
+ if (adfs_calczonecheck(sb, map) != map[0]) {
295
+ adfs_error(sb, "zone %d fails zonecheck", i);
296
+ zonecheck = 0;
297
+ }
298
+ crosscheck ^= map[3];
299
+ }
300
+ if (crosscheck != 0xff)
301
+ adfs_error(sb, "crosscheck != 0xff");
302
+ return crosscheck == 0xff && zonecheck;
303
+}
304
+
305
+/*
306
+ * Layout the map - the first zone contains a copy of the disc record,
307
+ * and the last zone must be limited to the size of the filesystem.
308
+ */
309
+static void adfs_map_layout(struct adfs_discmap *dm, unsigned int nzones,
310
+ struct adfs_discrecord *dr)
311
+{
312
+ unsigned int zone, zone_size;
313
+ u64 size;
314
+
315
+ zone_size = (8 << dr->log2secsize) - le16_to_cpu(dr->zone_spare);
316
+
317
+ dm[0].dm_bh = NULL;
318
+ dm[0].dm_startblk = 0;
319
+ dm[0].dm_startbit = 32 + ADFS_DR_SIZE_BITS;
320
+ dm[0].dm_endbit = 32 + zone_size;
321
+
322
+ for (zone = 1; zone < nzones; zone++) {
323
+ dm[zone].dm_bh = NULL;
324
+ dm[zone].dm_startblk = zone * zone_size - ADFS_DR_SIZE_BITS;
325
+ dm[zone].dm_startbit = 32;
326
+ dm[zone].dm_endbit = 32 + zone_size;
327
+ }
328
+
329
+ size = adfs_disc_size(dr) >> dr->log2bpmb;
330
+ size -= (nzones - 1) * zone_size - ADFS_DR_SIZE_BITS;
331
+ dm[nzones - 1].dm_endbit = 32 + size;
332
+}
333
+
334
+static int adfs_map_read(struct adfs_discmap *dm, struct super_block *sb,
335
+ unsigned int map_addr, unsigned int nzones)
336
+{
337
+ unsigned int zone;
338
+
339
+ for (zone = 0; zone < nzones; zone++) {
340
+ dm[zone].dm_bh = sb_bread(sb, map_addr + zone);
341
+ if (!dm[zone].dm_bh)
342
+ return -EIO;
343
+ }
344
+
345
+ return 0;
346
+}
347
+
348
+static void adfs_map_relse(struct adfs_discmap *dm, unsigned int nzones)
349
+{
350
+ unsigned int zone;
351
+
352
+ for (zone = 0; zone < nzones; zone++)
353
+ brelse(dm[zone].dm_bh);
354
+}
355
+
356
+struct adfs_discmap *adfs_read_map(struct super_block *sb, struct adfs_discrecord *dr)
357
+{
358
+ struct adfs_sb_info *asb = ADFS_SB(sb);
359
+ struct adfs_discmap *dm;
360
+ unsigned int map_addr, zone_size, nzones;
361
+ int ret;
362
+
363
+ nzones = dr->nzones | dr->nzones_high << 8;
364
+ zone_size = (8 << dr->log2secsize) - le16_to_cpu(dr->zone_spare);
365
+
366
+ asb->s_idlen = dr->idlen;
367
+ asb->s_map_size = nzones;
368
+ asb->s_map2blk = dr->log2bpmb - dr->log2secsize;
369
+ asb->s_log2sharesize = dr->log2sharesize;
370
+ asb->s_ids_per_zone = zone_size / (asb->s_idlen + 1);
371
+
372
+ map_addr = (nzones >> 1) * zone_size -
373
+ ((nzones > 1) ? ADFS_DR_SIZE_BITS : 0);
374
+ map_addr = signed_asl(map_addr, asb->s_map2blk);
375
+
376
+ dm = kmalloc_array(nzones, sizeof(*dm), GFP_KERNEL);
377
+ if (dm == NULL) {
378
+ adfs_error(sb, "not enough memory");
379
+ return ERR_PTR(-ENOMEM);
380
+ }
381
+
382
+ adfs_map_layout(dm, nzones, dr);
383
+
384
+ ret = adfs_map_read(dm, sb, map_addr, nzones);
385
+ if (ret) {
386
+ adfs_error(sb, "unable to read map");
387
+ goto error_free;
388
+ }
389
+
390
+ if (adfs_checkmap(sb, dm))
391
+ return dm;
392
+
393
+ adfs_error(sb, "map corrupted");
394
+
395
+error_free:
396
+ adfs_map_relse(dm, nzones);
397
+ kfree(dm);
398
+ return ERR_PTR(-EIO);
399
+}
400
+
401
+void adfs_free_map(struct super_block *sb)
402
+{
403
+ struct adfs_sb_info *asb = ADFS_SB(sb);
404
+
405
+ adfs_map_relse(asb->s_map, asb->s_map_size);
406
+ kfree(asb->s_map);
407
+}