1 /* $NetBSD: ffs.c,v 1.27 2009/04/05 12:03:48 lukem Exp $ */
4 * Copyright (c) 2002 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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"
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 $");
41 #include <sys/param.h>
43 #if !HAVE_NBTOOL_CONFIG_H
44 #include <sys/mount.h>
57 #include "installboot.h"
59 /* From <dev/raidframe/raidframevar.h> */
60 #define RF_PROTECTED_SECTORS 64L
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>
69 #include <ufs/ufs/ufs_bswap.h>
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)
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);
88 /* This reads a disk block from the filesystem. */
90 ffs_read_disk_block(ib_params
*params
, uint64_t blkno
, int size
, char blk
[])
94 assert(params
!= NULL
);
95 assert(params
->filesystem
!= NULL
);
96 assert(params
->fsfd
!= -1);
100 rv
= pread(params
->fsfd
, blk
, size
, blkno
* params
->sectorsize
);
102 warn("Reading block %llu in `%s'",
103 (unsigned long long)blkno
, params
->filesystem
);
105 } else if (rv
!= size
) {
106 warnx("Reading block %llu in `%s': short read",
107 (unsigned long long)blkno
, params
->filesystem
);
115 * This iterates over the data blocks belonging to an inode,
116 * making a callback each iteration with the disk block number
120 ffs_find_disk_blocks_ufs1(ib_params
*params
, ino_t ino
,
121 int (*callback
)(ib_params
*, void *, uint64_t, uint32_t),
124 char sbbuf
[SBLOCKSIZE
];
126 char inodebuf
[MAXBSIZE
];
127 struct ufs1_dinode
*inode
;
129 int32_t blk
, lblk
, nblk
;
134 unsigned long blkcount
;
135 char diskbuf
[MAXBSIZE
];
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
,
147 fs
= (struct fs
*)sbbuf
;
149 if (params
->fstype
->needswap
)
153 if (fs
->fs_inopb
<= 0) {
154 warnx("Bad inopb %d in superblock in `%s'",
155 fs
->fs_inopb
, params
->filesystem
);
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
))
164 inode
= (struct ufs1_dinode
*)inodebuf
;
165 inode
+= ino_to_fsbo(fs
, ino
);
167 if (params
->fstype
->needswap
)
168 ffs_dinode1_swap(inode
, inode
);
171 /* Get the block count and initialize for our block walk. */
172 nblk
= howmany(inode
->di_size
, fs
->fs_bsize
);
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. */
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
)
197 /* Get the next block at this level. */
198 blk
= *(level
[level_i
].blknums
++);
199 level
[level_i
].blkcount
--;
200 if (params
->fstype
->needswap
)
204 fprintf(stderr
, "ino %lu blk %lu level %d\n", ino
, blk
,
209 * If we're not at the direct level, descend one
210 * level, read in that level's new block list,
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
))
222 level
[level_i
].blknums
=
223 (int32_t *)level
[level_i
].diskbuf
;
224 level
[level_i
].blkcount
= NINDIR(fs
);
228 /* blk is the next direct level block. */
230 fprintf(stderr
, "ino %lu db %lu blksize %lu\n", ino
,
231 fsbtodb(fs
, blk
), sblksize(fs
, inode
->di_size
, lblk
));
233 rv
= (*callback
)(params
, state
,
234 fsbtodb(fs
, blk
) + params
->fstype
->offset
,
235 sblksize(fs
, (int64_t)inode
->di_size
, lblk
));
243 warnx("Inode %llu in `%s' ran out of blocks?",
244 (unsigned long long)ino
, params
->filesystem
);
252 * This iterates over the data blocks belonging to an inode,
253 * making a callback each iteration with the disk block number
257 ffs_find_disk_blocks_ufs2(ib_params
*params
, ino_t ino
,
258 int (*callback
)(ib_params
*, void *, uint64_t, uint32_t),
261 char sbbuf
[SBLOCKSIZE
];
263 char inodebuf
[MAXBSIZE
];
264 struct ufs2_dinode
*inode
;
266 int64_t blk
, lblk
, nblk
;
271 unsigned long blkcount
;
272 char diskbuf
[MAXBSIZE
];
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
,
284 fs
= (struct fs
*)sbbuf
;
286 if (params
->fstype
->needswap
)
290 if (fs
->fs_inopb
<= 0) {
291 warnx("Bad inopb %d in superblock in `%s'",
292 fs
->fs_inopb
, params
->filesystem
);
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
))
301 inode
= (struct ufs2_dinode
*)inodebuf
;
302 inode
+= ino_to_fsbo(fs
, ino
);
304 if (params
->fstype
->needswap
)
305 ffs_dinode2_swap(inode
, inode
);
308 /* Get the block count and initialize for our block walk. */
309 nblk
= howmany(inode
->di_size
, fs
->fs_bsize
);
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. */
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
)
334 /* Get the next block at this level. */
335 blk
= *(level
[level_i
].blknums
++);
336 level
[level_i
].blkcount
--;
337 if (params
->fstype
->needswap
)
341 fprintf(stderr
, "ino %lu blk %llu level %d\n", ino
,
342 (unsigned long long)blk
, level_i
);
346 * If we're not at the direct level, descend one
347 * level, read in that level's new block list,
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
))
358 level
[level_i
].blknums
=
359 (int64_t *)level
[level_i
].diskbuf
;
360 level
[level_i
].blkcount
= NINDIR(fs
);
364 /* blk is the next direct level block. */
366 fprintf(stderr
, "ino %lu db %llu blksize %lu\n", ino
,
367 fsbtodb(fs
, blk
), sblksize(fs
, inode
->di_size
, lblk
));
369 rv
= (*callback
)(params
, state
,
370 fsbtodb(fs
, blk
) + params
->fstype
->offset
,
371 sblksize(fs
, (int64_t)inode
->di_size
, lblk
));
379 warnx("Inode %llu in `%s' ran out of blocks?",
380 (unsigned long long)ino
, params
->filesystem
);
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.
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
;
400 assert(params
!= NULL
);
401 assert(params
->fstype
!= NULL
);
402 assert(params
->stage2
!= NULL
);
403 assert(_ino
!= NULL
);
405 /* Skip directory holes. */
409 /* Read the directory block. */
410 if (! ffs_read_disk_block(params
, blk
, blksize
, dirbuf
))
413 /* Loop over the directory entries. */
414 de
= (struct direct
*)&dirbuf
[0];
415 ede
= (struct direct
*)&dirbuf
[blksize
];
418 if (params
->fstype
->needswap
) {
420 de
->d_reclen
= bswap16(de
->d_reclen
);
422 if (ino
!= 0 && strcmp(de
->d_name
, params
->stage2
) == 0) {
423 *((uint32_t *)_ino
) = ino
;
426 if (de
->d_reclen
== 0)
428 de
= (struct direct
*)((char *)de
+ de
->d_reclen
);
434 struct findblks_state
{
440 /* This callback records the blocks of the secondary bootstrap. */
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
);
456 state
->blocks
[state
->nblk
].block
= blk
;
457 state
->blocks
[state
->nblk
].blocksize
= blksize
;
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.",
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
];
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
))
503 switch (fs
->fs_magic
) {
508 params
->fstype
->needswap
= 0;
509 params
->fstype
->blocksize
= fs
->fs_bsize
;
510 params
->fstype
->sblockloc
= loc
;
511 params
->fstype
->offset
= offset
;
514 case FS_UFS2_MAGIC_SWAPPED
:
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
;
527 if (!is_ufs2
&& sblock_try
[i
] == SBLOCK_UFS2
)
536 ffs_findstage2(ib_params
*params
, uint32_t *maxblk
, ib_block
*blocks
)
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] == '/')
553 if (strchr(params
->stage2
, '/') != NULL
) {
554 warnx("The secondary bootstrap `%s' must be in /",
556 warnx("(Path must be relative to the file system in `%s')",
561 /* Get the inode number of the secondary bootstrap. */
563 rv
= ffs_find_disk_blocks_ufs2(params
, ROOTINO
,
564 ffs_findstage2_ino
, &ino
);
566 rv
= ffs_find_disk_blocks_ufs1(params
, ROOTINO
,
567 ffs_findstage2_ino
, &ino
);
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')",
576 /* Record the disk blocks of the secondary bootstrap. */
577 state
.maxblk
= *maxblk
;
579 state
.blocks
= blocks
;
581 rv
= ffs_find_disk_blocks_ufs2(params
, ino
,
582 ffs_findstage2_blocks
, &state
);
584 rv
= ffs_find_disk_blocks_ufs1(params
, ino
,
585 ffs_findstage2_blocks
, &state
);
590 *maxblk
= state
.nblk
;