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