ffs.c revision 1.15.2.3 1 /* $NetBSD: ffs.c,v 1.15.2.3 2006/11/09 22:52:34 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.3 2006/11/09 22:52:34 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,
295 fsbtodb(fs, ino_to_fsba(fs, ino)) + params->fstype->offset,
296 fs->fs_bsize, inodebuf))
297 return (0);
298 inode = (struct ufs2_dinode *)inodebuf;
299 inode += ino_to_fsbo(fs, ino);
300 if (params->fstype->needswap)
301 ffs_dinode2_swap(inode, inode);
302
303 /* Get the block count and initialize for our block walk. */
304 nblk = howmany(inode->di_size, fs->fs_bsize);
305 lblk = 0;
306 level_i = 0;
307 level[0].blknums = &inode->di_db[0];
308 level[0].blkcount = NDADDR;
309 level[1].blknums = &inode->di_ib[0];
310 level[1].blkcount = 1;
311 level[2].blknums = &inode->di_ib[1];
312 level[2].blkcount = 1;
313 level[3].blknums = &inode->di_ib[2];
314 level[3].blkcount = 1;
315
316 /* Walk the data blocks. */
317 while (nblk > 0) {
318
319 /*
320 * If there are no more blocks at this indirection
321 * level, move up one indirection level and loop.
322 */
323 if (level[level_i].blkcount == 0) {
324 if (++level_i == LEVELS)
325 break;
326 continue;
327 }
328
329 /* Get the next block at this level. */
330 blk = *(level[level_i].blknums++);
331 level[level_i].blkcount--;
332 if (params->fstype->needswap)
333 blk = bswap64(blk);
334
335 #if 0
336 fprintf(stderr, "ino %lu blk %llu level %d\n", ino,
337 (unsigned long long)blk, level_i);
338 #endif
339
340 /*
341 * If we're not at the direct level, descend one
342 * level, read in that level's new block list,
343 * and loop.
344 */
345 if (level_i > 0) {
346 level_i--;
347 if (blk == 0)
348 memset(level[level_i].diskbuf, 0, MAXBSIZE);
349 else if (! ffs_read_disk_block(params,
350 fsbtodb(fs, blk) + params->fstype->offset,
351 fs->fs_bsize, level[level_i].diskbuf))
352 return (0);
353 level[level_i].blknums =
354 (int64_t *)level[level_i].diskbuf;
355 level[level_i].blkcount = NINDIR(fs);
356 continue;
357 }
358
359 /* blk is the next direct level block. */
360 #if 0
361 fprintf(stderr, "ino %lu db %llu blksize %lu\n", ino,
362 fsbtodb(fs, blk), sblksize(fs, inode->di_size, lblk));
363 #endif
364 rv = (*callback)(params, state,
365 fsbtodb(fs, blk) + params->fstype->offset,
366 sblksize(fs, inode->di_size, lblk));
367 lblk++;
368 nblk--;
369 if (rv != 1)
370 return (rv);
371 }
372
373 if (nblk != 0) {
374 warnx("Inode %d in `%s' ran out of blocks?", ino,
375 params->filesystem);
376 return (0);
377 }
378
379 return (1);
380 }
381
382 /*
383 * This callback reads a block of the root directory,
384 * searches for an entry for the secondary bootstrap,
385 * and saves the inode number if one is found.
386 */
387 static int
388 ffs_findstage2_ino(ib_params *params, void *_ino,
389 uint64_t blk, uint32_t blksize)
390 {
391 char dirbuf[MAXBSIZE];
392 struct direct *de, *ede;
393 uint32_t ino;
394
395 assert(params != NULL);
396 assert(params->fstype != NULL);
397 assert(params->stage2 != NULL);
398 assert(_ino != NULL);
399
400 /* Skip directory holes. */
401 if (blk == 0)
402 return (1);
403
404 /* Read the directory block. */
405 if (! ffs_read_disk_block(params, blk, blksize, dirbuf))
406 return (0);
407
408 /* Loop over the directory entries. */
409 de = (struct direct *)&dirbuf[0];
410 ede = (struct direct *)&dirbuf[blksize];
411 while (de < ede) {
412 ino = de->d_ino;
413 if (params->fstype->needswap) {
414 ino = bswap32(ino);
415 de->d_reclen = bswap16(de->d_reclen);
416 }
417 if (ino != 0 && strcmp(de->d_name, params->stage2) == 0) {
418 *((uint32_t *)_ino) = ino;
419 return (2);
420 }
421 if (de->d_reclen == 0)
422 break;
423 de = (struct direct *)((char *)de + de->d_reclen);
424 }
425
426 return (1);
427 }
428
429 struct findblks_state {
430 uint32_t maxblk;
431 uint32_t nblk;
432 ib_block *blocks;
433 };
434
435 /* This callback records the blocks of the secondary bootstrap. */
436 static int
437 ffs_findstage2_blocks(ib_params *params, void *_state,
438 uint64_t blk, uint32_t blksize)
439 {
440 struct findblks_state *state = _state;
441
442 assert(params != NULL);
443 assert(params->stage2 != NULL);
444 assert(_state != NULL);
445
446 if (state->nblk == state->maxblk) {
447 warnx("Secondary bootstrap `%s' has too many blocks (max %d)",
448 params->stage2, state->maxblk);
449 return (0);
450 }
451 state->blocks[state->nblk].block = blk;
452 state->blocks[state->nblk].blocksize = blksize;
453 state->nblk++;
454 return (1);
455 }
456
457 /*
458 * publicly visible functions
459 */
460
461 static off_t sblock_try[] = SBLOCKSEARCH;
462
463 int
464 ffs_match(ib_params *params)
465 {
466 return ffs_match_common(params, (off_t) 0);
467 }
468
469 int
470 raid_match(ib_params *params)
471 {
472 /* XXX Assumes 512 bytes / sector */
473 if (DEV_BSIZE != 512) {
474 warnx("Media is %d bytes/sector."
475 " RAID is only supported on 512 bytes/sector media.",
476 DEV_BSIZE);
477 return 0;
478 }
479 return ffs_match_common(params, (off_t) RF_PROTECTED_SECTORS);
480 }
481
482 int
483 ffs_match_common(ib_params *params, off_t offset)
484 {
485 char sbbuf[SBLOCKSIZE];
486 struct fs *fs;
487 int i;
488 off_t loc;
489
490 assert(params != NULL);
491 assert(params->fstype != NULL);
492
493 fs = (struct fs *)sbbuf;
494 for (i = 0; sblock_try[i] != -1; i++) {
495 loc = sblock_try[i] / DEV_BSIZE + offset;
496 if (!ffs_read_disk_block(params, loc, SBLOCKSIZE, sbbuf))
497 continue;
498 switch (fs->fs_magic) {
499 case FS_UFS2_MAGIC:
500 is_ufs2 = 1;
501 /* FALLTHROUGH */
502 case FS_UFS1_MAGIC:
503 params->fstype->needswap = 0;
504 params->fstype->blocksize = fs->fs_bsize;
505 params->fstype->sblockloc = loc;
506 params->fstype->offset = offset;
507 break;
508 case FS_UFS2_MAGIC_SWAPPED:
509 is_ufs2 = 1;
510 /* FALLTHROUGH */
511 case FS_UFS1_MAGIC_SWAPPED:
512 params->fstype->needswap = 1;
513 params->fstype->blocksize = bswap32(fs->fs_bsize);
514 params->fstype->sblockloc = loc;
515 params->fstype->offset = offset;
516 break;
517 default:
518 continue;
519 }
520 if (!is_ufs2 && sblock_try[i] == SBLOCK_UFS2)
521 continue;
522 return 1;
523 }
524
525 return (0);
526 }
527
528 int
529 ffs_findstage2(ib_params *params, uint32_t *maxblk, ib_block *blocks)
530 {
531 int rv;
532 uint32_t ino;
533 struct findblks_state state;
534
535 assert(params != NULL);
536 assert(params->stage2 != NULL);
537 assert(maxblk != NULL);
538 assert(blocks != NULL);
539
540 if (params->flags & IB_STAGE2START)
541 return (hardcode_stage2(params, maxblk, blocks));
542
543 /* The secondary bootstrap must be clearly in /. */
544 if (params->stage2[0] == '/')
545 params->stage2++;
546 if (strchr(params->stage2, '/') != NULL) {
547 warnx("The secondary bootstrap `%s' must be in /",
548 params->stage2);
549 return (0);
550 }
551
552 /* Get the inode number of the secondary bootstrap. */
553 if (is_ufs2)
554 rv = ffs_find_disk_blocks_ufs2(params, ROOTINO,
555 ffs_findstage2_ino, &ino);
556 else
557 rv = ffs_find_disk_blocks_ufs1(params, ROOTINO,
558 ffs_findstage2_ino, &ino);
559 if (rv != 2) {
560 warnx("Could not find secondary bootstrap `%s' in `%s'",
561 params->stage2, params->filesystem);
562 return (0);
563 }
564
565 /* Record the disk blocks of the secondary bootstrap. */
566 state.maxblk = *maxblk;
567 state.nblk = 0;
568 state.blocks = blocks;
569 if (is_ufs2)
570 rv = ffs_find_disk_blocks_ufs2(params, ino,
571 ffs_findstage2_blocks, &state);
572 else
573 rv = ffs_find_disk_blocks_ufs1(params, ino,
574 ffs_findstage2_blocks, &state);
575 if (! rv) {
576 return (0);
577 }
578
579 *maxblk = state.nblk;
580 return (1);
581 }
582