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