1 /* $NetBSD: ffs.c,v 1.33 2022/11/17 06:40:40 chs Exp $ */
2
3 /*-
4 * Copyright (c) 2002 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Matt Fredette.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #if HAVE_NBTOOL_CONFIG_H
33 #include "nbtool_config.h"
34 #endif
35
36 #include <sys/cdefs.h>
37 #if !defined(__lint)
38 __RCSID("$NetBSD: ffs.c,v 1.33 2022/11/17 06:40:40 chs Exp $");
39 #endif /* !__lint */
40
41 #include <sys/param.h>
42
43 #if !HAVE_NBTOOL_CONFIG_H
44 #include <sys/mount.h>
45 #endif
46
47 #include <assert.h>
48 #include <err.h>
49 #include <errno.h>
50 #include <fcntl.h>
51 #include <stdarg.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <unistd.h>
56
57 #include "installboot.h"
58
59 /* From <dev/raidframe/raidframevar.h> */
60 #define RF_PROTECTED_SECTORS 64L
61
62 #undef DIRBLKSIZ
63
64 #include <ufs/ufs/dinode.h>
65 #include <ufs/ufs/dir.h>
66 #include <ufs/ffs/fs.h>
67 #include <ufs/ffs/ffs_extern.h>
68 #ifndef NO_FFS_SWAP
69 #include <ufs/ufs/ufs_bswap.h>
70 #else
71 #define ffs_sb_swap(fs_a, fs_b)
72 #define ffs_dinode1_swap(inode_a, inode_b)
73 #define ffs_dinode2_swap(inode_a, inode_b)
74 #endif
75
76 static int ffs_match_common(ib_params *, off_t);
77 static int ffs_read_disk_block(ib_params *, uint64_t, int, char []);
78 static int ffs_find_disk_blocks_ufs1(ib_params *, ino_t,
79 int (*)(ib_params *, void *, uint64_t, uint32_t), void *);
80 static int ffs_find_disk_blocks_ufs2(ib_params *, ino_t,
81 int (*)(ib_params *, void *, uint64_t, uint32_t), void *);
82 static int ffs_findstage2_ino(ib_params *, void *, uint64_t, uint32_t);
83 static int ffs_findstage2_blocks(ib_params *, void *, uint64_t, uint32_t);
84
85 static int is_ufs2;
86
87
88 /* This reads a disk block from the filesystem. */
89 static int
ffs_read_disk_block(ib_params * params,uint64_t blkno,int size,char blk[])90 ffs_read_disk_block(ib_params *params, uint64_t blkno, int size, char blk[])
91 {
92 int rv;
93
94 assert(params != NULL);
95 assert(params->filesystem != NULL);
96 assert(params->fsfd != -1);
97 assert(size > 0);
98 assert(blk != NULL);
99
100 rv = pread(params->fsfd, blk, size, blkno * params->sectorsize);
101 if (rv == -1) {
102 warn("Reading block %llu in `%s'",
103 (unsigned long long)blkno, params->filesystem);
104 return (0);
105 } else if (rv != size) {
106 warnx("Reading block %llu in `%s': short read",
107 (unsigned long long)blkno, params->filesystem);
108 return (0);
109 }
110
111 return (1);
112 }
113
114 /*
115 * This iterates over the data blocks belonging to an inode,
116 * making a callback each iteration with the disk block number
117 * and the size.
118 */
119 static int
ffs_find_disk_blocks_ufs1(ib_params * params,ino_t ino,int (* callback)(ib_params *,void *,uint64_t,uint32_t),void * state)120 ffs_find_disk_blocks_ufs1(ib_params *params, ino_t ino,
121 int (*callback)(ib_params *, void *, uint64_t, uint32_t),
122 void *state)
123 {
124 char sbbuf[SBLOCKSIZE];
125 struct fs *fs;
126 char inodebuf[MAXBSIZE];
127 struct ufs1_dinode *inode;
128 int level_i;
129 int32_t blk, lblk, nblk;
130 int rv;
131 #define LEVELS 4
132 struct {
133 int32_t *blknums;
134 unsigned long blkcount;
135 char diskbuf[MAXBSIZE];
136 } level[LEVELS];
137
138 assert(params != NULL);
139 assert(params->fstype != NULL);
140 assert(callback != NULL);
141 assert(state != NULL);
142
143 /* Read the superblock. */
144 if (!ffs_read_disk_block(params, params->fstype->sblockloc, SBLOCKSIZE,
145 sbbuf))
146 return (0);
147 fs = (struct fs *)sbbuf;
148 #ifndef NO_FFS_SWAP
149 if (params->fstype->needswap)
150 ffs_sb_swap(fs, fs);
151 #endif
152
153 if (fs->fs_inopb <= 0) {
154 warnx("Bad inopb %d in superblock in `%s'",
155 fs->fs_inopb, params->filesystem);
156 return (0);
157 }
158
159 /* Read the inode. */
160 if (! ffs_read_disk_block(params,
161 FFS_FSBTODB(fs, ino_to_fsba(fs, ino)) + params->fstype->offset,
162 fs->fs_bsize, inodebuf))
163 return (0);
164 inode = (struct ufs1_dinode *)inodebuf;
165 inode += ino_to_fsbo(fs, ino);
166 #ifndef NO_FFS_SWAP
167 if (params->fstype->needswap)
168 ffs_dinode1_swap(inode, inode);
169 #endif
170
171 /* Get the block count and initialize for our block walk. */
172 nblk = howmany(inode->di_size, fs->fs_bsize);
173 lblk = 0;
174 level_i = 0;
175 level[0].blknums = &inode->di_db[0];
176 level[0].blkcount = UFS_NDADDR;
177 level[1].blknums = &inode->di_ib[0];
178 level[1].blkcount = 1;
179 level[2].blknums = &inode->di_ib[1];
180 level[2].blkcount = 1;
181 level[3].blknums = &inode->di_ib[2];
182 level[3].blkcount = 1;
183
184 /* Walk the data blocks. */
185 while (nblk > 0) {
186
187 /*
188 * If there are no more blocks at this indirection
189 * level, move up one indirection level and loop.
190 */
191 if (level[level_i].blkcount == 0) {
192 if (++level_i == LEVELS)
193 break;
194 continue;
195 }
196
197 /* Get the next block at this level. */
198 blk = *(level[level_i].blknums++);
199 level[level_i].blkcount--;
200 if (params->fstype->needswap)
201 blk = bswap32(blk);
202
203 #if 0
204 fprintf(stderr, "ino %lu blk %lu level %d\n", ino, blk,
205 level_i);
206 #endif
207
208 /*
209 * If we're not at the direct level, descend one
210 * level, read in that level's new block list,
211 * and loop.
212 */
213 if (level_i > 0) {
214 level_i--;
215 if (blk == 0)
216 memset(level[level_i].diskbuf, 0, MAXBSIZE);
217 else if (! ffs_read_disk_block(params,
218 FFS_FSBTODB(fs, blk) + params->fstype->offset,
219 fs->fs_bsize, level[level_i].diskbuf))
220 return (0);
221 /* XXX ondisk32 */
222 level[level_i].blknums =
223 (int32_t *)level[level_i].diskbuf;
224 level[level_i].blkcount = FFS_NINDIR(fs);
225 continue;
226 }
227
228 /* blk is the next direct level block. */
229 #if 0
230 fprintf(stderr, "ino %lu db %lu blksize %lu\n", ino,
231 FFS_FSBTODB(fs, blk), ffs_sblksize(fs, inode->di_size, lblk));
232 #endif
233 rv = (*callback)(params, state,
234 FFS_FSBTODB(fs, blk) + params->fstype->offset,
235 ffs_sblksize(fs, (int64_t)inode->di_size, lblk));
236 lblk++;
237 nblk--;
238 if (rv != 1)
239 return (rv);
240 }
241
242 if (nblk != 0) {
243 warnx("Inode %llu in `%s' ran out of blocks?",
244 (unsigned long long)ino, params->filesystem);
245 return (0);
246 }
247
248 return (1);
249 }
250
251 /*
252 * This iterates over the data blocks belonging to an inode,
253 * making a callback each iteration with the disk block number
254 * and the size.
255 */
256 static int
ffs_find_disk_blocks_ufs2(ib_params * params,ino_t ino,int (* callback)(ib_params *,void *,uint64_t,uint32_t),void * state)257 ffs_find_disk_blocks_ufs2(ib_params *params, ino_t ino,
258 int (*callback)(ib_params *, void *, uint64_t, uint32_t),
259 void *state)
260 {
261 char sbbuf[SBLOCKSIZE];
262 struct fs *fs;
263 char inodebuf[MAXBSIZE];
264 struct ufs2_dinode *inode;
265 int level_i;
266 int64_t blk, lblk, nblk;
267 int rv;
268 #define LEVELS 4
269 struct {
270 int64_t *blknums;
271 unsigned long blkcount;
272 char diskbuf[MAXBSIZE];
273 } level[LEVELS];
274
275 assert(params != NULL);
276 assert(params->fstype != NULL);
277 assert(callback != NULL);
278 assert(state != NULL);
279
280 /* Read the superblock. */
281 if (!ffs_read_disk_block(params, params->fstype->sblockloc, SBLOCKSIZE,
282 sbbuf))
283 return (0);
284 fs = (struct fs *)sbbuf;
285 #ifndef NO_FFS_SWAP
286 if (params->fstype->needswap)
287 ffs_sb_swap(fs, fs);
288 #endif
289
290 if (fs->fs_inopb <= 0) {
291 warnx("Bad inopb %d in superblock in `%s'",
292 fs->fs_inopb, params->filesystem);
293 return (0);
294 }
295
296 /* Read the inode. */
297 if (! ffs_read_disk_block(params,
298 FFS_FSBTODB(fs, ino_to_fsba(fs, ino)) + params->fstype->offset,
299 fs->fs_bsize, inodebuf))
300 return (0);
301 inode = (struct ufs2_dinode *)inodebuf;
302 inode += ino_to_fsbo(fs, ino);
303 #ifndef NO_FFS_SWAP
304 if (params->fstype->needswap)
305 ffs_dinode2_swap(inode, inode);
306 #endif
307
308 /* Get the block count and initialize for our block walk. */
309 nblk = howmany(inode->di_size, fs->fs_bsize);
310 lblk = 0;
311 level_i = 0;
312 level[0].blknums = &inode->di_db[0];
313 level[0].blkcount = UFS_NDADDR;
314 level[1].blknums = &inode->di_ib[0];
315 level[1].blkcount = 1;
316 level[2].blknums = &inode->di_ib[1];
317 level[2].blkcount = 1;
318 level[3].blknums = &inode->di_ib[2];
319 level[3].blkcount = 1;
320
321 /* Walk the data blocks. */
322 while (nblk > 0) {
323
324 /*
325 * If there are no more blocks at this indirection
326 * level, move up one indirection level and loop.
327 */
328 if (level[level_i].blkcount == 0) {
329 if (++level_i == LEVELS)
330 break;
331 continue;
332 }
333
334 /* Get the next block at this level. */
335 blk = *(level[level_i].blknums++);
336 level[level_i].blkcount--;
337 if (params->fstype->needswap)
338 blk = bswap64(blk);
339
340 #if 0
341 fprintf(stderr, "ino %lu blk %llu level %d\n", ino,
342 (unsigned long long)blk, level_i);
343 #endif
344
345 /*
346 * If we're not at the direct level, descend one
347 * level, read in that level's new block list,
348 * and loop.
349 */
350 if (level_i > 0) {
351 level_i--;
352 if (blk == 0)
353 memset(level[level_i].diskbuf, 0, MAXBSIZE);
354 else if (! ffs_read_disk_block(params,
355 FFS_FSBTODB(fs, blk) + params->fstype->offset,
356 fs->fs_bsize, level[level_i].diskbuf))
357 return (0);
358 level[level_i].blknums =
359 (int64_t *)level[level_i].diskbuf;
360 level[level_i].blkcount = FFS_NINDIR(fs);
361 continue;
362 }
363
364 /* blk is the next direct level block. */
365 #if 0
366 fprintf(stderr, "ino %lu db %llu blksize %lu\n", ino,
367 FFS_FSBTODB(fs, blk), ffs_sblksize(fs, inode->di_size, lblk));
368 #endif
369 rv = (*callback)(params, state,
370 FFS_FSBTODB(fs, blk) + params->fstype->offset,
371 ffs_sblksize(fs, (int64_t)inode->di_size, lblk));
372 lblk++;
373 nblk--;
374 if (rv != 1)
375 return (rv);
376 }
377
378 if (nblk != 0) {
379 warnx("Inode %llu in `%s' ran out of blocks?",
380 (unsigned long long)ino, params->filesystem);
381 return (0);
382 }
383
384 return (1);
385 }
386
387 /*
388 * This callback reads a block of the root directory,
389 * searches for an entry for the secondary bootstrap,
390 * and saves the inode number if one is found.
391 */
392 static int
ffs_findstage2_ino(ib_params * params,void * _ino,uint64_t blk,uint32_t blksize)393 ffs_findstage2_ino(ib_params *params, void *_ino,
394 uint64_t blk, uint32_t blksize)
395 {
396 char dirbuf[MAXBSIZE];
397 struct direct *de, *ede;
398 uint32_t ino;
399
400 assert(params != NULL);
401 assert(params->fstype != NULL);
402 assert(params->stage2 != NULL);
403 assert(_ino != NULL);
404
405 /* Skip directory holes. */
406 if (blk == 0)
407 return (1);
408
409 /* Read the directory block. */
410 if (! ffs_read_disk_block(params, blk, blksize, dirbuf))
411 return (0);
412
413 /* Loop over the directory entries. */
414 de = (struct direct *)&dirbuf[0];
415 ede = (struct direct *)&dirbuf[blksize];
416 while (de < ede) {
417 ino = de->d_fileno;
418 if (params->fstype->needswap) {
419 ino = bswap32(ino);
420 de->d_reclen = bswap16(de->d_reclen);
421 }
422 if (ino != 0 && strcmp(de->d_name, params->stage2) == 0) {
423 *((uint32_t *)_ino) = ino;
424 return (2);
425 }
426 if (de->d_reclen == 0)
427 break;
428 de = (struct direct *)((char *)de + de->d_reclen);
429 }
430
431 return (1);
432 }
433
434 struct findblks_state {
435 uint32_t maxblk;
436 uint32_t nblk;
437 ib_block *blocks;
438 };
439
440 /* This callback records the blocks of the secondary bootstrap. */
441 static int
ffs_findstage2_blocks(ib_params * params,void * _state,uint64_t blk,uint32_t blksize)442 ffs_findstage2_blocks(ib_params *params, void *_state,
443 uint64_t blk, uint32_t blksize)
444 {
445 struct findblks_state *state = _state;
446
447 assert(params != NULL);
448 assert(params->stage2 != NULL);
449 assert(_state != NULL);
450
451 if (state->nblk == state->maxblk) {
452 warnx("Secondary bootstrap `%s' has too many blocks (max %d)",
453 params->stage2, state->maxblk);
454 return (0);
455 }
456 state->blocks[state->nblk].block = blk;
457 state->blocks[state->nblk].blocksize = blksize;
458 state->nblk++;
459 return (1);
460 }
461
462 /*
463 * publicly visible functions
464 */
465
466 static off_t sblock_try[] = SBLOCKSEARCH;
467
468 int
ffs_match(ib_params * params)469 ffs_match(ib_params *params)
470 {
471 return ffs_match_common(params, (off_t) 0);
472 }
473
474 int
raid_match(ib_params * params)475 raid_match(ib_params *params)
476 {
477 /* XXX Assumes 512 bytes / sector */
478 if (params->sectorsize != 512) {
479 warnx("Media is %d bytes/sector."
480 " RAID is only supported on 512 bytes/sector media.",
481 params->sectorsize);
482 return 0;
483 }
484 return ffs_match_common(params, (off_t) RF_PROTECTED_SECTORS);
485 }
486
487 int
ffs_match_common(ib_params * params,off_t offset)488 ffs_match_common(ib_params *params, off_t offset)
489 {
490 char sbbuf[SBLOCKSIZE];
491 struct fs *fs;
492 int i;
493 off_t loc;
494
495 assert(params != NULL);
496 assert(params->fstype != NULL);
497
498 fs = (struct fs *)sbbuf;
499 for (i = 0; sblock_try[i] != -1; i++) {
500 loc = sblock_try[i] / params->sectorsize + offset;
501 if (!ffs_read_disk_block(params, loc, SBLOCKSIZE, sbbuf))
502 continue;
503 switch (fs->fs_magic) {
504 case FS_UFS2_MAGIC:
505 case FS_UFS2EA_MAGIC:
506 is_ufs2 = 1;
507 /* FALLTHROUGH */
508 case FS_UFS1_MAGIC:
509 params->fstype->needswap = 0;
510 params->fstype->blocksize = fs->fs_bsize;
511 params->fstype->sblockloc = loc;
512 params->fstype->offset = offset;
513 break;
514 #ifndef FFS_NO_SWAP
515 case FS_UFS2_MAGIC_SWAPPED:
516 case FS_UFS2EA_MAGIC_SWAPPED:
517 is_ufs2 = 1;
518 /* FALLTHROUGH */
519 case FS_UFS1_MAGIC_SWAPPED:
520 params->fstype->needswap = 1;
521 params->fstype->blocksize = bswap32(fs->fs_bsize);
522 params->fstype->sblockloc = loc;
523 params->fstype->offset = offset;
524 break;
525 #endif
526 default:
527 continue;
528 }
529 if (!is_ufs2 && sblock_try[i] == SBLOCK_UFS2)
530 continue;
531 return 1;
532 }
533
534 return (0);
535 }
536
537 int
ffs_findstage2(ib_params * params,uint32_t * maxblk,ib_block * blocks)538 ffs_findstage2(ib_params *params, uint32_t *maxblk, ib_block *blocks)
539 {
540 int rv;
541 uint32_t ino;
542 struct findblks_state state;
543
544 assert(params != NULL);
545 assert(params->stage2 != NULL);
546 assert(maxblk != NULL);
547 assert(blocks != NULL);
548
549 if (params->flags & IB_STAGE2START)
550 return (hardcode_stage2(params, maxblk, blocks));
551
552 /* The secondary bootstrap must be clearly in /. */
553 if (params->stage2[0] == '/')
554 params->stage2++;
555 if (strchr(params->stage2, '/') != NULL) {
556 warnx("The secondary bootstrap `%s' must be in /",
557 params->stage2);
558 warnx("(Path must be relative to the file system in `%s')",
559 params->filesystem);
560 return (0);
561 }
562
563 /* Get the inode number of the secondary bootstrap. */
564 if (is_ufs2)
565 rv = ffs_find_disk_blocks_ufs2(params, UFS_ROOTINO,
566 ffs_findstage2_ino, &ino);
567 else
568 rv = ffs_find_disk_blocks_ufs1(params, UFS_ROOTINO,
569 ffs_findstage2_ino, &ino);
570 if (rv != 2) {
571 warnx("Could not find secondary bootstrap `%s' in `%s'",
572 params->stage2, params->filesystem);
573 warnx("(Path must be relative to the file system in `%s')",
574 params->filesystem);
575 return (0);
576 }
577
578 /* Record the disk blocks of the secondary bootstrap. */
579 state.maxblk = *maxblk;
580 state.nblk = 0;
581 state.blocks = blocks;
582 if (is_ufs2)
583 rv = ffs_find_disk_blocks_ufs2(params, ino,
584 ffs_findstage2_blocks, &state);
585 else
586 rv = ffs_find_disk_blocks_ufs1(params, ino,
587 ffs_findstage2_blocks, &state);
588 if (! rv) {
589 return (0);
590 }
591
592 *maxblk = state.nblk;
593 return (1);
594 }
595