1/* $NetBSD: ffs.c,v 1.33 2022/11/17 06:40:40 chs 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 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32#if HAVE_NBTOOL_CONFIG_H 33#include "nbtool_config.h" 34#endif 35 36#include <sys/cdefs.h> 37#if !defined(__lint) 38__RCSID("$NetBSD: ffs.c,v 1.33 2022/11/17 06:40:40 chs Exp $"); 39#endif /* !__lint */ 40 41#include <sys/param.h> 42 43#if !HAVE_NBTOOL_CONFIG_H 44#include <sys/mount.h> 45#endif 46 47#include <assert.h> 48#include <err.h> 49#include <errno.h> 50#include <fcntl.h> 51#include <stdarg.h> 52#include <stdio.h> 53#include <stdlib.h> 54#include <string.h> 55#include <unistd.h> 56 57#include "installboot.h" 58 59/* From <dev/raidframe/raidframevar.h> */ 60#define RF_PROTECTED_SECTORS 64L 61 62#undef DIRBLKSIZ 63 64#include <ufs/ufs/dinode.h> 65#include <ufs/ufs/dir.h> 66#include <ufs/ffs/fs.h> 67#include <ufs/ffs/ffs_extern.h> 68#ifndef NO_FFS_SWAP 69#include <ufs/ufs/ufs_bswap.h> 70#else 71#define ffs_sb_swap(fs_a, fs_b) 72#define ffs_dinode1_swap(inode_a, inode_b) 73#define ffs_dinode2_swap(inode_a, inode_b) 74#endif 75 76static int ffs_match_common(ib_params *, off_t); 77static int ffs_read_disk_block(ib_params *, uint64_t, int, char []); 78static int ffs_find_disk_blocks_ufs1(ib_params *, ino_t, 79 int (*)(ib_params *, void *, uint64_t, uint32_t), void *); 80static int ffs_find_disk_blocks_ufs2(ib_params *, ino_t, 81 int (*)(ib_params *, void *, uint64_t, uint32_t), void *); 82static int ffs_findstage2_ino(ib_params *, void *, uint64_t, uint32_t); 83static int ffs_findstage2_blocks(ib_params *, void *, uint64_t, uint32_t); 84 85static int is_ufs2; 86 87 88/* This reads a disk block from the filesystem. */ 89static int 90ffs_read_disk_block(ib_params *params, uint64_t blkno, int size, char blk[]) 91{ 92 int rv; 93 94 assert(params != NULL); 95 assert(params->filesystem != NULL); 96 assert(params->fsfd != -1); 97 assert(size > 0); 98 assert(blk != NULL); 99 100 rv = pread(params->fsfd, blk, size, blkno * params->sectorsize); 101 if (rv == -1) { 102 warn("Reading block %llu in `%s'", 103 (unsigned long long)blkno, params->filesystem); 104 return (0); 105 } else if (rv != size) { 106 warnx("Reading block %llu in `%s': short read", 107 (unsigned long long)blkno, params->filesystem); 108 return (0); 109 } 110 111 return (1); 112} 113 114/* 115 * This iterates over the data blocks belonging to an inode, 116 * making a callback each iteration with the disk block number 117 * and the size. 118 */ 119static int 120ffs_find_disk_blocks_ufs1(ib_params *params, ino_t ino, 121 int (*callback)(ib_params *, void *, uint64_t, uint32_t), 122 void *state) 123{ 124 char sbbuf[SBLOCKSIZE]; 125 struct fs *fs; 126 char inodebuf[MAXBSIZE]; 127 struct ufs1_dinode *inode; 128 int level_i; 129 int32_t blk, lblk, nblk; 130 int rv; 131#define LEVELS 4 132 struct { 133 int32_t *blknums; 134 unsigned long blkcount; 135 char diskbuf[MAXBSIZE]; 136 } level[LEVELS]; 137 138 assert(params != NULL); 139 assert(params->fstype != NULL); 140 assert(callback != NULL); 141 assert(state != NULL); 142 143 /* Read the superblock. */ 144 if (!ffs_read_disk_block(params, params->fstype->sblockloc, SBLOCKSIZE, 145 sbbuf)) 146 return (0); 147 fs = (struct fs *)sbbuf; 148#ifndef NO_FFS_SWAP 149 if (params->fstype->needswap) 150 ffs_sb_swap(fs, fs); 151#endif 152 153 if (fs->fs_inopb <= 0) { 154 warnx("Bad inopb %d in superblock in `%s'", 155 fs->fs_inopb, params->filesystem); 156 return (0); 157 } 158 159 /* Read the inode. */ 160 if (! ffs_read_disk_block(params, 161 FFS_FSBTODB(fs, ino_to_fsba(fs, ino)) + params->fstype->offset, 162 fs->fs_bsize, inodebuf)) 163 return (0); 164 inode = (struct ufs1_dinode *)inodebuf; 165 inode += ino_to_fsbo(fs, ino); 166#ifndef NO_FFS_SWAP 167 if (params->fstype->needswap) 168 ffs_dinode1_swap(inode, inode); 169#endif 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 = UFS_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 FFS_FSBTODB(fs, blk) + params->fstype->offset, 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 = FFS_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 FFS_FSBTODB(fs, blk), ffs_sblksize(fs, inode->di_size, lblk)); 232#endif 233 rv = (*callback)(params, state, 234 FFS_FSBTODB(fs, blk) + params->fstype->offset, 235 ffs_sblksize(fs, (int64_t)inode->di_size, lblk)); 236 lblk++; 237 nblk--; 238 if (rv != 1) 239 return (rv); 240 } 241 242 if (nblk != 0) { 243 warnx("Inode %llu in `%s' ran out of blocks?", 244 (unsigned long long)ino, params->filesystem); 245 return (0); 246 } 247 248 return (1); 249} 250 251/* 252 * This iterates over the data blocks belonging to an inode, 253 * making a callback each iteration with the disk block number 254 * and the size. 255 */ 256static int 257ffs_find_disk_blocks_ufs2(ib_params *params, ino_t ino, 258 int (*callback)(ib_params *, void *, uint64_t, uint32_t), 259 void *state) 260{ 261 char sbbuf[SBLOCKSIZE]; 262 struct fs *fs; 263 char inodebuf[MAXBSIZE]; 264 struct ufs2_dinode *inode; 265 int level_i; 266 int64_t blk, lblk, nblk; 267 int rv; 268#define LEVELS 4 269 struct { 270 int64_t *blknums; 271 unsigned long blkcount; 272 char diskbuf[MAXBSIZE]; 273 } level[LEVELS]; 274 275 assert(params != NULL); 276 assert(params->fstype != NULL); 277 assert(callback != NULL); 278 assert(state != NULL); 279 280 /* Read the superblock. */ 281 if (!ffs_read_disk_block(params, params->fstype->sblockloc, SBLOCKSIZE, 282 sbbuf)) 283 return (0); 284 fs = (struct fs *)sbbuf; 285#ifndef NO_FFS_SWAP 286 if (params->fstype->needswap) 287 ffs_sb_swap(fs, fs); 288#endif 289 290 if (fs->fs_inopb <= 0) { 291 warnx("Bad inopb %d in superblock in `%s'", 292 fs->fs_inopb, params->filesystem); 293 return (0); 294 } 295 296 /* Read the inode. */ 297 if (! ffs_read_disk_block(params, 298 FFS_FSBTODB(fs, ino_to_fsba(fs, ino)) + params->fstype->offset, 299 fs->fs_bsize, inodebuf)) 300 return (0); 301 inode = (struct ufs2_dinode *)inodebuf; 302 inode += ino_to_fsbo(fs, ino); 303#ifndef NO_FFS_SWAP 304 if (params->fstype->needswap) 305 ffs_dinode2_swap(inode, inode); 306#endif 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 = UFS_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 FFS_FSBTODB(fs, blk) + params->fstype->offset, 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 = FFS_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 FFS_FSBTODB(fs, blk), ffs_sblksize(fs, inode->di_size, lblk)); 368#endif 369 rv = (*callback)(params, state, 370 FFS_FSBTODB(fs, blk) + params->fstype->offset, 371 ffs_sblksize(fs, (int64_t)inode->di_size, lblk)); 372 lblk++; 373 nblk--; 374 if (rv != 1) 375 return (rv); 376 } 377 378 if (nblk != 0) { 379 warnx("Inode %llu in `%s' ran out of blocks?", 380 (unsigned long long)ino, params->filesystem); 381 return (0); 382 } 383 384 return (1); 385} 386 387/* 388 * This callback reads a block of the root directory, 389 * searches for an entry for the secondary bootstrap, 390 * and saves the inode number if one is found. 391 */ 392static int 393ffs_findstage2_ino(ib_params *params, void *_ino, 394 uint64_t blk, uint32_t blksize) 395{ 396 char dirbuf[MAXBSIZE]; 397 struct direct *de, *ede; 398 uint32_t ino; 399 400 assert(params != NULL); 401 assert(params->fstype != NULL); 402 assert(params->stage2 != NULL); 403 assert(_ino != NULL); 404 405 /* Skip directory holes. */ 406 if (blk == 0) 407 return (1); 408 409 /* Read the directory block. */ 410 if (! ffs_read_disk_block(params, blk, blksize, dirbuf)) 411 return (0); 412 413 /* Loop over the directory entries. */ 414 de = (struct direct *)&dirbuf[0]; 415 ede = (struct direct *)&dirbuf[blksize]; 416 while (de < ede) { 417 ino = de->d_fileno; 418 if (params->fstype->needswap) { 419 ino = bswap32(ino); 420 de->d_reclen = bswap16(de->d_reclen); 421 } 422 if (ino != 0 && strcmp(de->d_name, params->stage2) == 0) { 423 *((uint32_t *)_ino) = ino; 424 return (2); 425 } 426 if (de->d_reclen == 0) 427 break; 428 de = (struct direct *)((char *)de + de->d_reclen); 429 } 430 431 return (1); 432} 433 434struct findblks_state { 435 uint32_t maxblk; 436 uint32_t nblk; 437 ib_block *blocks; 438}; 439 440/* This callback records the blocks of the secondary bootstrap. */ 441static int 442ffs_findstage2_blocks(ib_params *params, void *_state, 443 uint64_t blk, uint32_t blksize) 444{ 445 struct findblks_state *state = _state; 446 447 assert(params != NULL); 448 assert(params->stage2 != NULL); 449 assert(_state != NULL); 450 451 if (state->nblk == state->maxblk) { 452 warnx("Secondary bootstrap `%s' has too many blocks (max %d)", 453 params->stage2, state->maxblk); 454 return (0); 455 } 456 state->blocks[state->nblk].block = blk; 457 state->blocks[state->nblk].blocksize = blksize; 458 state->nblk++; 459 return (1); 460} 461 462/* 463 * publicly visible functions 464 */ 465 466static off_t sblock_try[] = SBLOCKSEARCH; 467 468int 469ffs_match(ib_params *params) 470{ 471 return ffs_match_common(params, (off_t) 0); 472} 473 474int 475raid_match(ib_params *params) 476{ 477 /* XXX Assumes 512 bytes / sector */ 478 if (params->sectorsize != 512) { 479 warnx("Media is %d bytes/sector." 480 " RAID is only supported on 512 bytes/sector media.", 481 params->sectorsize); 482 return 0; 483 } 484 return ffs_match_common(params, (off_t) RF_PROTECTED_SECTORS); 485} 486 487int 488ffs_match_common(ib_params *params, off_t offset) 489{ 490 char sbbuf[SBLOCKSIZE]; 491 struct fs *fs; 492 int i; 493 off_t loc; 494 495 assert(params != NULL); 496 assert(params->fstype != NULL); 497 498 fs = (struct fs *)sbbuf; 499 for (i = 0; sblock_try[i] != -1; i++) { 500 loc = sblock_try[i] / params->sectorsize + offset; 501 if (!ffs_read_disk_block(params, loc, SBLOCKSIZE, sbbuf)) 502 continue; 503 switch (fs->fs_magic) { 504 case FS_UFS2_MAGIC: 505 case FS_UFS2EA_MAGIC: 506 is_ufs2 = 1; 507 /* FALLTHROUGH */ 508 case FS_UFS1_MAGIC: 509 params->fstype->needswap = 0; 510 params->fstype->blocksize = fs->fs_bsize; 511 params->fstype->sblockloc = loc; 512 params->fstype->offset = offset; 513 break; 514#ifndef FFS_NO_SWAP 515 case FS_UFS2_MAGIC_SWAPPED: 516 case FS_UFS2EA_MAGIC_SWAPPED: 517 is_ufs2 = 1; 518 /* FALLTHROUGH */ 519 case FS_UFS1_MAGIC_SWAPPED: 520 params->fstype->needswap = 1; 521 params->fstype->blocksize = bswap32(fs->fs_bsize); 522 params->fstype->sblockloc = loc; 523 params->fstype->offset = offset; 524 break; 525#endif 526 default: 527 continue; 528 } 529 if (!is_ufs2 && sblock_try[i] == SBLOCK_UFS2) 530 continue; 531 return 1; 532 } 533 534 return (0); 535} 536 537int 538ffs_findstage2(ib_params *params, uint32_t *maxblk, ib_block *blocks) 539{ 540 int rv; 541 uint32_t ino; 542 struct findblks_state state; 543 544 assert(params != NULL); 545 assert(params->stage2 != NULL); 546 assert(maxblk != NULL); 547 assert(blocks != NULL); 548 549 if (params->flags & IB_STAGE2START) 550 return (hardcode_stage2(params, maxblk, blocks)); 551 552 /* The secondary bootstrap must be clearly in /. */ 553 if (params->stage2[0] == '/') 554 params->stage2++; 555 if (strchr(params->stage2, '/') != NULL) { 556 warnx("The secondary bootstrap `%s' must be in /", 557 params->stage2); 558 warnx("(Path must be relative to the file system in `%s')", 559 params->filesystem); 560 return (0); 561 } 562 563 /* Get the inode number of the secondary bootstrap. */ 564 if (is_ufs2) 565 rv = ffs_find_disk_blocks_ufs2(params, UFS_ROOTINO, 566 ffs_findstage2_ino, &ino); 567 else 568 rv = ffs_find_disk_blocks_ufs1(params, UFS_ROOTINO, 569 ffs_findstage2_ino, &ino); 570 if (rv != 2) { 571 warnx("Could not find secondary bootstrap `%s' in `%s'", 572 params->stage2, params->filesystem); 573 warnx("(Path must be relative to the file system in `%s')", 574 params->filesystem); 575 return (0); 576 } 577 578 /* Record the disk blocks of the secondary bootstrap. */ 579 state.maxblk = *maxblk; 580 state.nblk = 0; 581 state.blocks = blocks; 582 if (is_ufs2) 583 rv = ffs_find_disk_blocks_ufs2(params, ino, 584 ffs_findstage2_blocks, &state); 585 else 586 rv = ffs_find_disk_blocks_ufs1(params, ino, 587 ffs_findstage2_blocks, &state); 588 if (! rv) { 589 return (0); 590 } 591 592 *maxblk = state.nblk; 593 return (1); 594} 595