ffs.c revision 1.18 1 /* $NetBSD: ffs.c,v 1.18 2006/02/18 12:39:38 dsl 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.18 2006/02/18 12:39:38 dsl 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 #undef DIRBLKSIZ
67
68 #include <ufs/ufs/dinode.h>
69 #include <ufs/ufs/dir.h>
70 #include <ufs/ffs/fs.h>
71 #include <ufs/ffs/ffs_extern.h>
72 #ifndef NO_FFS_SWAP
73 #include <ufs/ufs/ufs_bswap.h>
74 #else
75 #define ffs_sb_swap(fs_a, fs_b)
76 #define ffs_dinode1_swap(inode_a, inode_b)
77 #define ffs_dinode2_swap(inode_a, inode_b)
78 #endif
79
80 static int ffs_read_disk_block(ib_params *, uint64_t, int, char *);
81 static int ffs_find_disk_blocks_ufs1(ib_params *, ino_t,
82 int (*)(ib_params *, void *, uint64_t, uint32_t), void *);
83 static int ffs_find_disk_blocks_ufs2(ib_params *, ino_t,
84 int (*)(ib_params *, void *, uint64_t, uint32_t), void *);
85 static int ffs_findstage2_ino(ib_params *, void *, uint64_t, uint32_t);
86 static int ffs_findstage2_blocks(ib_params *, void *, uint64_t, uint32_t);
87
88 static int is_ufs2;
89
90
91 /* This reads a disk block from the filesystem. */
92 static int
93 ffs_read_disk_block(ib_params *params, uint64_t blkno, int size, char *blk)
94 {
95 int rv;
96
97 assert(params != NULL);
98 assert(blk != NULL);
99 assert(params->filesystem != NULL);
100 assert(params->fsfd != -1);
101 assert(blkno >= 0);
102 assert(size > 0);
103 assert(blk != NULL);
104
105 rv = pread(params->fsfd, blk, size, blkno * DEV_BSIZE);
106 if (rv == -1) {
107 warn("Reading block %llu in `%s'",
108 (unsigned long long)blkno, params->filesystem);
109 return (0);
110 } else if (rv != size) {
111 warnx("Reading block %llu in `%s': short read",
112 (unsigned long long)blkno, params->filesystem);
113 return (0);
114 }
115
116 return (1);
117 }
118
119 /*
120 * This iterates over the data blocks belonging to an inode,
121 * making a callback each iteration with the disk block number
122 * and the size.
123 */
124 static int
125 ffs_find_disk_blocks_ufs1(ib_params *params, ino_t ino,
126 int (*callback)(ib_params *, void *, uint64_t, uint32_t),
127 void *state)
128 {
129 char sbbuf[SBLOCKSIZE];
130 struct fs *fs;
131 char inodebuf[MAXBSIZE];
132 struct ufs1_dinode *inode;
133 int level_i;
134 int32_t blk, lblk, nblk;
135 int rv;
136 #define LEVELS 4
137 struct {
138 int32_t *blknums;
139 unsigned long blkcount;
140 char diskbuf[MAXBSIZE];
141 } level[LEVELS];
142
143 assert(params != NULL);
144 assert(params->fstype != NULL);
145 assert(callback != NULL);
146 assert(state != NULL);
147
148 /* Read the superblock. */
149 if (!ffs_read_disk_block(params, params->fstype->sblockloc, SBLOCKSIZE,
150 sbbuf))
151 return (0);
152 fs = (struct fs *)sbbuf;
153 if (params->fstype->needswap)
154 ffs_sb_swap(fs, fs);
155
156 if (fs->fs_inopb <= 0) {
157 warnx("Bad inopb %d in superblock in `%s'",
158 fs->fs_inopb, params->filesystem);
159 return (0);
160 }
161
162 /* Read the inode. */
163 if (! ffs_read_disk_block(params, fsbtodb(fs, ino_to_fsba(fs, ino)),
164 fs->fs_bsize, inodebuf))
165 return (0);
166 inode = (struct ufs1_dinode *)inodebuf;
167 inode += ino_to_fsbo(fs, ino);
168 if (params->fstype->needswap)
169 ffs_dinode1_swap(inode, inode);
170
171 /* Get the block count and initialize for our block walk. */
172 nblk = howmany(inode->di_size, fs->fs_bsize);
173 lblk = 0;
174 level_i = 0;
175 level[0].blknums = &inode->di_db[0];
176 level[0].blkcount = NDADDR;
177 level[1].blknums = &inode->di_ib[0];
178 level[1].blkcount = 1;
179 level[2].blknums = &inode->di_ib[1];
180 level[2].blkcount = 1;
181 level[3].blknums = &inode->di_ib[2];
182 level[3].blkcount = 1;
183
184 /* Walk the data blocks. */
185 while (nblk > 0) {
186
187 /*
188 * If there are no more blocks at this indirection
189 * level, move up one indirection level and loop.
190 */
191 if (level[level_i].blkcount == 0) {
192 if (++level_i == LEVELS)
193 break;
194 continue;
195 }
196
197 /* Get the next block at this level. */
198 blk = *(level[level_i].blknums++);
199 level[level_i].blkcount--;
200 if (params->fstype->needswap)
201 blk = bswap32(blk);
202
203 #if 0
204 fprintf(stderr, "ino %lu blk %lu level %d\n", ino, blk,
205 level_i);
206 #endif
207
208 /*
209 * If we're not at the direct level, descend one
210 * level, read in that level's new block list,
211 * and loop.
212 */
213 if (level_i > 0) {
214 level_i--;
215 if (blk == 0)
216 memset(level[level_i].diskbuf, 0, MAXBSIZE);
217 else if (! ffs_read_disk_block(params,
218 fsbtodb(fs, blk),
219 fs->fs_bsize, level[level_i].diskbuf))
220 return (0);
221 /* XXX ondisk32 */
222 level[level_i].blknums =
223 (int32_t *)level[level_i].diskbuf;
224 level[level_i].blkcount = NINDIR(fs);
225 continue;
226 }
227
228 /* blk is the next direct level block. */
229 #if 0
230 fprintf(stderr, "ino %lu db %lu blksize %lu\n", ino,
231 fsbtodb(fs, blk), sblksize(fs, inode->di_size, lblk));
232 #endif
233 rv = (*callback)(params, state,
234 fsbtodb(fs, blk), 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 %llu in `%s' ran out of blocks?",
243 (unsigned long long)ino, 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 %llu in `%s' ran out of blocks?",
373 (unsigned long long)ino, 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_fileno;
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 char sbbuf[SBLOCKSIZE];
465 struct fs *fs;
466 int i;
467 off_t loc;
468
469 assert(params != NULL);
470 assert(params->fstype != NULL);
471
472 fs = (struct fs *)sbbuf;
473 for (i = 0; sblock_try[i] != -1; i++) {
474 loc = sblock_try[i] / DEV_BSIZE;
475 if (!ffs_read_disk_block(params, loc, SBLOCKSIZE, sbbuf))
476 continue;
477 switch (fs->fs_magic) {
478 case FS_UFS2_MAGIC:
479 is_ufs2 = 1;
480 /* FALLTHROUGH */
481 case FS_UFS1_MAGIC:
482 params->fstype->needswap = 0;
483 params->fstype->blocksize = fs->fs_bsize;
484 params->fstype->sblockloc = loc;
485 break;
486 #ifndef FFS_NO_SWAP
487 case FS_UFS2_MAGIC_SWAPPED:
488 is_ufs2 = 1;
489 /* FALLTHROUGH */
490 case FS_UFS1_MAGIC_SWAPPED:
491 params->fstype->needswap = 1;
492 params->fstype->blocksize = bswap32(fs->fs_bsize);
493 params->fstype->sblockloc = loc;
494 break;
495 #endif
496 default:
497 continue;
498 }
499 if (!is_ufs2 && sblock_try[i] == SBLOCK_UFS2)
500 continue;
501 return 1;
502 }
503
504 return (0);
505 }
506
507 int
508 ffs_findstage2(ib_params *params, uint32_t *maxblk, ib_block *blocks)
509 {
510 int rv;
511 uint32_t ino;
512 struct findblks_state state;
513
514 assert(params != NULL);
515 assert(params->stage2 != NULL);
516 assert(maxblk != NULL);
517 assert(blocks != NULL);
518
519 if (params->flags & IB_STAGE2START)
520 return (hardcode_stage2(params, maxblk, blocks));
521
522 /* The secondary bootstrap must be clearly in /. */
523 if (params->stage2[0] == '/')
524 params->stage2++;
525 if (strchr(params->stage2, '/') != NULL) {
526 warnx("The secondary bootstrap `%s' must be in /",
527 params->stage2);
528 return (0);
529 }
530
531 /* Get the inode number of the secondary bootstrap. */
532 if (is_ufs2)
533 rv = ffs_find_disk_blocks_ufs2(params, ROOTINO,
534 ffs_findstage2_ino, &ino);
535 else
536 rv = ffs_find_disk_blocks_ufs1(params, ROOTINO,
537 ffs_findstage2_ino, &ino);
538 if (rv != 2) {
539 warnx("Could not find secondary bootstrap `%s' in `%s'",
540 params->stage2, params->filesystem);
541 return (0);
542 }
543
544 /* Record the disk blocks of the secondary bootstrap. */
545 state.maxblk = *maxblk;
546 state.nblk = 0;
547 state.blocks = blocks;
548 if (is_ufs2)
549 rv = ffs_find_disk_blocks_ufs2(params, ino,
550 ffs_findstage2_blocks, &state);
551 else
552 rv = ffs_find_disk_blocks_ufs1(params, ino,
553 ffs_findstage2_blocks, &state);
554 if (! rv) {
555 return (0);
556 }
557
558 *maxblk = state.nblk;
559 return (1);
560 }
561