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