ffs.c revision 1.19 1 /* $NetBSD: ffs.c,v 1.19 2006/06/20 05:37:24 jdc 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.19 2006/06/20 05:37:24 jdc 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 #include <dev/raidframe/raidframevar.h>
67
68 #undef DIRBLKSIZ
69
70 #include <ufs/ufs/dinode.h>
71 #include <ufs/ufs/dir.h>
72 #include <ufs/ffs/fs.h>
73 #include <ufs/ffs/ffs_extern.h>
74 #ifndef NO_FFS_SWAP
75 #include <ufs/ufs/ufs_bswap.h>
76 #else
77 #define ffs_sb_swap(fs_a, fs_b)
78 #define ffs_dinode1_swap(inode_a, inode_b)
79 #define ffs_dinode2_swap(inode_a, inode_b)
80 #endif
81
82 static int ffs_match_common(ib_params *, off_t);
83 static int ffs_read_disk_block(ib_params *, uint64_t, int, char *);
84 static int ffs_find_disk_blocks_ufs1(ib_params *, ino_t,
85 int (*)(ib_params *, void *, uint64_t, uint32_t), void *);
86 static int ffs_find_disk_blocks_ufs2(ib_params *, ino_t,
87 int (*)(ib_params *, void *, uint64_t, uint32_t), void *);
88 static int ffs_findstage2_ino(ib_params *, void *, uint64_t, uint32_t);
89 static int ffs_findstage2_blocks(ib_params *, void *, uint64_t, uint32_t);
90
91 static int is_ufs2;
92
93
94 /* This reads a disk block from the filesystem. */
95 static int
96 ffs_read_disk_block(ib_params *params, uint64_t blkno, int size, char *blk)
97 {
98 int rv;
99
100 assert(params != NULL);
101 assert(blk != NULL);
102 assert(params->filesystem != NULL);
103 assert(params->fsfd != -1);
104 assert(blkno >= 0);
105 assert(size > 0);
106 assert(blk != NULL);
107
108 rv = pread(params->fsfd, blk, size, blkno * DEV_BSIZE);
109 if (rv == -1) {
110 warn("Reading block %llu in `%s'",
111 (unsigned long long)blkno, params->filesystem);
112 return (0);
113 } else if (rv != size) {
114 warnx("Reading block %llu in `%s': short read",
115 (unsigned long long)blkno, params->filesystem);
116 return (0);
117 }
118
119 return (1);
120 }
121
122 /*
123 * This iterates over the data blocks belonging to an inode,
124 * making a callback each iteration with the disk block number
125 * and the size.
126 */
127 static int
128 ffs_find_disk_blocks_ufs1(ib_params *params, ino_t ino,
129 int (*callback)(ib_params *, void *, uint64_t, uint32_t),
130 void *state)
131 {
132 char sbbuf[SBLOCKSIZE];
133 struct fs *fs;
134 char inodebuf[MAXBSIZE];
135 struct ufs1_dinode *inode;
136 int level_i;
137 int32_t blk, lblk, nblk;
138 int rv;
139 #define LEVELS 4
140 struct {
141 int32_t *blknums;
142 unsigned long blkcount;
143 char diskbuf[MAXBSIZE];
144 } level[LEVELS];
145
146 assert(params != NULL);
147 assert(params->fstype != NULL);
148 assert(callback != NULL);
149 assert(state != NULL);
150
151 /* Read the superblock. */
152 if (!ffs_read_disk_block(params, params->fstype->sblockloc, SBLOCKSIZE,
153 sbbuf))
154 return (0);
155 fs = (struct fs *)sbbuf;
156 if (params->fstype->needswap)
157 ffs_sb_swap(fs, fs);
158
159 if (fs->fs_inopb <= 0) {
160 warnx("Bad inopb %d in superblock in `%s'",
161 fs->fs_inopb, params->filesystem);
162 return (0);
163 }
164
165 /* Read the inode. */
166 if (! ffs_read_disk_block(params,
167 fsbtodb(fs, ino_to_fsba(fs, ino)) + params->fstype->offset,
168 fs->fs_bsize, inodebuf))
169 return (0);
170 inode = (struct ufs1_dinode *)inodebuf;
171 inode += ino_to_fsbo(fs, ino);
172 if (params->fstype->needswap)
173 ffs_dinode1_swap(inode, inode);
174
175 /* Get the block count and initialize for our block walk. */
176 nblk = howmany(inode->di_size, fs->fs_bsize);
177 lblk = 0;
178 level_i = 0;
179 level[0].blknums = &inode->di_db[0];
180 level[0].blkcount = NDADDR;
181 level[1].blknums = &inode->di_ib[0];
182 level[1].blkcount = 1;
183 level[2].blknums = &inode->di_ib[1];
184 level[2].blkcount = 1;
185 level[3].blknums = &inode->di_ib[2];
186 level[3].blkcount = 1;
187
188 /* Walk the data blocks. */
189 while (nblk > 0) {
190
191 /*
192 * If there are no more blocks at this indirection
193 * level, move up one indirection level and loop.
194 */
195 if (level[level_i].blkcount == 0) {
196 if (++level_i == LEVELS)
197 break;
198 continue;
199 }
200
201 /* Get the next block at this level. */
202 blk = *(level[level_i].blknums++);
203 level[level_i].blkcount--;
204 if (params->fstype->needswap)
205 blk = bswap32(blk);
206
207 #if 0
208 fprintf(stderr, "ino %lu blk %lu level %d\n", ino, blk,
209 level_i);
210 #endif
211
212 /*
213 * If we're not at the direct level, descend one
214 * level, read in that level's new block list,
215 * and loop.
216 */
217 if (level_i > 0) {
218 level_i--;
219 if (blk == 0)
220 memset(level[level_i].diskbuf, 0, MAXBSIZE);
221 else if (! ffs_read_disk_block(params,
222 fsbtodb(fs, blk) + params->fstype->offset,
223 fs->fs_bsize, level[level_i].diskbuf))
224 return (0);
225 /* XXX ondisk32 */
226 level[level_i].blknums =
227 (int32_t *)level[level_i].diskbuf;
228 level[level_i].blkcount = NINDIR(fs);
229 continue;
230 }
231
232 /* blk is the next direct level block. */
233 #if 0
234 fprintf(stderr, "ino %lu db %lu blksize %lu\n", ino,
235 fsbtodb(fs, blk), sblksize(fs, inode->di_size, lblk));
236 #endif
237 rv = (*callback)(params, state,
238 fsbtodb(fs, blk) + params->fstype->offset,
239 sblksize(fs, inode->di_size, lblk));
240 lblk++;
241 nblk--;
242 if (rv != 1)
243 return (rv);
244 }
245
246 if (nblk != 0) {
247 warnx("Inode %llu in `%s' ran out of blocks?",
248 (unsigned long long)ino, params->filesystem);
249 return (0);
250 }
251
252 return (1);
253 }
254
255 /*
256 * This iterates over the data blocks belonging to an inode,
257 * making a callback each iteration with the disk block number
258 * and the size.
259 */
260 static int
261 ffs_find_disk_blocks_ufs2(ib_params *params, ino_t ino,
262 int (*callback)(ib_params *, void *, uint64_t, uint32_t),
263 void *state)
264 {
265 char sbbuf[SBLOCKSIZE];
266 struct fs *fs;
267 char inodebuf[MAXBSIZE];
268 struct ufs2_dinode *inode;
269 int level_i;
270 int64_t blk, lblk, nblk;
271 int rv;
272 #define LEVELS 4
273 struct {
274 int64_t *blknums;
275 unsigned long blkcount;
276 char diskbuf[MAXBSIZE];
277 } level[LEVELS];
278
279 assert(params != NULL);
280 assert(params->fstype != NULL);
281 assert(callback != NULL);
282 assert(state != NULL);
283
284 /* Read the superblock. */
285 if (!ffs_read_disk_block(params, params->fstype->sblockloc, SBLOCKSIZE,
286 sbbuf))
287 return (0);
288 fs = (struct fs *)sbbuf;
289 if (params->fstype->needswap)
290 ffs_sb_swap(fs, fs);
291
292 if (fs->fs_inopb <= 0) {
293 warnx("Bad inopb %d in superblock in `%s'",
294 fs->fs_inopb, params->filesystem);
295 return (0);
296 }
297
298 /* Read the inode. */
299 if (! ffs_read_disk_block(params, fsbtodb(fs, ino_to_fsba(fs, ino)),
300 fs->fs_bsize, inodebuf))
301 return (0);
302 inode = (struct ufs2_dinode *)inodebuf;
303 inode += ino_to_fsbo(fs, ino);
304 if (params->fstype->needswap)
305 ffs_dinode2_swap(inode, inode);
306
307 /* Get the block count and initialize for our block walk. */
308 nblk = howmany(inode->di_size, fs->fs_bsize);
309 lblk = 0;
310 level_i = 0;
311 level[0].blknums = &inode->di_db[0];
312 level[0].blkcount = NDADDR;
313 level[1].blknums = &inode->di_ib[0];
314 level[1].blkcount = 1;
315 level[2].blknums = &inode->di_ib[1];
316 level[2].blkcount = 1;
317 level[3].blknums = &inode->di_ib[2];
318 level[3].blkcount = 1;
319
320 /* Walk the data blocks. */
321 while (nblk > 0) {
322
323 /*
324 * If there are no more blocks at this indirection
325 * level, move up one indirection level and loop.
326 */
327 if (level[level_i].blkcount == 0) {
328 if (++level_i == LEVELS)
329 break;
330 continue;
331 }
332
333 /* Get the next block at this level. */
334 blk = *(level[level_i].blknums++);
335 level[level_i].blkcount--;
336 if (params->fstype->needswap)
337 blk = bswap64(blk);
338
339 #if 0
340 fprintf(stderr, "ino %lu blk %llu level %d\n", ino,
341 (unsigned long long)blk, level_i);
342 #endif
343
344 /*
345 * If we're not at the direct level, descend one
346 * level, read in that level's new block list,
347 * and loop.
348 */
349 if (level_i > 0) {
350 level_i--;
351 if (blk == 0)
352 memset(level[level_i].diskbuf, 0, MAXBSIZE);
353 else if (! ffs_read_disk_block(params,
354 fsbtodb(fs, blk),
355 fs->fs_bsize, level[level_i].diskbuf))
356 return (0);
357 level[level_i].blknums =
358 (int64_t *)level[level_i].diskbuf;
359 level[level_i].blkcount = NINDIR(fs);
360 continue;
361 }
362
363 /* blk is the next direct level block. */
364 #if 0
365 fprintf(stderr, "ino %lu db %llu blksize %lu\n", ino,
366 fsbtodb(fs, blk), sblksize(fs, inode->di_size, lblk));
367 #endif
368 rv = (*callback)(params, state,
369 fsbtodb(fs, blk), sblksize(fs, inode->di_size, lblk));
370 lblk++;
371 nblk--;
372 if (rv != 1)
373 return (rv);
374 }
375
376 if (nblk != 0) {
377 warnx("Inode %llu in `%s' ran out of blocks?",
378 (unsigned long long)ino, params->filesystem);
379 return (0);
380 }
381
382 return (1);
383 }
384
385 /*
386 * This callback reads a block of the root directory,
387 * searches for an entry for the secondary bootstrap,
388 * and saves the inode number if one is found.
389 */
390 static int
391 ffs_findstage2_ino(ib_params *params, void *_ino,
392 uint64_t blk, uint32_t blksize)
393 {
394 char dirbuf[MAXBSIZE];
395 struct direct *de, *ede;
396 uint32_t ino;
397
398 assert(params != NULL);
399 assert(params->fstype != NULL);
400 assert(params->stage2 != NULL);
401 assert(_ino != NULL);
402
403 /* Skip directory holes. */
404 if (blk == 0)
405 return (1);
406
407 /* Read the directory block. */
408 if (! ffs_read_disk_block(params, blk, blksize, dirbuf))
409 return (0);
410
411 /* Loop over the directory entries. */
412 de = (struct direct *)&dirbuf[0];
413 ede = (struct direct *)&dirbuf[blksize];
414 while (de < ede) {
415 ino = de->d_fileno;
416 if (params->fstype->needswap) {
417 ino = bswap32(ino);
418 de->d_reclen = bswap16(de->d_reclen);
419 }
420 if (ino != 0 && strcmp(de->d_name, params->stage2) == 0) {
421 *((uint32_t *)_ino) = ino;
422 return (2);
423 }
424 if (de->d_reclen == 0)
425 break;
426 de = (struct direct *)((char *)de + de->d_reclen);
427 }
428
429 return (1);
430 }
431
432 struct findblks_state {
433 uint32_t maxblk;
434 uint32_t nblk;
435 ib_block *blocks;
436 };
437
438 /* This callback records the blocks of the secondary bootstrap. */
439 static int
440 ffs_findstage2_blocks(ib_params *params, void *_state,
441 uint64_t blk, uint32_t blksize)
442 {
443 struct findblks_state *state = _state;
444
445 assert(params != NULL);
446 assert(params->stage2 != NULL);
447 assert(_state != NULL);
448
449 if (state->nblk == state->maxblk) {
450 warnx("Secondary bootstrap `%s' has too many blocks (max %d)",
451 params->stage2, state->maxblk);
452 return (0);
453 }
454 state->blocks[state->nblk].block = blk;
455 state->blocks[state->nblk].blocksize = blksize;
456 state->nblk++;
457 return (1);
458 }
459
460 /*
461 * publicly visible functions
462 */
463
464 static off_t sblock_try[] = SBLOCKSEARCH;
465
466 int
467 ffs_match(ib_params *params)
468 {
469 return ffs_match_common(params, (off_t) 0);
470 }
471
472 int
473 raid_match(ib_params *params)
474 {
475 /* XXX Assumes 512 bytes / sector */
476 if (DEV_BSIZE != 512) {
477 warnx("Media is %d bytes/sector."
478 " RAID is only supported on 512 bytes/sector media.",
479 DEV_BSIZE);
480 return 0;
481 }
482 return ffs_match_common(params, (off_t) RF_PROTECTED_SECTORS);
483 }
484
485 int
486 ffs_match_common(ib_params *params, off_t offset)
487 {
488 char sbbuf[SBLOCKSIZE];
489 struct fs *fs;
490 int i;
491 off_t loc;
492
493 assert(params != NULL);
494 assert(params->fstype != NULL);
495
496 fs = (struct fs *)sbbuf;
497 for (i = 0; sblock_try[i] != -1; i++) {
498 loc = sblock_try[i] / DEV_BSIZE + offset;
499 if (!ffs_read_disk_block(params, loc, SBLOCKSIZE, sbbuf))
500 continue;
501 switch (fs->fs_magic) {
502 case FS_UFS2_MAGIC:
503 is_ufs2 = 1;
504 /* FALLTHROUGH */
505 case FS_UFS1_MAGIC:
506 params->fstype->needswap = 0;
507 params->fstype->blocksize = fs->fs_bsize;
508 params->fstype->sblockloc = loc;
509 params->fstype->offset = offset;
510 break;
511 #ifndef FFS_NO_SWAP
512 case FS_UFS2_MAGIC_SWAPPED:
513 is_ufs2 = 1;
514 /* FALLTHROUGH */
515 case FS_UFS1_MAGIC_SWAPPED:
516 params->fstype->needswap = 1;
517 params->fstype->blocksize = bswap32(fs->fs_bsize);
518 params->fstype->sblockloc = loc;
519 params->fstype->offset = offset;
520 break;
521 #endif
522 default:
523 continue;
524 }
525 if (!is_ufs2 && sblock_try[i] == SBLOCK_UFS2)
526 continue;
527 return 1;
528 }
529
530 return (0);
531 }
532
533 int
534 ffs_findstage2(ib_params *params, uint32_t *maxblk, ib_block *blocks)
535 {
536 int rv;
537 uint32_t ino;
538 struct findblks_state state;
539
540 assert(params != NULL);
541 assert(params->stage2 != NULL);
542 assert(maxblk != NULL);
543 assert(blocks != NULL);
544
545 if (params->flags & IB_STAGE2START)
546 return (hardcode_stage2(params, maxblk, blocks));
547
548 /* The secondary bootstrap must be clearly in /. */
549 if (params->stage2[0] == '/')
550 params->stage2++;
551 if (strchr(params->stage2, '/') != NULL) {
552 warnx("The secondary bootstrap `%s' must be in /",
553 params->stage2);
554 return (0);
555 }
556
557 /* Get the inode number of the secondary bootstrap. */
558 if (is_ufs2)
559 rv = ffs_find_disk_blocks_ufs2(params, ROOTINO,
560 ffs_findstage2_ino, &ino);
561 else
562 rv = ffs_find_disk_blocks_ufs1(params, ROOTINO,
563 ffs_findstage2_ino, &ino);
564 if (rv != 2) {
565 warnx("Could not find secondary bootstrap `%s' in `%s'",
566 params->stage2, params->filesystem);
567 return (0);
568 }
569
570 /* Record the disk blocks of the secondary bootstrap. */
571 state.maxblk = *maxblk;
572 state.nblk = 0;
573 state.blocks = blocks;
574 if (is_ufs2)
575 rv = ffs_find_disk_blocks_ufs2(params, ino,
576 ffs_findstage2_blocks, &state);
577 else
578 rv = ffs_find_disk_blocks_ufs1(params, ino,
579 ffs_findstage2_blocks, &state);
580 if (! rv) {
581 return (0);
582 }
583
584 *maxblk = state.nblk;
585 return (1);
586 }
587