1 /* $NetBSD: ext2fs.c,v 1.4 2009/10/19 18:41:17 bouyer Exp $ */
4 * Copyright (c) 1997 Manuel Bouyer.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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.
28 * Copyright (c) 2002 The NetBSD Foundation, Inc.
29 * All rights reserved.
31 * This code is derived from software contributed to The NetBSD Foundation
34 * Redistribution and use in source and binary forms, with or without
35 * modification, are permitted provided that the following conditions
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.
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.
56 #if HAVE_NBTOOL_CONFIG_H
57 #include "nbtool_config.h"
60 #include <sys/cdefs.h>
61 #if defined(__RCSID) && !defined(__lint)
62 __RCSID("$NetBSD: ext2fs.c,v 1.4 2009/10/19 18:41:17 bouyer Exp $");
65 #include <sys/param.h>
67 #if !HAVE_NBTOOL_CONFIG_H
68 #include <sys/mount.h>
81 #include "installboot.h"
83 #include <ufs/ext2fs/ext2fs_dinode.h>
84 #include <ufs/ext2fs/ext2fs_dir.h>
85 #include <ufs/ext2fs/ext2fs.h>
87 static int ext2fs_read_disk_block(ib_params
*, uint64_t, int, uint8_t []);
88 static int ext2fs_read_sblock(ib_params
*, struct m_ext2fs
*fs
);
89 static int ext2fs_read_gdblock(ib_params
*, struct m_ext2fs
*fs
);
90 static int ext2fs_find_disk_blocks(ib_params
*, ino_t
,
91 int (*)(ib_params
*, void *, uint64_t, uint32_t), void *);
92 static int ext2fs_findstage2_ino(ib_params
*, void *, uint64_t, uint32_t);
93 static int ext2fs_findstage2_blocks(ib_params
*, void *, uint64_t,
97 /* This reads a disk block from the file system. */
98 /* XXX: should be shared with ffs.c? */
100 ext2fs_read_disk_block(ib_params
*params
, uint64_t blkno
, int size
,
105 assert(params
!= NULL
);
106 assert(params
->filesystem
!= NULL
);
107 assert(params
->fsfd
!= -1);
111 rv
= pread(params
->fsfd
, blk
, size
, blkno
* params
->sectorsize
);
113 warn("Reading block %llu in `%s'",
114 (unsigned long long)blkno
, params
->filesystem
);
116 } else if (rv
!= size
) {
117 warnx("Reading block %llu in `%s': short read",
118 (unsigned long long)blkno
, params
->filesystem
);
126 ext2fs_read_sblock(ib_params
*params
, struct m_ext2fs
*fs
)
128 uint8_t sbbuf
[SBSIZE
];
130 if (ext2fs_read_disk_block(params
, SBOFF
/ params
->sectorsize
, SBSIZE
,
133 e2fs_sbload((void *)sbbuf
, &fs
->e2fs
);
135 if (fs
->e2fs
.e2fs_magic
!= E2FS_MAGIC
)
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)))
146 howmany(fs
->e2fs
.e2fs_bcount
- fs
->e2fs
.e2fs_first_dblock
,
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
;
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
;
163 ext2fs_read_gdblock(ib_params
*params
, struct m_ext2fs
*fs
)
165 uint8_t gdbuf
[MAXBSIZE
];
169 gdpb
= fs
->e2fs_bsize
/ sizeof(struct ext2_gd
);
171 for (i
= 0; i
< fs
->e2fs_ngdb
; i
++) {
172 if (ext2fs_read_disk_block(params
, fsbtodb(fs
,
173 fs
->e2fs
.e2fs_first_dblock
+ 1 /* superblock */ + i
),
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
):
187 * This iterates over the data blocks belonging to an inode,
188 * making a callback each iteration with the disk block number
192 ext2fs_find_disk_blocks(ib_params
*params
, ino_t ino
,
193 int (*callback
)(ib_params
*, void *, uint64_t, uint32_t),
196 uint8_t sbbuf
[sizeof(struct m_ext2fs
)];
198 uint8_t inodebuf
[MAXBSIZE
];
199 struct ext2fs_dinode inode_store
, *inode
;
201 int32_t blk
, lblk
, nblk
;
206 unsigned long blkcount
;
207 uint8_t diskbuf
[MAXBSIZE
];
210 assert(params
!= NULL
);
211 assert(params
->fstype
!= NULL
);
212 assert(callback
!= NULL
);
213 assert(state
!= NULL
);
215 /* Read the superblock. */
217 if (ext2fs_read_sblock(params
, fs
) == 0)
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");
226 if (ext2fs_read_gdblock(params
, fs
) == 0) {
227 warnx("Can't read group descriptors");
231 if (fs
->e2fs_ipb
<= 0) {
232 warnx("Bad ipb %d in superblock in `%s'",
233 fs
->e2fs_ipb
, params
->filesystem
);
237 /* Read the inode. */
238 if (ext2fs_read_disk_block(params
,
239 fsbtodb(fs
, ino_to_fsba(fs
, ino
)) + params
->fstype
->offset
,
240 fs
->e2fs_bsize
, inodebuf
))
242 inode
= (void *)inodebuf
;
243 e2fs_iload(&inode
[ino_to_fsbo(fs
, ino
)], &inode_store
);
244 inode
= &inode_store
;
246 /* Get the block count and initialize for our block walk. */
247 nblk
= howmany(inode
->e2di_size
, fs
->e2fs_bsize
);
250 level
[0].blknums
= &inode
->e2di_blocks
[0];
251 level
[0].blkcount
= NDADDR
;
252 level
[1].blknums
= &inode
->e2di_blocks
[NDADDR
+ 0];
253 level
[1].blkcount
= 1;
254 level
[2].blknums
= &inode
->e2di_blocks
[NDADDR
+ 1];
255 level
[2].blkcount
= 1;
256 level
[3].blknums
= &inode
->e2di_blocks
[NDADDR
+ 2];
257 level
[3].blkcount
= 1;
259 /* Walk the data blocks. */
263 * If there are no more blocks at this indirection
264 * level, move up one indirection level and loop.
266 if (level
[level_i
].blkcount
== 0) {
267 if (++level_i
== LEVELS
)
272 /* Get the next block at this level. */
273 blk
= fs2h32(*(level
[level_i
].blknums
++));
274 level
[level_i
].blkcount
--;
277 fprintf(stderr
, "ino %lu blk %lu level %d\n", ino
, blk
,
282 * If we're not at the direct level, descend one
283 * level, read in that level's new block list,
289 memset(level
[level_i
].diskbuf
, 0, MAXBSIZE
);
290 else if (ext2fs_read_disk_block(params
,
291 fsbtodb(fs
, blk
) + params
->fstype
->offset
,
292 fs
->e2fs_bsize
, level
[level_i
].diskbuf
) == 0)
295 level
[level_i
].blknums
=
296 (uint32_t *)level
[level_i
].diskbuf
;
297 level
[level_i
].blkcount
= NINDIR(fs
);
301 /* blk is the next direct level block. */
303 fprintf(stderr
, "ino %lu db %lu blksize %lu\n", ino
,
304 fsbtodb(fs
, blk
), sblksize(fs
, inode
->di_size
, lblk
));
306 rv
= (*callback
)(params
, state
,
307 fsbtodb(fs
, blk
) + params
->fstype
->offset
, fs
->e2fs_bsize
);
315 warnx("Inode %llu in `%s' ran out of blocks?",
316 (unsigned long long)ino
, params
->filesystem
);
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.
329 ext2fs_findstage2_ino(ib_params
*params
, void *_ino
,
330 uint64_t blk
, uint32_t blksize
)
332 uint8_t dirbuf
[MAXBSIZE
];
333 struct ext2fs_direct
*de
, *ede
;
336 assert(params
!= NULL
);
337 assert(params
->fstype
!= NULL
);
338 assert(params
->stage2
!= NULL
);
339 assert(_ino
!= NULL
);
341 /* Skip directory holes. */
345 /* Read the directory block. */
346 if (ext2fs_read_disk_block(params
, blk
, blksize
, dirbuf
) == 0)
349 /* Loop over the directory entries. */
350 de
= (struct ext2fs_direct
*)&dirbuf
[0];
351 ede
= (struct ext2fs_direct
*)&dirbuf
[blksize
];
353 ino
= fs2h32(de
->e2d_ino
);
354 if (ino
!= 0 && strcmp(de
->e2d_name
, params
->stage2
) == 0) {
355 *((uint32_t *)_ino
) = ino
;
358 if (fs2h16(de
->e2d_reclen
) == 0)
360 de
= (struct ext2fs_direct
*)((char *)de
+
361 fs2h16(de
->e2d_reclen
));
367 struct findblks_state
{
373 /* This callback records the blocks of the secondary bootstrap. */
375 ext2fs_findstage2_blocks(ib_params
*params
, void *_state
,
376 uint64_t blk
, uint32_t blksize
)
378 struct findblks_state
*state
= _state
;
380 assert(params
!= NULL
);
381 assert(params
->stage2
!= NULL
);
382 assert(_state
!= NULL
);
384 if (state
->nblk
== state
->maxblk
) {
385 warnx("Secondary bootstrap `%s' has too many blocks (max %d)",
386 params
->stage2
, state
->maxblk
);
389 state
->blocks
[state
->nblk
].block
= blk
;
390 state
->blocks
[state
->nblk
].blocksize
= blksize
;
396 * publicly visible functions
400 ext2fs_match(ib_params
*params
)
402 uint8_t sbbuf
[sizeof(struct m_ext2fs
)];
405 assert(params
!= NULL
);
406 assert(params
->fstype
!= NULL
);
408 /* Read the superblock. */
410 if (ext2fs_read_sblock(params
, fs
) == 0)
413 params
->fstype
->needswap
= 0;
414 params
->fstype
->blocksize
= fs
->e2fs_bsize
;
415 params
->fstype
->offset
= 0;
421 ext2fs_findstage2(ib_params
*params
, uint32_t *maxblk
, ib_block
*blocks
)
425 struct findblks_state state
;
427 assert(params
!= NULL
);
428 assert(params
->stage2
!= NULL
);
429 assert(maxblk
!= NULL
);
430 assert(blocks
!= NULL
);
432 if (params
->flags
& IB_STAGE2START
)
433 return hardcode_stage2(params
, maxblk
, blocks
);
435 /* The secondary bootstrap must be clearly in /. */
436 if (params
->stage2
[0] == '/')
438 if (strchr(params
->stage2
, '/') != NULL
) {
439 warnx("The secondary bootstrap `%s' must be in /",
441 warnx("(Path must be relative to the file system in `%s')",
446 /* Get the inode number of the secondary bootstrap. */
447 rv
= ext2fs_find_disk_blocks(params
, EXT2_ROOTINO
,
448 ext2fs_findstage2_ino
, &ino
);
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')",
457 /* Record the disk blocks of the secondary bootstrap. */
458 state
.maxblk
= *maxblk
;
460 state
.blocks
= blocks
;
461 rv
= ext2fs_find_disk_blocks(params
, ino
,
462 ext2fs_findstage2_blocks
, &state
);
466 *maxblk
= state
.nblk
;