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