Home | History | Annotate | Line # | Download | only in installboot
ext2fs.c revision 1.1
      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