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