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