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