1/* $NetBSD: ext2fs.c,v 1.8 2013/06/19 17:51:26 dholland Exp $ */ 2 3/* 4 * Copyright (c) 1997 Manuel Bouyer. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27/*- 28 * Copyright (c) 2002 The NetBSD Foundation, Inc. 29 * All rights reserved. 30 * 31 * This code is derived from software contributed to The NetBSD Foundation 32 * by Matt Fredette. 33 * 34 * Redistribution and use in source and binary forms, with or without 35 * modification, are permitted provided that the following conditions 36 * are met: 37 * 1. Redistributions of source code must retain the above copyright 38 * notice, this list of conditions and the following disclaimer. 39 * 2. Redistributions in binary form must reproduce the above copyright 40 * notice, this list of conditions and the following disclaimer in the 41 * documentation and/or other materials provided with the distribution. 42 * 43 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 44 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 45 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 46 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 47 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 48 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 49 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 50 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 51 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 52 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 53 * POSSIBILITY OF SUCH DAMAGE. 54 */ 55 56#if HAVE_NBTOOL_CONFIG_H 57#include "nbtool_config.h" 58#endif 59 60#include <sys/cdefs.h> 61#if !defined(__lint) 62__RCSID("$NetBSD: ext2fs.c,v 1.8 2013/06/19 17:51:26 dholland Exp $"); 63#endif /* !__lint */ 64 65#include <sys/param.h> 66 67#if !HAVE_NBTOOL_CONFIG_H 68#include <sys/mount.h> 69#endif 70 71#include <assert.h> 72#include <err.h> 73#include <errno.h> 74#include <fcntl.h> 75#include <stdarg.h> 76#include <stdio.h> 77#include <stdlib.h> 78#include <string.h> 79#include <unistd.h> 80 81#include "installboot.h" 82 83#include <ufs/ext2fs/ext2fs_dinode.h> 84#include <ufs/ext2fs/ext2fs_dir.h> 85#include <ufs/ext2fs/ext2fs.h> 86 87static int ext2fs_read_disk_block(ib_params *, uint64_t, int, uint8_t []); 88static int ext2fs_read_sblock(ib_params *, struct m_ext2fs *fs); 89static int ext2fs_read_gdblock(ib_params *, struct m_ext2fs *fs); 90static int ext2fs_find_disk_blocks(ib_params *, ino_t, 91 int (*)(ib_params *, void *, uint64_t, uint32_t), void *); 92static int ext2fs_findstage2_ino(ib_params *, void *, uint64_t, uint32_t); 93static int ext2fs_findstage2_blocks(ib_params *, void *, uint64_t, 94 uint32_t); 95 96 97/* This reads a disk block from the file system. */ 98/* XXX: should be shared with ffs.c? */ 99static int 100ext2fs_read_disk_block(ib_params *params, uint64_t blkno, int size, 101 uint8_t blk[]) 102{ 103 int rv; 104 105 assert(params != NULL); 106 assert(params->filesystem != NULL); 107 assert(params->fsfd != -1); 108 assert(size > 0); 109 assert(blk != NULL); 110 111 rv = pread(params->fsfd, blk, size, blkno * params->sectorsize); 112 if (rv == -1) { 113 warn("Reading block %llu in `%s'", 114 (unsigned long long)blkno, params->filesystem); 115 return 0; 116 } else if (rv != size) { 117 warnx("Reading block %llu in `%s': short read", 118 (unsigned long long)blkno, params->filesystem); 119 return 0; 120 } 121 122 return 1; 123} 124 125static int 126ext2fs_read_sblock(ib_params *params, struct m_ext2fs *fs) 127{ 128 uint8_t sbbuf[SBSIZE]; 129 130 if (ext2fs_read_disk_block(params, SBOFF / params->sectorsize, SBSIZE, 131 sbbuf) == 0) 132 133 e2fs_sbload((void *)sbbuf, &fs->e2fs); 134 135 if (fs->e2fs.e2fs_magic != E2FS_MAGIC) 136 return 0; 137 138 if (fs->e2fs.e2fs_rev > E2FS_REV1 || 139 (fs->e2fs.e2fs_rev == E2FS_REV1 && 140 (fs->e2fs.e2fs_first_ino != EXT2_FIRSTINO || 141 fs->e2fs.e2fs_inode_size != EXT2_DINODE_SIZE || 142 (fs->e2fs.e2fs_features_incompat & ~EXT2F_INCOMPAT_SUPP) != 0))) 143 return 0; 144 145 fs->e2fs_ncg = 146 howmany(fs->e2fs.e2fs_bcount - fs->e2fs.e2fs_first_dblock, 147 fs->e2fs.e2fs_bpg); 148 /* XXX assume hw bsize = 512 */ 149 fs->e2fs_fsbtodb = fs->e2fs.e2fs_log_bsize + 1; 150 fs->e2fs_bsize = MINBSIZE << fs->e2fs.e2fs_log_bsize; 151 fs->e2fs_bshift = LOG_MINBSIZE + fs->e2fs.e2fs_log_bsize; 152 fs->e2fs_qbmask = fs->e2fs_bsize - 1; 153 fs->e2fs_bmask = ~fs->e2fs_qbmask; 154 fs->e2fs_ngdb = 155 howmany(fs->e2fs_ncg, fs->e2fs_bsize / sizeof(struct ext2_gd)); 156 fs->e2fs_ipb = fs->e2fs_bsize / EXT2_DINODE_SIZE; 157 fs->e2fs_itpg = fs->e2fs.e2fs_ipg / fs->e2fs_ipb; 158 159 return 1; 160} 161 162static int 163ext2fs_read_gdblock(ib_params *params, struct m_ext2fs *fs) 164{ 165 uint8_t gdbuf[MAXBSIZE]; 166 uint32_t gdpb; 167 int i; 168 169 gdpb = fs->e2fs_bsize / sizeof(struct ext2_gd); 170 171 for (i = 0; i < fs->e2fs_ngdb; i++) { 172 if (ext2fs_read_disk_block(params, EXT2_FSBTODB(fs, 173 fs->e2fs.e2fs_first_dblock + 1 /* superblock */ + i), 174 SBSIZE, gdbuf) == 0) 175 return 0; 176 177 e2fs_cgload((struct ext2_gd *)gdbuf, &fs->e2fs_gd[gdpb * i], 178 (i == (fs->e2fs_ngdb - 1)) ? 179 (fs->e2fs_ncg - gdpb * i) * sizeof(struct ext2_gd): 180 fs->e2fs_bsize); 181 } 182 183 return 1; 184} 185 186/* 187 * This iterates over the data blocks belonging to an inode, 188 * making a callback each iteration with the disk block number 189 * and the size. 190 */ 191static int 192ext2fs_find_disk_blocks(ib_params *params, ino_t ino, 193 int (*callback)(ib_params *, void *, uint64_t, uint32_t), 194 void *state) 195{ 196 uint8_t sbbuf[sizeof(struct m_ext2fs)]; 197 struct m_ext2fs *fs; 198 uint8_t inodebuf[MAXBSIZE]; 199 struct ext2fs_dinode inode_store, *inode; 200 int level_i; 201 int32_t blk, lblk, nblk; 202 int rv; 203#define LEVELS 4 204 struct { 205 uint32_t *blknums; 206 unsigned long blkcount; 207 uint8_t diskbuf[MAXBSIZE]; 208 } level[LEVELS]; 209 210 assert(params != NULL); 211 assert(params->fstype != NULL); 212 assert(callback != NULL); 213 assert(state != NULL); 214 215 /* Read the superblock. */ 216 fs = (void *)sbbuf; 217 if (ext2fs_read_sblock(params, fs) == 0) 218 return 0; 219 220 fs->e2fs_gd = malloc(sizeof(struct ext2_gd) * fs->e2fs_ncg); 221 if (fs->e2fs_gd == NULL) { 222 warnx("Can't allocate memofy for group descriptors"); 223 return 0; 224 } 225 226 if (ext2fs_read_gdblock(params, fs) == 0) { 227 warnx("Can't read group descriptors"); 228 return 0; 229 } 230 231 if (fs->e2fs_ipb <= 0) { 232 warnx("Bad ipb %d in superblock in `%s'", 233 fs->e2fs_ipb, params->filesystem); 234 return 0; 235 } 236 237 /* Read the inode. */ 238 if (ext2fs_read_disk_block(params, 239 EXT2_FSBTODB(fs, ino_to_fsba(fs, ino)) + params->fstype->offset, 240 fs->e2fs_bsize, inodebuf)) 241 return 0; 242 inode = (void *)inodebuf; 243 e2fs_iload(&inode[ino_to_fsbo(fs, ino)], &inode_store); 244 inode = &inode_store; 245 246 /* Get the block count and initialize for our block walk. */ 247 nblk = howmany(inode->e2di_size, fs->e2fs_bsize); 248 lblk = 0; 249 level_i = 0; 250 level[0].blknums = &inode->e2di_blocks[0]; 251 level[0].blkcount = UFS_NDADDR; 252 level[1].blknums = &inode->e2di_blocks[UFS_NDADDR + 0]; 253 level[1].blkcount = 1; 254 level[2].blknums = &inode->e2di_blocks[UFS_NDADDR + 1]; 255 level[2].blkcount = 1; 256 level[3].blknums = &inode->e2di_blocks[UFS_NDADDR + 2]; 257 level[3].blkcount = 1; 258 259 /* Walk the data blocks. */ 260 while (nblk > 0) { 261 262 /* 263 * If there are no more blocks at this indirection 264 * level, move up one indirection level and loop. 265 */ 266 if (level[level_i].blkcount == 0) { 267 if (++level_i == LEVELS) 268 break; 269 continue; 270 } 271 272 /* Get the next block at this level. */ 273 blk = fs2h32(*(level[level_i].blknums++)); 274 level[level_i].blkcount--; 275 276#if 0 277 fprintf(stderr, "ino %lu blk %lu level %d\n", ino, blk, 278 level_i); 279#endif 280 281 /* 282 * If we're not at the direct level, descend one 283 * level, read in that level's new block list, 284 * and loop. 285 */ 286 if (level_i > 0) { 287 level_i--; 288 if (blk == 0) 289 memset(level[level_i].diskbuf, 0, MAXBSIZE); 290 else if (ext2fs_read_disk_block(params, 291 EXT2_FSBTODB(fs, blk) + params->fstype->offset, 292 fs->e2fs_bsize, level[level_i].diskbuf) == 0) 293 return 0; 294 /* XXX ondisk32 */ 295 level[level_i].blknums = 296 (uint32_t *)level[level_i].diskbuf; 297 level[level_i].blkcount = EXT2_NINDIR(fs); 298 continue; 299 } 300 301 /* blk is the next direct level block. */ 302#if 0 303 fprintf(stderr, "ino %lu db %lu blksize %lu\n", ino, 304 EXT2_FSBTODB(fs, blk), ext2_sblksize(fs, inode->di_size, lblk)); 305#endif 306 rv = (*callback)(params, state, 307 EXT2_FSBTODB(fs, blk) + params->fstype->offset, fs->e2fs_bsize); 308 lblk++; 309 nblk--; 310 if (rv != 1) 311 return rv; 312 } 313 314 if (nblk != 0) { 315 warnx("Inode %llu in `%s' ran out of blocks?", 316 (unsigned long long)ino, params->filesystem); 317 return 0; 318 } 319 320 return 1; 321} 322 323/* 324 * This callback reads a block of the root directory, 325 * searches for an entry for the secondary bootstrap, 326 * and saves the inode number if one is found. 327 */ 328static int 329ext2fs_findstage2_ino(ib_params *params, void *_ino, 330 uint64_t blk, uint32_t blksize) 331{ 332 uint8_t dirbuf[MAXBSIZE]; 333 struct ext2fs_direct *de, *ede; 334 uint32_t ino; 335 336 assert(params != NULL); 337 assert(params->fstype != NULL); 338 assert(params->stage2 != NULL); 339 assert(_ino != NULL); 340 341 /* Skip directory holes. */ 342 if (blk == 0) 343 return 1; 344 345 /* Read the directory block. */ 346 if (ext2fs_read_disk_block(params, blk, blksize, dirbuf) == 0) 347 return 0; 348 349 /* Loop over the directory entries. */ 350 de = (struct ext2fs_direct *)&dirbuf[0]; 351 ede = (struct ext2fs_direct *)&dirbuf[blksize]; 352 while (de < ede) { 353 ino = fs2h32(de->e2d_ino); 354 if (ino != 0 && strcmp(de->e2d_name, params->stage2) == 0) { 355 *((uint32_t *)_ino) = ino; 356 return (2); 357 } 358 if (fs2h16(de->e2d_reclen) == 0) 359 break; 360 de = (struct ext2fs_direct *)((char *)de + 361 fs2h16(de->e2d_reclen)); 362 } 363 364 return 1; 365} 366 367struct findblks_state { 368 uint32_t maxblk; 369 uint32_t nblk; 370 ib_block *blocks; 371}; 372 373/* This callback records the blocks of the secondary bootstrap. */ 374static int 375ext2fs_findstage2_blocks(ib_params *params, void *_state, 376 uint64_t blk, uint32_t blksize) 377{ 378 struct findblks_state *state = _state; 379 380 assert(params != NULL); 381 assert(params->stage2 != NULL); 382 assert(_state != NULL); 383 384 if (state->nblk == state->maxblk) { 385 warnx("Secondary bootstrap `%s' has too many blocks (max %d)", 386 params->stage2, state->maxblk); 387 return (0); 388 } 389 state->blocks[state->nblk].block = blk; 390 state->blocks[state->nblk].blocksize = blksize; 391 state->nblk++; 392 return 1; 393} 394 395/* 396 * publicly visible functions 397 */ 398 399int 400ext2fs_match(ib_params *params) 401{ 402 uint8_t sbbuf[sizeof(struct m_ext2fs)]; 403 struct m_ext2fs *fs; 404 405 assert(params != NULL); 406 assert(params->fstype != NULL); 407 408 /* Read the superblock. */ 409 fs = (void *)sbbuf; 410 if (ext2fs_read_sblock(params, fs) == 0) 411 return 0; 412 413 params->fstype->needswap = 0; 414 params->fstype->blocksize = fs->e2fs_bsize; 415 params->fstype->offset = 0; 416 417 return 1; 418} 419 420int 421ext2fs_findstage2(ib_params *params, uint32_t *maxblk, ib_block *blocks) 422{ 423 int rv; 424 uint32_t ino; 425 struct findblks_state state; 426 427 assert(params != NULL); 428 assert(params->stage2 != NULL); 429 assert(maxblk != NULL); 430 assert(blocks != NULL); 431 432 if (params->flags & IB_STAGE2START) 433 return hardcode_stage2(params, maxblk, blocks); 434 435 /* The secondary bootstrap must be clearly in /. */ 436 if (params->stage2[0] == '/') 437 params->stage2++; 438 if (strchr(params->stage2, '/') != NULL) { 439 warnx("The secondary bootstrap `%s' must be in /", 440 params->stage2); 441 warnx("(Path must be relative to the file system in `%s')", 442 params->filesystem); 443 return 0; 444 } 445 446 /* Get the inode number of the secondary bootstrap. */ 447 rv = ext2fs_find_disk_blocks(params, EXT2_ROOTINO, 448 ext2fs_findstage2_ino, &ino); 449 if (rv != 2) { 450 warnx("Could not find secondary bootstrap `%s' in `%s'", 451 params->stage2, params->filesystem); 452 warnx("(Path must be relative to the file system in `%s')", 453 params->filesystem); 454 return 0; 455 } 456 457 /* Record the disk blocks of the secondary bootstrap. */ 458 state.maxblk = *maxblk; 459 state.nblk = 0; 460 state.blocks = blocks; 461 rv = ext2fs_find_disk_blocks(params, ino, 462 ext2fs_findstage2_blocks, &state); 463 if (rv == 0) 464 return 0; 465 466 *maxblk = state.nblk; 467 return 1; 468} 469