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