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