Sync usage with man page.
[netbsd-mini2440.git] / usr.sbin / installboot / ffs.c
blobb0615cbfcfb8c72fec4281405d68a008f18482d5
1 /* $NetBSD: ffs.c,v 1.27 2009/04/05 12:03:48 lukem Exp $ */
3 /*-
4 * Copyright (c) 2002 The NetBSD Foundation, Inc.
5 * All rights reserved.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Matt Fredette.
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.
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.
32 #if HAVE_NBTOOL_CONFIG_H
33 #include "nbtool_config.h"
34 #endif
36 #include <sys/cdefs.h>
37 #if defined(__RCSID) && !defined(__lint)
38 __RCSID("$NetBSD: ffs.c,v 1.27 2009/04/05 12:03:48 lukem Exp $");
39 #endif /* !__lint */
41 #include <sys/param.h>
43 #if !HAVE_NBTOOL_CONFIG_H
44 #include <sys/mount.h>
45 #endif
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>
57 #include "installboot.h"
59 /* From <dev/raidframe/raidframevar.h> */
60 #define RF_PROTECTED_SECTORS 64L
62 #undef DIRBLKSIZ
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
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);
85 static int is_ufs2;
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[])
92 int rv;
94 assert(params != NULL);
95 assert(params->filesystem != NULL);
96 assert(params->fsfd != -1);
97 assert(size > 0);
98 assert(blk != NULL);
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);
111 return (1);
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.
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)
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];
138 assert(params != NULL);
139 assert(params->fstype != NULL);
140 assert(callback != NULL);
141 assert(state != NULL);
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
153 if (fs->fs_inopb <= 0) {
154 warnx("Bad inopb %d in superblock in `%s'",
155 fs->fs_inopb, params->filesystem);
156 return (0);
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
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;
184 /* Walk the data blocks. */
185 while (nblk > 0) {
188 * If there are no more blocks at this indirection
189 * level, move up one indirection level and loop.
191 if (level[level_i].blkcount == 0) {
192 if (++level_i == LEVELS)
193 break;
194 continue;
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);
203 #if 0
204 fprintf(stderr, "ino %lu blk %lu level %d\n", ino, blk,
205 level_i);
206 #endif
209 * If we're not at the direct level, descend one
210 * level, read in that level's new block list,
211 * and loop.
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;
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);
242 if (nblk != 0) {
243 warnx("Inode %llu in `%s' ran out of blocks?",
244 (unsigned long long)ino, params->filesystem);
245 return (0);
248 return (1);
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.
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)
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];
275 assert(params != NULL);
276 assert(params->fstype != NULL);
277 assert(callback != NULL);
278 assert(state != NULL);
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
290 if (fs->fs_inopb <= 0) {
291 warnx("Bad inopb %d in superblock in `%s'",
292 fs->fs_inopb, params->filesystem);
293 return (0);
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
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;
321 /* Walk the data blocks. */
322 while (nblk > 0) {
325 * If there are no more blocks at this indirection
326 * level, move up one indirection level and loop.
328 if (level[level_i].blkcount == 0) {
329 if (++level_i == LEVELS)
330 break;
331 continue;
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);
340 #if 0
341 fprintf(stderr, "ino %lu blk %llu level %d\n", ino,
342 (unsigned long long)blk, level_i);
343 #endif
346 * If we're not at the direct level, descend one
347 * level, read in that level's new block list,
348 * and loop.
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;
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);
378 if (nblk != 0) {
379 warnx("Inode %llu in `%s' ran out of blocks?",
380 (unsigned long long)ino, params->filesystem);
381 return (0);
384 return (1);
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.
392 static int
393 ffs_findstage2_ino(ib_params *params, void *_ino,
394 uint64_t blk, uint32_t blksize)
396 char dirbuf[MAXBSIZE];
397 struct direct *de, *ede;
398 uint32_t ino;
400 assert(params != NULL);
401 assert(params->fstype != NULL);
402 assert(params->stage2 != NULL);
403 assert(_ino != NULL);
405 /* Skip directory holes. */
406 if (blk == 0)
407 return (1);
409 /* Read the directory block. */
410 if (! ffs_read_disk_block(params, blk, blksize, dirbuf))
411 return (0);
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);
422 if (ino != 0 && strcmp(de->d_name, params->stage2) == 0) {
423 *((uint32_t *)_ino) = ino;
424 return (2);
426 if (de->d_reclen == 0)
427 break;
428 de = (struct direct *)((char *)de + de->d_reclen);
431 return (1);
434 struct findblks_state {
435 uint32_t maxblk;
436 uint32_t nblk;
437 ib_block *blocks;
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)
445 struct findblks_state *state = _state;
447 assert(params != NULL);
448 assert(params->stage2 != NULL);
449 assert(_state != NULL);
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);
456 state->blocks[state->nblk].block = blk;
457 state->blocks[state->nblk].blocksize = blksize;
458 state->nblk++;
459 return (1);
463 * publicly visible functions
466 static off_t sblock_try[] = SBLOCKSEARCH;
469 ffs_match(ib_params *params)
471 return ffs_match_common(params, (off_t) 0);
475 raid_match(ib_params *params)
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;
484 return ffs_match_common(params, (off_t) RF_PROTECTED_SECTORS);
488 ffs_match_common(ib_params *params, off_t offset)
490 char sbbuf[SBLOCKSIZE];
491 struct fs *fs;
492 int i;
493 off_t loc;
495 assert(params != NULL);
496 assert(params->fstype != NULL);
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;
527 if (!is_ufs2 && sblock_try[i] == SBLOCK_UFS2)
528 continue;
529 return 1;
532 return (0);
536 ffs_findstage2(ib_params *params, uint32_t *maxblk, ib_block *blocks)
538 int rv;
539 uint32_t ino;
540 struct findblks_state state;
542 assert(params != NULL);
543 assert(params->stage2 != NULL);
544 assert(maxblk != NULL);
545 assert(blocks != NULL);
547 if (params->flags & IB_STAGE2START)
548 return (hardcode_stage2(params, maxblk, blocks));
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);
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);
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);
590 *maxblk = state.nblk;
591 return (1);