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