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