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