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