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