xref: /netbsd/usr.sbin/installboot/ffs.c (revision 6550d01e)
1 /*	$NetBSD: ffs.c,v 1.29 2010/01/14 16:27:49 tsutsui 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.29 2010/01/14 16:27:49 tsutsui 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
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
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 		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 = 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 				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 = 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 		    fsbtodb(fs, blk), sblksize(fs, inode->di_size, lblk));
232 #endif
233 		rv = (*callback)(params, state,
234 		    fsbtodb(fs, blk) + params->fstype->offset,
235 		    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
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 		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 = 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 				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 = 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 		    fsbtodb(fs, blk), sblksize(fs, inode->di_size, lblk));
368 #endif
369 		rv = (*callback)(params, state,
370 		    fsbtodb(fs, blk) + params->fstype->offset,
371 		    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
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
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
469 ffs_match(ib_params *params)
470 {
471 	return ffs_match_common(params, (off_t) 0);
472 }
473 
474 int
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
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 			is_ufs2 = 1;
506 			/* FALLTHROUGH */
507 		case FS_UFS1_MAGIC:
508 			params->fstype->needswap = 0;
509 			params->fstype->blocksize = fs->fs_bsize;
510 			params->fstype->sblockloc = loc;
511 			params->fstype->offset = offset;
512 			break;
513 #ifndef FFS_NO_SWAP
514 		case FS_UFS2_MAGIC_SWAPPED:
515 			is_ufs2 = 1;
516 			/* FALLTHROUGH */
517 		case FS_UFS1_MAGIC_SWAPPED:
518 			params->fstype->needswap = 1;
519 			params->fstype->blocksize = bswap32(fs->fs_bsize);
520 			params->fstype->sblockloc = loc;
521 			params->fstype->offset = offset;
522 			break;
523 #endif
524 		default:
525 			continue;
526 		}
527 		if (!is_ufs2 && sblock_try[i] == SBLOCK_UFS2)
528 			continue;
529 		return 1;
530 	}
531 
532 	return (0);
533 }
534 
535 int
536 ffs_findstage2(ib_params *params, uint32_t *maxblk, ib_block *blocks)
537 {
538 	int			rv;
539 	uint32_t		ino;
540 	struct findblks_state	state;
541 
542 	assert(params != NULL);
543 	assert(params->stage2 != NULL);
544 	assert(maxblk != NULL);
545 	assert(blocks != NULL);
546 
547 	if (params->flags & IB_STAGE2START)
548 		return (hardcode_stage2(params, maxblk, blocks));
549 
550 	/* The secondary bootstrap must be clearly in /. */
551 	if (params->stage2[0] == '/')
552 		params->stage2++;
553 	if (strchr(params->stage2, '/') != NULL) {
554 		warnx("The secondary bootstrap `%s' must be in /",
555 		    params->stage2);
556 		warnx("(Path must be relative to the file system in `%s')",
557 		    params->filesystem);
558 		return (0);
559 	}
560 
561 	/* Get the inode number of the secondary bootstrap. */
562 	if (is_ufs2)
563 		rv = ffs_find_disk_blocks_ufs2(params, ROOTINO,
564 		    ffs_findstage2_ino, &ino);
565 	else
566 		rv = ffs_find_disk_blocks_ufs1(params, ROOTINO,
567 		    ffs_findstage2_ino, &ino);
568 	if (rv != 2) {
569 		warnx("Could not find secondary bootstrap `%s' in `%s'",
570 		    params->stage2, params->filesystem);
571 		warnx("(Path must be relative to the file system in `%s')",
572 		    params->filesystem);
573 		return (0);
574 	}
575 
576 	/* Record the disk blocks of the secondary bootstrap. */
577 	state.maxblk = *maxblk;
578 	state.nblk = 0;
579 	state.blocks = blocks;
580 	if (is_ufs2)
581 		rv = ffs_find_disk_blocks_ufs2(params, ino,
582 		    ffs_findstage2_blocks, &state);
583 	else
584 		rv = ffs_find_disk_blocks_ufs1(params, ino,
585 		    ffs_findstage2_blocks, &state);
586 	if (! rv) {
587 		return (0);
588 	}
589 
590 	*maxblk = state.nblk;
591 	return (1);
592 }
593